BG MVC Model View Controller eğitim serisi yayında...

Ana sayfa > Programlama > Bgmvc > Yönlendirici (Router) sınıfı

Yönlendirici (Router) sınıfı

Proje ana dizinde bulunan index.php dosyasına yönlendirilen tüm akışlar, gerekli işlemler yapıldıktan sonra, URL değerleri parameter olarak kullanılarak, Router.php dosyasındaki Router sınıfı içinde yer alan route($url) yönlendirici fonksiyonuna geçirilir.

Ana dizin altındaki core dizini altında Router.php adlı bir dosya oluşturulur:

C:\wamp\www\bgmvc\core\Router.php


<?php 
namespace Core;

use Core\Session;
use App\Models\Users;

class Router {

    public static function route($url) {
		$error = false;
		// $url[0] = 'Denetleyici'
		$controller = !empty($url[0]) ? ucwords($url[0]) : Config::get('default_controller'); // $controller = 'Home'
		$controller_name = $controller; // $controller_name = 'Home'
        $controller = '\App\Controllers\\' . $controller . 'Controller'; // $controller = '\App\Controllers\HomeController'
		
		// URL satırında sadece denetleyici adı girilmişse ve bu denetleyiciye ait index.php dosyası varsa, 
        // index değerini action olarak URL'ye eklemek için (İçinde index.php olmayan denetleyici dizinlerine girişi bloke etmek için)		
		$index_exist = file_exists(FROOT . DS . 'app' . DS . 'views' . DS . lcfirst($controller_name) . DS . 'index.php');

        // $url içinde denetleyici ve hareket değeri mevcutsa, denetleyici değerini siler, hareket değeri dizi başına gelir.
		array_shift($url); // $url[0] = 'Hareket' 
		
		// Seçilen yol içinde action var ise atanır, yoksa $controller_name'e ait index.php dosyası varsa 'index' atanır.
		// Böylece diğer dizinler için mevcut bir index.php yoksa $action boş kalır ve method_exists() koşulunu geçemez.
		$action = !empty($url[0]) ? $url[0] : ($index_exist ? 'index' : ''); // $action = 'index'
		$action_name = $action; // $action_name = 'index'
        $action .= "Action"; // $action = 'indexAction'
		
		// $url içinde hareket değeri mevcutsa, hareket değerini siler, parametre değeri dizi başına gelir.
		array_shift($url); // $url[0] = 'Parametre 1', $url[1] = 'Parametre 2', ... 

		// Denetleyici sınıfı veya hareket metodu mevcut değilse, $url dizisine sayfanın mevcut olmadığını gösteren bir değer atar.
		if(!class_exists($controller) || !method_exists($controller, $action)) {
		   $url = ["nopage"];
		   $error = true;
        }
		else {
		   // acl kontrolü (acl.json dosyası ile menüde gösterilmeyen sayfalara erişimi engeller)
		   // Menüde gösterilen sayfalara erişimi engellemek için ayrıca işlem gerekir (if($current_user->acl=='admin')). 
		   $grant_access = self::has_access($controller_name, $action_name);

		   // Kullanıcının giriş izni yoksa, $url dizisine giriş izni olmadığını gösteren bir değer atar.
		   if(!$grant_access) {
			  $url = ["noaccess"];
			  $error = true;
		   }
		}		
		
		// Sınıf veya metod olmamasından (nopage) veya giriş izni olmamasından (noaccess) kaynaklı hata varsa,
        // sayfayı hata gösterimine yönlendirecek şekilde, denetleyici ve hareket adını değiştirir.
		if($error) {
		   $controller = str_replace($controller_name, Config::get('exceptions'), $controller);  
		   $controller_name = Config::get('exceptions'); // $controller_name = 'Exceptions'
		   $action_name = 'index'; // $action_name = 'index'
		   $action = $action_name . 'Action'; // $action = 'indexAction' 
		}

	    // HomeController sınıfı cinsinden bir nesne oluşturur. Bu işlemi yaparken, HomeController sınıfının türetildiği Controller 
		// sınıfının __construct() fonksiyonunu otomatik olarak çağırarak, $controller_name ve $action_name değerlerini geçirir.  
		// $controller = '\App\Controllers\HomeController', $controller_name = 'Home', $action_name = 'index'
		$controller_class = new $controller($controller_name, $action_name);
		
		// HomeController sınıfındaki $action fonksiyonunu (indexAction) $url parametreleri ile çağırma
		call_user_func_array([$controller_class, $action], $url); // $action = 'indexAction', $url = params
    }
	
    public static function redirect($location) {
        if(!headers_sent()) {
           header('Location: ' . ROOT . $location);
        } 
		else {
           echo '<script type="text/javascript">';
           echo 'window.location.href = "'. ROOT . $location .'"';
           echo '</script>';
           echo '<nosript>';
           echo '<meta http-equiv="refresh" content="0;url=' . ROOT . $location . '" />';
           echo '</nosript>';
        }
        exit();
    }

