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

Ana sayfa > Programlama > Bgmvc > Core sınıfları

Core sınıfları

Core dizisi sınıfları

BG MVC sisteminde, core dizinindeki dosyalarda tanımlanmış toplam 8 adet temel sınıf kullanılmaktadır.

Core dizisi sınıfları değişken ve fonksiyonlar tablosu

BG MVC temel sınıf tanımlamaları, core dizinindeki .php uzantılı dosyalarda yapılmaktadır.

Denetleyici sınıfı oluşturma (Controller.php)

core dizini altındaki Controller.php dosyası içinde tanımlanan Controller sınıfı, app/controllers dizini altındaki denetleyici dosyalarında yer alan tüm sınıfların türetildiği bir sınıftır.

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


<?php 
namespace Core;

use Core\{View, Config, Request};

class Controller {
    private $_controller_name, $_action_name;
    public $view, $request;

    public function __construct($controller, $action) {        
		$this->_controller_name = $controller;
        $this->_action_name = $action;
        $view_path = strtolower($controller) . '/' .$action;
        $this->view = new View($view_path);
        $this->view->set_layout(Config::get('default_layout'));
        $this->request = new Request();
        $this->onConstruct();
    }

    public function onConstruct(){}
}

Controller sınıfı içinde 4 adet değişken oluşturulur:

  • $_controller_name
  • $_action_name
  • $view
  • $request

app/controllers dizini altındaki denetleyici dosyalarından herhangi birinde yer alan sınıflardan birisinden bir nesne oluşturulduğunda, oluşturulan nesnenin tanımlandığı sınıfın türetildiği Controller sınıfının __construct() fonksiyonu otomatik olarak çağrılarak, $controller_name ve $action_name değerleri parametre olarak geçirilir.

Çağrılan __construct() fonksiyonu ile:

  1. $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.
  2. $controller ve $action değerleriyle View sınıfından bir nesne oluşturularak, Controller sınıfı $view değişkenine atanır.
  3. Config sınıfından okunan default_layout ('default') değeri View nesnesinin set_layout() fonksiyonu ile yine View nesnesinin $_layout değişkenine atanır.
  4. Request sınıfından bir nesne oluşturularak, Controller sınıfı $request değişkenine atanır.

Konfigürasyon dosyası (config.php)

BG MVC projesinin core dizisi altında Config adlı bir sınıf tanımlanır.

config.php dosyasında tanımlı Config sınıfı veya .env dosyasında tanımlı ortam değişkenlerinden birini elde etmek için, Config sınıfında tanımlı get() fonksiyonu kullanılır. get() fonksiyonuna geçirilen parametre karşılığı olan değer, parametre değeri $_ENV değişken dizisinde mevcut ise oradan, değilse Config sınıfı içindeki $config dizisinden okunarak elde edilir.

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


<?php 

namespace Core;

class Config {
    private static $config = [
        'version'             => '1.0.0',
        'default_controller'  => 'Home',    // Ön tanımlı denetleyici
        'default_layout'      => 'default', // Ön tanımlı görünüm yapısı
        'default_site_title'  => 'BG MVC',  // Ön tanımlı site başlığı
    ];

    public static function get($key) {
        if(array_key_exists($key, $_ENV)) return $_ENV[$key];
        return array_key_exists($key, self::$config)? self::$config[$key] : NULL;
    }
}

Ortam değişken dosyası (.env)

Ortam değişken dosyasında genellikle güvenlik sağlamak istediğimiz veriler yer alır.

C:\wamp\www\bgmvc\.env


root_dir="/bgmvc/"
db_host="127.0.0.1"
db_name="veritabani_adi"
db_user="kullanici_adi"
db_password="parola"
login_cookie_name="sgcdnfyrhfuwoqazpw"
exceptions="Exceptions"
main_slide=0
record_limit=4

Veritabanı sınıfı oluşturma (DB.php)

Controller.php içindeki Controller sınıfından türetilen herhangi bir denetleyici sınıfı içindeki bir hareket fonksiyonundan, veritabanı işlemlerini gerçekleştirmek için, DB.php dosyasında tanımlı DB sınıfı oluşturulur.

