PHP'de Rota Sistemi ile Basit ve Hızlı Uygulamalar

20 Ağustos 2018 Pazartesi günü PHP başlığında

PHP'de Rota Sistemi ile Basit ve Hızlı Uygulamalar

Bu yazıda yazacağımız kodlar, MVC'ye alternatif olacak ve nesne yönelimli olmayacak. Bunun yerine fonksiyonel programlama yöntemiyle oluşturacağız ve basit bir düzen oluşturacağız. Ama unutmayın, bu karışık olmayacağı anlamına gelmez!

Daha önceden PHP İle Basit Bir Router Nasıl Yapılır başlıklı bir yazı yazmıştım. Bu yazıda nesne yönelimli programlama ile çok basit bir rota sistemi yazmıştık. Şimdiyse bunun daha basit halini yazacağız.

Hemen bir app.php dosyası oluşturup kodlarımızı yazmaya başlayalım. Çok az kod yazacağız ama çok işleve sahip olacak. Kod satırlarında ne işe yaradıklarını, ne için yazdığımızı vs. anlattım. Es geçmemenizi öneririm.

<?php

// Oluşturduğumuz rotaları tutacak değişken
$routes = [];

/**
 * @param string $method Rotanın istek metodu (GET, POST gibi)
 * @param string $path URL yolu (/merhaba mesele)
 * @param callable $callback Anonim işlevimiz yani /merhaba sayfasında çalışacak fonksiyon
 * @return void herhangi bir şey döndürmeyecek
 */
function router_map(string $method, string $path, callable $callback)
{
  // Fonksiyonun dışındaki değişkene erişmek için global'i kullanıyoruz
  global $routes;

  // routes değişkenine yeni eleman ekliyoruz
  $routes[] = [$method, $path, $callback];
}

// Rota sistemini çalıştıracak olan fonksiyon
function router_run()
{
  global $routes;

  // oluşturduğumuz rotaları döngüye sokup tek tek işleyeceğiz
  foreach ($routes as $route) {
    /**
     * Her bir rotanın içindeki verileri list fonksiyonu ile
     * bir değişkene aktarıyoruz. Bu yaptığımız şununla denk:
     * $method = $route[0];
     * $path = $route[1];
     * $callback = $route[2];
     */
    list($method, $path, $callback) = $route;

    if (
      // Eğer tanımlanan method, kullanıcının methodu ile aynıysa
      // yani GET == GET ise sıkıntı yok, devam
      $method == $_SERVER['REQUEST_METHOD'] &&

      // Eğer tanımlanan yol, kullanıcının talep ettiği yol ile aynıysa
      preg_match("@^{$path}$@ixs", $_SERVER['REQUEST_URI'], $params)
    ) {
      // $params değişkenini preg_match fonksiyonu oluşturdu
      // Bu değişken içerisinde varsa parametreler tutulacak
      // yani örneğin: /haber/5 URL'sinde 5 parametre değeridir
      // array_shift fonksiyonu ise $params değişkenindeki ilk 
      // öğeyi kaldırır. Çünkü preg_match parametre verirken 1 tane fazla verir
      array_shift($params);

      // artık fonksiyonumuzu çağırabiliriz
      // aşağıdaki fonksiyon şuna denktir:
      // $callback(...$params);
      return call_user_func_array($callback, $params);
    }
  }

  // Eğer döngüde herhangi bir şey dönmezde
  // sayfa bulunamadı mesajı ver ve uygulamadan çık
    http_response_code(404);
  exit('404 Sayfa Bulunamadı');
}

/**
 * @param string $name View dosyası adı
 * @param array $data View dosyası için çıkarılacak değişkenler
 * @return string
 */
function view_render(string $name, array $data = [])
{
  // View dosyalarının bulunduğu dizin
  $path = __DIR__ . '/views';

  // Eğer dosya mevcutsa
  if (is_file($fullPath = "$path/$name.php")) {

    // Çıktı tamponlamayı başlatalım
    // ob_start() ve ob_get_clean() arasında yazılan kodlar
    // gösterilmez, hafızaya alınır. ob_get_clean bu veriyi döndürür
    ob_start();

    // extract(['isim' => 'Yılmaz']) şeklinde bi kullanım yaparsanız
    // $isim = 'Yılmaz'; yapmış gibi olursunuz. Yani extract fonksiyonu
    // dizedeki verileri değişkene döndürür
    extract($data);

    // Görünüm dosyasını çağıralım
    require $fullPath;

    // Yaptığımız işlemi döndürelim
    return ob_get_clean();
  }
}

// Bütün uygulama kodları bu kadar!

Çekirdek uygulamamızı yaptık. Artık bunu her sayfaya include ederek kullanabileceğiz. İlk örneklerimizi yapalım:

index.php

<?php

require 'app.php';

router_map('GET', '/', function(){
  echo 'Burası giriş sayfası';
});

router_map('GET', '/test_et', function(){
  echo 'Burası test sayfası';
});

// (\d+) ifadesi bir rakamı ifade eder
// parantez içinde olmasının sebebi bunu parametre olarak tanımlamak içindir
// /yazi/\d+ ile de eşleşir ama parametre olarak çıkmaz
router_map('GET', '/yazi/(\d+)', function($id){
  echo 'Burası yazı sayfası<br>';
  echo "$id numaralı yazı gösterilecek";
});

// Form verisini işlemek için POST metodu kullanıyoruz
router_map('POST', '/yazi/kaydet', function(){
  echo 'Burası yazıyı kayıt sayfası';
});

router_run();

Bunu da çalıştırıp testlerimizi yapalım. http://localhost , http://localhost/test_et , http://localhost/yazi/5 sayfalarına erişmeyi deneyelim ve sonuçları gözlemleyelim.

Her şey yolundaysa, doğru yazdınız demektir. Gelelim bir de olayın View yani görünüm yorumlayıcısına. view_render fonksiyonu ile yapacaklarımız oldukça işimize yarayacak. Öncelikle dosyaların bulunduğu dizinde views klasörü oluşturalım ve içerisinde merhaba.php dosyası oluşturalım. İçerisine Merhaba sevgili <?= $name ?>, <?= $age ?> yaşında olduğun söyleniyor! yazıp kaydedelim ve ilk denememizi bir rota oluşturup yapalım:

router_map('GET', '/merhaba_de/(\w+)', function($urlDenGelenIsim){
  echo view_render('merhaba', [
    'name' => $urlDenGelenIsim,
    'age' => 35
  ]);
});

Hemen http://localhost/merhaba_de/Yilmaz sayfasına girelim. Türkçe karakter kullanmayalım ama yoksa 404 döner, çünkü regexi bu türkçe karakterli yazmadık.

Bunu da yaptıktan sonra işler sizin hayal gücünüze kalıyor. Bu çok basit bir uygulama. Bunu ufak uygulamalarınızda kullanabilirsiniz de. Ama normal bir uygulama için hiç iyi bir çözüm değil. Geliştirmeniz gerekir. Nasıl geliştirebilirsiniz? En basit örnek şu:

function router_get(string $path, callable $callback)
{
  return router_map('GET', $path, $callback);
}

router_get('/', function(){
  echo 'selam!'
});

Böylelikle GET metodu tanımlarken fazla uğraşmamış olacaksınız. Kalın sağlıcakla. Sorularını sormaktan çekinmeyin, seve seve cevaplarım.


Yorumlar