    public static function perm_redirect($perm, $redirect, $msg = "Bu sayfaya giriş yapamazsınız.") {
        $user = Users::get_current_user();
        $allowed = $user && $user->has_permission($perm);
        if(!$allowed) {
           Session::msg($msg);
           self::redirect($redirect);
        } 
    }
	
    public static function get_menu($menu) {
      $menu_array  = [];
      $menu_file = file_get_contents(FROOT . DS . 'app/views/inc' . DS . $menu . '.json');
      $acl = json_decode($menu_file, true);
	  
      foreach($acl as $key => $val) {
        if(is_array($val)) {
		   $sub = [];
           foreach($val as $k => $v) {
			 if(substr($k,0,9) == 'separator' && !empty($sub)) {
                $sub[$k] = '';
                continue;
             }
			 else if($final_val = self::get_link($v)) {
                $sub[$k] = $final_val;
             }
           }
           if(!empty($sub)) {
              $menu_array[$key] = $sub;
           }
        } 
		else {
		   if($final_val = self::get_link($val)) {
              $menu_array[$key] = $final_val;
           }
        }
      }
	  
      return $menu_array;
    }	
	
    private static function get_link($val) {
	  
	  // Harici bağlantı ise
      if(preg_match('/https?:\/\//', $val) == 1) {
	     return $val;
      } 
	  else {
         $uAry = explode('/', $val);
         $controller_name = ucwords($uAry[0]);
         $action_name = (isset($uAry[1]))? $uAry[1] : '';
		 
		 if(self::has_access($controller_name, $action_name)) {            
			return $val;
         }
         
		 return false;
      }
    }	
	
    public static function has_access($controller_name, $action_name='index') {
      $acl_file = file_get_contents(FROOT . DS . 'app/views/inc' . DS . 'acl.json');
      $acl = json_decode($acl_file, true);
      $current_user_acls = ["Guest"];
      $grant_access = false;
	  
      if(Session::exists('logged_in_user')) {
         $current_user_acls[] = "LoggedIn";
		 $current_user_acls[] = ucwords(Users::get_current_user()->acl); // Veritabanındaki users tablosunda acl değeri tek ise
      }
	  
      foreach($current_user_acls as $level) { // 'Guest', 'LoggedIn' ve 'Admin'
		if(array_key_exists($level, $acl) && array_key_exists($controller_name, $acl[$level])) {
		   if(in_array($action_name, $acl[$level][$controller_name]) || in_array("*", $acl[$level][$controller_name])) {
			  $grant_access = true;
              break;
           }
        }
      }
	  
      // Girişi bloke edilen menü seçenekleri
      foreach($current_user_acls as $level) {
        $denied = $acl[$level]['denied'];
        if(!empty($denied) && array_key_exists($controller_name, $denied) && in_array($action_name, $denied[$controller_name])) {
           $grant_access = false;
           break;
        }
      }
	  
	  return $grant_access;
    }
}

Router.php dosyasında sırayla aşağıdaki işlemler gerçekleştirilir:

  1. Eğer $url dizisi boş değilse, $url dizisinin ilk elemanı olan denetleyici adı baş harfi büyütülerek, boş ise Config sınıfından okunan ön tanımlı denetleyici adı ('Home') $controller değişkenine atanır.
  2. $controller değişken değeri $controller_name değişkenine atanır.
  3. $controller değişkeninin başına denetleyicilerin yol tanımlaması sonuna ise 'Controller' ifadesi eklenir.
  4. URI satırında sadece denetleyici adı girilmişse ve bu denetleyiciye ait index.php dosyası varsa, 'index' değerini action olarak URI'ye eklemek için, dosyanın olup olmadığı kontrol edilerek sonuç $index_exist değişkenine atanır (İçinde index.php olmayan denetleyici dizinlerine girişi bloke etmek için)
  5. array_shift($url) fonksiyonu ile $url içinde denetleyici ve hareket değeri mevcutsa, denetleyici değerini siler, hareket değeri dizi başına gelir.
  6. Seçilen yol içinde action var ise atanır, yoksa $controller_name'e ait index.php dosyası varsa 'index' atanır. Böylece, diğer dizinler için mevcut bir index.php yoksa $action boş kalır ve method_exists() koşulunu geçemez.

  7. array_shift($url) fonksiyonu ile $url içinde hareket değeri mevcutsa, hareket değerini siler, parametre değeri dizi başına gelir.
  8. class_exists() ve method_exists() fonksiyonlarını kullanarak, denetleyici sınıfı veya hareket metodu mevcut değilse, $url dizisine sayfanın mevcut olmadığını gösteren bir değer atar, mevcut ise işlem devam eder.

  9. acl.json dosyasının içeriğini kontrol ederek, kullanıcının sayfaya erişim yetkisi olup olmadığını belirler ve sonucu $grant_access değişkenine atar. Kullanıcının giriş izni yoksa, $url dizisine giriş izni olmadığını gösteren bir değer atar.
  10. Sınıf veya metod olmamasından (nopage) veya giriş izni olmamasından (noaccess) kaynaklı hata varsa, sayfayı hata gösterimine yönlendirecek şekilde, denetleyici ve hareket adını değiştirirerek akışı hata sayfasına yönlendirir.
  11. HomeController sınıfı cinsinden bir nesne oluşturur. Bu işlemi yaparken, HomeController sınıfının türetildiği Controller sınıfının __construct() fonksiyonunu otomatik olarak çağırarak, $controller_name ve $action_name değerlerini geçirir. Çağrılan __construct() fonksiyonu ile:
    • $controller_name ve $action_name değerleri Controller sınıfının sırasıyla $_controller_name ve $_action_name değişkenlerine atanır.
    • $controller ve $action değerleriyle View sınıfından bir nesne oluşturularak, Controller sınıfı $view değişkenine atanır.
    • Config sınıfından okunan default_layout ('default') değeri View nesnesinin setLayout() fonksiyonu ile yine View nesnesinin $_layout değişkenine atanır.
    • Request sınıfından bir nesne oluşturularak, Controller sınıfı $request değişkenine atanır.
    
    class Controller {
        private $_controller_name, $_action_name;
        public $view, $request;
    
        public function __construct($controller, $action) {
            
    		$this->_controller_name = $controller;
            $this->_action_name = $action;
            $viewPath = strtolower($controller) . '/' .$action;
            $this->view = new View($viewPath);
            $this->view->setLayout(Config::get('default_layout'));
            $this->request = new Request();
            $this->onConstruct();
        }
    
        public function onConstruct(){}
    }	
    
    
  12. call_user_func_array() fonksiyonunu kullanarak, HomeController sınıfındaki $action fonksiyonunu (indexAction) $url parametreleri ile çağırır.