Hareket fonksiyonunu, veritabanı işlemleri için, DB sınıfındaki getInstance() fonksiyonunu doğrudan çağırarak bir veritabanı nesnesi oluşturur:

$db = DB:get_instance();

Oluşturulan nesne yoluyla, DB sınıfındaki tüm değişken ve fonksiyonlara doğrudan veya dolaylı olarak erişim sağlar.


<?php 
namespace Core;

use \PDO;
use \Exception;
use Core\{Config, H};

class DB {
    protected $_dbh, $_results, $_last_insert_id, $_row_count = 0, $_fetch_type = PDO::FETCH_OBJ, $_class, $_error = false;
    protected $_stmt;
    protected static $_db;
	
    public function __construct() {
        $host = Config::get('db_host');
        $name = Config::get('db_name');
        $user = Config::get('db_user');
        $pass = Config::get('db_password');
        $options = [
            PDO::ATTR_EMULATE_PREPARES => false, 
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
			PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4 COLLATE utf8mb4_turkish_ci' // SET NAMES komutu PHP 5.3.6 öncesi için 
        ];
        try {
            $this->_dbh = new PDO("mysql:host={$host};dbname={$name};charset=utf8mb4", $user, $pass, $options);
        } 
		catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    public static function get_instance() {
        if(!self::$_db) {
           self::$_db = new self();
        }
        return self::$_db;
    }

    public function execute($sql, $bind=[]) {
        $this->_results = null;
        $this->_last_insert_id = null;
        $this->_error = false;
		
        $this->_stmt = $this->_dbh->prepare($sql);
        if(!$this->_stmt->execute($bind)) {
		   $this->_error = true;
        } 
		else {
           $this->_last_insert_id = $this->_dbh->lastInsertId();
        }

        return $this;
    }

    public function query($sql, $bind=[]) {
		$this->execute($sql, $bind);
        
		if(!$this->_error) {
           $this->_row_count = $this->_stmt->rowCount();
           if($this->_fetch_type === PDO::FETCH_CLASS) {
              $this->_results = $this->_stmt->fetchAll($this->_fetch_type, $this->_class);
           } 
		   else {
              $this->_results = $this->_stmt->fetchAll($this->_fetch_type);
           }
        }
		
        return $this;
    }

    public function insert($table, $values) {
        $fields = [];
        $binds = [];
        foreach($values as $key => $value) {
            $fields[] = $key;
            $binds[] = ":{$key}";
        }
        $field_str = implode('`, `', $fields);
        $bind_str = implode(', ', $binds);
        $sql = "INSERT INTO {$table} (`{$field_str}`) VALUES ({$bind_str})";
        $this->execute($sql, $values);
		
        return !$this->_error;
    }

    public function update($table, $values, $conditions) {
        $binds = [];
        $value_str = "";
        foreach($values as $field => $value) {
            $value_str .= ", `{$field}` = :{$field}";
            $binds[$field] = $value;
        }
        $value_str = ltrim($value_str, ', ');
        $sql = "UPDATE {$table} SET {$value_str}";

        if(!empty($conditions)) {
            $condition_str = " WHERE ";
            foreach($conditions as $field => $value) {
                $condition_str .= "`{$field}` = :cond{$field} AND ";
                $binds['cond'.$field] = $value;
            }
            $condition_str = rtrim($condition_str, ' AND ');
            $sql .= $condition_str;
        }
        $this->execute($sql, $binds);
        return !$this->_error;
    }

    public function results() {
        return $this->_results;
    }

    public function count() {
        return $this->_row_count;
    }

    public function last_insert_id() {
        return $this->_last_insert_id;
    }

    public function set_class($class) {
        $this->_class = $class;
    }

    public function get_class() {
        return $this->_class;
    }

    public function set_fetch_type($type) {
        $this->_fetch_type = $type;
    }

    public function get_fetch_type() {
        return $this->_fetch_type;
    }
}

Controller.php içindeki Controller sınıfından türetilen herhangi bir denetleyici sınıfı içindeki bir hareket fonksiyonu, veritabanı işlemleri için, DB sınıfındaki get_instance() fonksiyonunu doğrudan çağırarak bir veritabanı nesnesi oluşturarak işlem yapar.

Model sınıfı oluşturma (Model.php)

Controller.php içindeki Controller sınıfından türetilen herhangi bir denetleyici sınıfı içindeki bir hareket fonksiyonundan, veritabanı işlemlerini gerçekleştirmek için oluşturulan DB.php dosyasında tanımlı DB sınıfına erişim sağlayarak bu sınıfın fonksiyonlarını kullanmak için, Model.php dosyasında tanımlı Model sınıfı oluşturulur.

app/controllers dizininde tanımlı bir denetleyici hareket fonksiyonu içinden, kullanılacak veritabanı tablosu için Model sınıfından türetilerek oluşturulmuş olan bir sınıf arayüzü üzerinden Model sınıfı fonksiyonları doğrudan çağrılarak DB sınıfı fonksiyonlarına erişim sağlanır.


<?php 
namespace Core;
use \PDO;
use Core\{DB, Router};

class Model {
    protected static $table = "";
    protected static $columns = false;
    protected $_validation_passed = true, $_errors = [], $_skip_update = [];

    protected static function get_db($set_fetch_class = false) {
        $db = DB::get_instance();
        if($set_fetch_class) {
            $db->set_class(get_called_class());
            $db->set_fetch_type(PDO::FETCH_CLASS);
        }
        return $db;
    }

    public static function insert($values) {
        $db = static::get_db();
        return $db->insert(static::$table, $values);
    }

    public static function update($values, $conditions) {
        $db = static::get_db();
        return $db->update(static::$table, $values, $conditions);
    }

    public function delete() {
        $db = static::get_db();
        $table = static::$table;
        $params = [
            'conditions' => "id = :id",
            'bind' => ['id' => $this->id]
        ];
        list('sql' => $conds, 'bind' => $bind) = self::query_param_builder($params);
        $sql = "DELETE FROM {$table} {$conds}";
        return $db->execute($sql, $bind);
    }

    public static function find($params = []) {
        $db = static::get_db(true);
        list('sql' => $sql, 'bind' => $bind) = self::select_builder($params);
		
        return $db->query($sql, $bind)->results();
    }

    public static function find_first($params = []) {
        $db = static::get_db(true);
        list('sql' => $sql, 'bind' => $bind) = self::select_builder($params);
        
		$results = $db->query($sql, $bind)->results();
        return isset($results[0])? $results[0] : false;
    }

    public static function find_by_id($id) {
        return static::find_first([
            'conditions' => "id = :id", 
            'bind' => ['id' => $id]
        ]);
    }

    public static function find_total($params = []) {
        unset($params['limit']);
        unset($params['offset']);
        $table = static::$table;
        $sql = "SELECT COUNT(*) AS total FROM {$table}";
        list('sql' => $conds, 'bind' => $bind) = self::query_param_builder($params);
        $sql .= $conds;
        $db = static::get_db();
        $results = $db->query($sql, $bind);
        $total = sizeof($results->results()) > 0 ? $results->results()[0]->total : 0;
        return $total;
    }