Yerel sunucuda, siteye ilk giriş durumunda değişkenlerin aldığı değerler aşağıda gösterilmektedir:

  1. Yerel sunucuda, siteye ilk giriş durumunda değişkenlerin aldığı değerler aşağıda gösterilmektedir:

    http://localhost/bgmvc/ (Siteye ilk giriş)

    1. $url değeri:
    array (size=1)
      0 => string '' (length=0)
    $controller_name: Home
    $controller: \App\Controllers\HomeController
    
    2. $url değeri:
    array (size=0)
      empty
    $action_name: index
    $action: indexAction
    
    3. $url değeri:
    array (size=0)
      empty
    $controller: \App\Controllers\HomeController
    $action: indexAction
    $controller_name: Home
    $action_name: index
    $grant_access: İzin var
    
    $controller_class değeri:
    object(App\Controllers\HomeController)[46]
      private '_controller_name' (Core\Controller) => string 'Home' (length=4)
      private '_action_name' (Core\Controller) => string 'index' (length=5)
      public 'view' => 
        object(Core\View)[43]
          public 'articles' => null
          public 'total' => null
          public 'heading' => null
          public 'errors' => null
          public 'user' => null
          public 'header' => null
          public 'users' => null
          public 'article' => null
          public 'msg' => null
          public 'limit' => null
          public 'page' => null
          public 'url' => null
          private '_site_title' => string 'BG MVC' (length=6)
          private '_content' => 
            array (size=0)
              empty
          private '_current_content' => null
          private '_buffer' => null
          private '_layout' => string 'default' (length=7)
          private '_default_view_path' => string 'home/index' (length=10)
      public 'request' => 
        object(Core\Request)[38]
           
  2. Yerel sunucuda, siteye denetleyici, hareket ve bir parametre ile giriş durumunda değişkenlerin aldığı değerler aşağıda gösterilmektedir:

    http://localhost/bgmvc/home/details/5 (Denetleyici, hareket ve bir parametre)

    1. $url değeri:
    array (size=3)
      0 => string 'home' (length=4)
      1 => string 'details' (length=7)
      2 => string '5' (length=1)
    $controller_name: Home
    $controller: \App\Controllers\HomeController
    
    2. $url değeri:
    array (size=2)
      0 => string 'details' (length=7)
      1 => string '5' (length=1)
    $action_name: details
    $action: detailsAction
    
    3. $url değeri:
    array (size=1)
      0 => string '5' (length=1)
    $controller: \App\Controllers\HomeController
    $action: detailsAction
    $controller_name: Home
    $action_name: details
    $grant_access: İzin var
    
    $controller_class değeri:
    object(App\Controllers\HomeController)[46]
      private '_controller_name' (Core\Controller) => string 'Home' (length=4)
      private '_action_name' (Core\Controller) => string 'details' (length=7)
      public 'view' => 
        object(Core\View)[43]
          public 'articles' => null
          public 'total' => null
          public 'heading' => null
          public 'errors' => null
          public 'user' => null
          public 'header' => null
          public 'users' => null
          public 'article' => null
          public 'msg' => null
          public 'limit' => null
          public 'page' => null
          public 'url' => null
          private '_site_title' => string 'BG MVC' (length=6)
          private '_content' => 
            array (size=0)
              empty
          private '_current_content' => null
          private '_buffer' => null
          private '_layout' => string 'default' (length=7)
          private '_default_view_path' => string 'home/details' (length=12)
      public 'request' => 
        object(Core\Request)[38]