    public function save() {
        $save = false;
		$this->before_save();
		
		if($this->_validation_passed) {
            $db = static::get_db();
            $values = $this->get_values_for_save();
            if($this->is_new(()) {
                $save = $db->insert(static::$table, $values);
                if($save) {
                    $this->id = $db->last_insert_id();
                }
            } else {
                $save = $db->update(static::$table,$values, ['id' => $this->id]);
            }
        }
		
        return $save;
    }

    public function is_new(() {
        return empty($this->id);
    }

    public static function select_builder($params = []) {
        $columns = array_key_exists('columns', $params)? $params['columns'] : "*";
        $table = static::$table;
        $sql = "SELECT {$columns} FROM {$table}";
        list('sql' => $conds, 'bind' => $bind) = self::query_param_builder($params);
        $sql .= $conds;
        return ['sql' => $sql, 'bind' => $bind];
    }

    public static function query_param_builder($params = []) {
        $sql = "";
        $bind = array_key_exists('bind', $params)? $params['bind'] : [];
        // joins
        // [['table2', 'table1.id = table2.key', 'tableAlias', 'LEFT' ]]
        if(array_key_exists('joins', $params)) {
            $joins = $params['joins'];
            foreach($joins as $join) {
                $join_table = $join[0];
                $join_on = $join[1];
                $join_alias = isset($join[2])? $join[2] : "";
                $join_type = isset($join[3])? "{$join[3]} JOIN" : "JOIN";
                $sql .= " {$join_type} {$join_table} {$join_alias} ON {$join_on}";
            }
        }

        // where 
        if(array_key_exists('conditions', $params)) {
            $conds = $params['conditions'];
            $sql .= " WHERE {$conds}";
        }

        // group 
        if(array_key_exists('group', $params)) {
            $group = $params['group'];
            $sql .= " GROUP BY {$group}";
        }

        // order
        if(array_key_exists('order', $params)) {
            $order = $params['order'];
            $sql .= " ORDER BY {$order}";
        } 

        // limit
        if(array_key_exists('limit', $params)) {
            $limit = $params['limit'];
            $sql .= " LIMIT {$limit}";
        }

        // offset
        if(array_key_exists('offset', $params)) {
            $offset = $params['offset'];
            $sql .= " OFFSET {$offset}";
        }
        return ['sql' => $sql, 'bind' => $bind];
    }

    public function get_values_for_save() {
        $columns = static::get_columns();
        $values = [];
        foreach($columns as $column) {
            if(!in_array($column, $this->_skip_update)) {
               $values[$column] = $this->{$column};
            }
        }
        return $values;
    }

    public static function get_columns() {
        if(!static::$columns) {
           $db = static::get_db();
           $table = static::$table;
           $sql = "SHOW COLUMNS FROM {$table}";
           $results = $db->query($sql)->results();
           $columns = [];
           foreach($results as $column) {
               $columns[] = $column->Field;
           }
           static::$columns = $columns;
        }
        return static::$columns;
    }

    public function run_validation($validator) {
		$validates = $validator->run_validation();
        if(!$validates) {
           $this->_validation_passed = false;
           $this->_errors[$validator->field] = $validator->msg;
        }
    }

    public function get_errors() {
        return $this->_errors;
    }

    public function set_error($name, $value) {
        $this->_errors[$name] = $value;
        $this->_validation_passed = false;
    }

    public function time_stamps() {
        $dt = new \DateTime("now", new \DateTimeZone("UTC"));
        $now = $dt->format('Y-m-d H:i:s');
        $this->updated_at = $now;
        if($this->is_new(()) {
           $this->created_at = $now;
        }
    }

    public static function merge_with_pagination($params, $pageno, $total) {
		if(isset($_POST['records_limit'])) { 
		   $_SESSION['records_limit'] = $_POST['records_limit'];
	    }
	    $limit = isset($_SESSION['records_limit']) ? $_SESSION['records_limit'] : (int) Config::get('record_limit');
		if((int)$pageno>ceil($total/$limit)) {
		   Router::redirect('exceptions/index/nopage');	
		}		
	    $page = $pageno!='' ? $pageno : 1;
	    $pagination_start = ($page - 1) * $limit;
	    $params['limit'] = $limit;
	    $params['offset'] = $pagination_start;
		
		return [$params, $page];
    }	

    public function before_save(){}
}

Görüntü sınıfı oluşturma (View.php)

Controller.php içindeki Controller sınıfından türetilen herhangi bir denetleyici sınıfı içindeki bir hareket fonksiyonundan, yapılan görüntü işlemlerini gerçekleştirmek için, View.php dosyasında tanımlı View sınıfı oluşturulur.


<?php 
namespace Core;

use Core\{Config, Router};

class View {
    public $articles, $total, $heading;
	public $errors, $user, $header, $users, $article;
	public $msg, $msg_txt, $limit, $page, $url;
	
	private $_site_title = '', $_content = [], $_current_content, $_buffer, $_layout;
    private $_default_view_path;

    public function __construct($path = '') {
       $this->_default_view_path = $path;
       $this->_site_title = Config::get('default_site_title');
    }

    public function set_layout($layout) {
        $this->_layout = $layout;
    }

    public function set_site_title($title) {
        $this->_site_title = $title;
    }

    public function get_site_title() {
        return $this->_site_title;
    }

    public function render($path = '') {
        if(empty($path)) {
           $path = $this->_default_view_path;
        }
        $layout_path = FROOT . DS . 'app' . DS . 'views' . DS . 'layouts' . DS . $this->_layout . '.php';
        $full_path = FROOT . DS . 'app' . DS . 'views' . DS . $path . '.php';
		
		if(!file_exists($full_path)) {
           Router::redirect('exceptions/index/noview');
        }
        if(!file_exists($layout_path)) {
           Router::redirect('exceptions/index/notemplate');
        }
        
		include($full_path);
        include($layout_path);
    }

    public function start($key) {		
		if(empty($key)) {
		   Router::redirect('exceptions/index/nokey');	
        }
        $this->_buffer = $key;
		
        ob_start();
    }

    public function end() {
        if(empty($this->_buffer)) {
		   Router::redirect('exceptions/index/nostart');	
        } 
        $this->_content[$this->_buffer] = ob_get_clean();
        $this->_buffer = null;
    }

    public function content($key) {
        if(array_key_exists($key, $this->_content)) {
           echo $this->_content[$key];
        } 
		else {
           echo '';
        }
    }

    public function inc($path) {
		$full_path = FROOT . DS . 'app' . DS . 'views' . DS . $path . '.php';

		if(file_exists($full_path)) {           
		   include($full_path);
        }
    }
}

Görüntü sınıfı kullanımı

1. Controller.php içindeki Controller sınıfından türetilen herhangi bir denetleyici sınıfı içindeki bir hareket fonksiyonundan, Controller sınıfı içinde tanmlı view değişkeni yoluyla, View.php dosyasında tanımlı View sınıfı içindeki render() fonksiyonu çağrılır.

$this->view->render('home/index');

2. render() fonksiyonu, kendisine geçirilen parametre değerine (home/index) karşılık gelen dosya ile şablon dosyası mevcut ise, sırayla görünüme dahil eder:

  1. C:\wamp\www\bgmvc\app\views\home\index.php
  2. C:\wamp\www\bgmvc\app\views\layouts\default.php

Yönlendirici sınıfı oluşturma (Router.php)

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.
  12. call_user_func_array() fonksiyonunu kullanarak, HomeController sınıfındaki $action fonksiyonunu (indexAction) $url parametreleri ile çağırır.

Request sınıfı oluşturma (Request.php)

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


<?php 
namespace Core;

class Request {

    public function isPost() {
        return $this->get_request_method() === 'POST';
    }

    public function isPut(){
        return $this->get_request_method() === 'PUT';
    }

    public function isGet(){
        return $this->get_request_method() === 'GET';
    }

    public function isDelete(){
        return $this->get_request_method() === 'DELETE';
    }

    public function isPatch(){
        return $this->get_request_method() === 'PATCH';
    }

    public function get_request_method(){
        return strtoupper($_SERVER['REQUEST_METHOD']);
    }

    public function get($input = false) {
        if(!$input) {
           $data = [];
           
		   foreach($_REQUEST as $field => $value) {
               $data[$field] = self::sanitize($value);
           }
           
		   return $data;
        }
        return array_key_exists($input, $_REQUEST) ? self::sanitize($_REQUEST[$input]) : false;
    }

    public static function sanitize($dirty) {
        return htmlentities(trim($dirty), ENT_QUOTES, "UTF-8");
    }
}

Oturum sınıfı oluşturma (Session.php)

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


<?php 
namespace Core;
use Core\{Request, H};

class Session {

    public static function exists($name) {
        return isset($_SESSION[$name]);
    }

    public static function set($name, $value) {
        $_SESSION[$name] = $value;
    }

    public static function get($name) {
        if(self::exists($name) && !empty($_SESSION[$name])) {
           return $_SESSION[$name];
        }
        return false;
    }

    public static function delete($name) {
        unset($_SESSION[$name]);
    }

    public static function create_csrf_token() {
        $token = md5('csrf'.time());
        self::set('csrfToken', $token);
        return $token;
    }

    public static function csrf_check() {
        $request = new Request();
        $check = $request->get('csrfToken');
        if(self::exists('csrfToken') && self::get('csrfToken') == $check){
            return true;
        }
        Router::redirect('exceptions/index/badtoken'); // Router::redirect('home/badToken');
    }

    public static function msg($msg, $type = 'danger') {
        $alerts = self::exists('session_alerts')? self::get('session_alerts') : [];
        $alerts[$type][] = $msg;
        self::set('session_alerts', $alerts);
    }

    public static function display_session_alerts() {
        
		$alerts = self::exists('session_alerts')? self::get('session_alerts') : [];
        $html = "";
        foreach($alerts as $type => $msgs) {
            foreach($msgs as $msg) {
                $html .= "<div class='alert alert-{$type} alert-dismissible m-3' role='alert'><div>{$msg}</div><button type='button' class='btn-close' data-bs-dismiss='alert' aria-label='Close'></button></div>";
            }
        }
        self::delete('session_alerts');
		
        return $html;
    }
}

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


<?php 
namespace Core;

class Cookie {
    public static function get($name) {
        if(self::exists($name)) {
           return $_COOKIE[$name];
        }
        return false;
    }

    public static function set($name, $value, $expiry) {
        if(setCookie($name, $value, time()+$expiry, '/')){
           return true;
        }
        return false;
    }

    public static function delete($name) {
        return self::set($name, '', -1);
    }

    public static function exists($name) {
        return isset($_COOKIE[$name]);
    }
}

Yardımcı fonksiyonlar sınıfı oluşturma (H.php)

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


<?php 
namespace Core;

class H {
	
    public static function remove_root($url) {
		if(ROOT != '/') { // En soldaki '/bgmvc/' (Lokal sunucu) veya '/' (Uzak sunucu) değerini kaldırma
		   $url = str_replace(ROOT, '', $url);
		} 
		else { // '/' değerini kaldırma (Canlı sunucu) $url = '/' -> $url = ''
		   $url = ltrim($url, '/'); 
		}	
	
	    // $url değerinin en sağında yer alabilecek id=21&md=34 gibi değerleri silmek için
		$url = preg_replace('/(\?.+)/', '', $url); 
	
	    return $url;
    }	

    public static function get_action() {
		$url = self::remove_root($_SERVER['REQUEST_URI']);         
		$url = explode ('/', $url);
        
		$link = ((isset($url[0]) && $url[0]!='') ? $url[0] : 'home') . '/' . (isset($url[1]) ? $url[1] : 'index') . (isset($url[2]) && ($url[1]=='author' || $url[1]=='category') ? ('/' . $url[2]) : '');
		
        return $link;
    }	

    public static function dnd($data=[], $die = true){
        echo "<pre>";
        var_dump($data);
        echo "</pre>";
        if($die) {
           die;
        }
    }

    public static function is_current_page($page) {
        global $current_page;
		
		if(!empty($page) && strpos($page, ':id') > -1) {
            $page = str_replace(":id", "", $page);
			return strpos($current_page, $page) > -1;
        }
		return $page == $current_page;
    }
	
    public static function active_class($page, $class = '') {
        $active = self::is_current_page($page);
        $class = $active ? $class . " active" : $class;
		
        return $class;
    }

    public static function nav_item($link, $label, $is_dropdown_item = false) {
		$active = self::is_current_page($link);		
        $class = self::active_class($link, 'nav-item');
        $link_class = $is_dropdown_item ? 'dropdown-item' : 'nav-link';
        $link_class .= $active ? " active" : "";
        $link = ROOT . $link;
		
        $html = "<li>";
        $html .= "<a class=\"{$link_class}\" href=\"{$link}\" >{$label}</a>";
        $html .= "</li>";
        return $html;
    }    
}

Form yardımcı fonksiyonlar sınıfı oluşturma (FH.php)

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


<?php 
namespace Core;

class FH {
	
    public static function input_block($label, $id, $value, $input_attrs =[], $wrapper_attrs = [], $errors = []) {
        $wrapper_str = self::process_attrs($wrapper_attrs);
        $input_attrs = self::append_errors($id, $input_attrs, $errors);
        $input_attrs = self::process_attrs($input_attrs);
        $error_msg = array_key_exists($id, $errors)? $errors[$id] : "";
        $html = "<div {$wrapper_str}>";
        $html .= "<label for='{$id}' class='form-label small'>{$label}</label>";
        $html .= "<input id='{$id}' name='{$id}' value='{$value}' {$input_attrs} />";
        $html .= "<div class='invalid-feedback'>{$error_msg}</div></div>";
        return $html;
    }

    public static function select_block($label, $id, $value, $options, $input_attrs=[], $wrapper_attrs=[], $errors=[]) {
        
		$input_attrs = self::append_errors($id, $input_attrs, $errors);
        $input_attrs = self::process_attrs($input_attrs);
        $wrapper_str = self::process_attrs($wrapper_attrs);
        $error_msg = array_key_exists($id, $errors)? $errors[$id] : "";
        $html = "<div {$wrapper_str}>";
        $html .= "<label for='{$id}' class='form-label small'>{$label}</label>";
        $html .= "<select id='{$id}' name='{$id}' {$input_attrs}>";
        foreach($options as $val => $display) {
            $selected = $val == $value? ' selected ' : "";
            $html .= "<option value='{$val}'{$selected}>{$display}</option>";
        }
        $html .= "</select>";
        $html .= "<div class='invalid-feedback'>{$error_msg}</div></div>";
        return $html;
    }

    public static function check($label, $id, $checked = '', $input_attrs=[], $wrapper_attrs=[], $errors=[]) {
        $input_attrs = self::append_errors($id, $input_attrs, $errors);
        $wrapper_str = self::process_attrs($wrapper_attrs);
        $input_str = self::process_attrs($input_attrs);
        $checked_str = $checked == 'on'? "checked" : "";
        $html = "<div {$wrapper_str}>";
        $html .= "<input type=\"checkbox\" id=\"{$id}\" name=\"{$id}\" {$input_str} {$checked_str}>";
        $html .= "<label class=\"form-check-label\" for=\"{$id}\">{$label}</label></div>";
        return $html;
    }

    public static function text_area($label, $id, $value, $input_attrs =[], $wrapper_attrs = [], $errors = []) {
        $wrapper_str = self::process_attrs($wrapper_attrs);
        $input_attrs = self::append_errors($id, $input_attrs, $errors);
        $input_attrs = self::process_attrs($input_attrs);
        $error_msg = array_key_exists($id, $errors)? $errors[$id] : "";
        $html = "<div {$wrapper_str}>";
        $html .= "<label for='{$id}' class='form-label small'>{$label}</label>";
        $html .= "<textarea id='{$id}' name='{$id}' value='{$value}' {$input_attrs}>{$value}</textarea>";
        $html .= "<div class='invalid-feedback'>{$error_msg}</div></div>";
        return $html;		
    }

    public static function append_errors($key, $input_attrs, $errors) {
        if(array_key_exists($key, $errors)) {
            if(array_key_exists('class', $input_attrs)) {
               $input_attrs['class'] .= ' is-invalid';
            } 
			else {
               $input_attrs['class'] = 'is-invalid';
            }
        }
        return $input_attrs;
    }

    public static function process_attrs($attrs) {
        $html = "";
        foreach($attrs as $key => $value) {
            $html .= " {$key}='{$value}'";
        }
        return $html;
    }

    public static function csrf_field(){
        $token = Session::create_csrf_token();
        $html = "<input type='hidden' value='{$token}' name='csrfToken' />";
        return $html;
    }
}