📜 ⬆️ ⬇️

HMVC in the namespace

Recently, a lot has been said about the MVC design scheme, almost all popular PHP frameworks have long since switched to this scheme. As for Kohana , starting from version 3, a hierarchical MVC - HMVC scheme has been implemented. The advantages of HMVC are clear to everyone and we will not discuss them here.

As a person who wanted to change the status of “amateur programmer” to “professional programmer”, I decided that I had already learned enough PHP and it’s time to start working with frameworks, the choice of my very first framework fell on Kohana, since it implements the HMVC, well, in general, it is much praised for its simplicity.

In reality, everything turned out to be not easy, but the absence of isolation of the MVC triads was especially straining, the triads were broken up into parts and placed in different folders. In my understanding, the Model-View-Controller triad should be isolated from other triads, and be in the same folder, of course, this could be implemented in Kohana, but everything will look very clumsy and confusing. It was also not clear why not use such a great PHP feature as a namespace? I looked through some more HMVC-frameworks, but not to one “the soul did not lay down”. Then I decided to independently implement HMVC.
')
Immediately set several directives:


Then he added a few more:


The result was a small HMVC structure, Model does not matter, so the code does not cite, I can only say that I tend to the static class.

So, the very first class of Autoload :
To uncover
class Autoload { static $dirs=array(); //      ! static function add( $dir ){ if( !in_array($dir,self::$dirs) ){ self::$dirs[]=$dir; } } static function findClass( $className ){ $path=str_replace('\\','/',$className).'.php'; foreach( self::$dirs as $dir ){ $file=$dir.$path; if( is_file($file) ){ return $file; } } return false; } static function loadClass( $className ){ if( $file=self::findClass( $className ) ){ require $file; } } } spl_autoload_register( '\\Autoload::loadClass' ); Autoload::add( __DIR__.'/'); 


Class NameSpaceAdapter :
To uncover
 namespace lib; class NameSpaceAdapter { public function getDirectory(){ return dirname( \Autoload::findClass( get_class($this) ) ).'/'; } public function getNameSpace(){ $c=trim(str_replace('\\',' ',get_class($this))); $path=explode(' ',$c); if( count($path) ){ array_pop($path); return ('\\'.implode('\\',$path)); } return null; } } 


Controller class:
To uncover
 namespace lib; class Controller extends NameSpaceAdapter { protected $view_data=array(); protected $params=array(); protected $action=null; protected $uri=null; public function __construct( $a_uri=null , $controller_uri=null ){ $this->uri=$controller_uri; if( is_array($a_uri) && count($a_uri) ){ $action=$a_uri[0]; if( is_numeric($action) ){ $this->action='show'; $this->params=$a_uri; }else{ $this->action=array_shift($a_uri); $this->params=$a_uri; } }else{ $this->action='index'; } $this->action(); } public function action( $action=null, array $params=null){ if( $action ) $this->action=$action; if( $params ) $this->params=$params; $method='action_'.$this->action; if( !\method_exists($this,$method) ){ $this->action=null; //     $method='action404'; } if( $this->before_action() ){ $this->$method(); }else{ $this->setError('e  '); } return $this; } protected function before_action(){ return true; } //      -  protected function before_view(){ } //     -  protected function action404(){ $this->setError('  '); } protected function action_index(){ throw new \Exception('<pre> index  '); } protected function action_show(){ throw new \Exception('<pre> show  '); } protected function setError( $message ){ $this->action='error'; //    error.php $this->view_data['title']=''; $this->view_data['message']=$message; } public function render(){ $this->before_view(); //  View,      ,  NameSpaceAdapter $viewClass=$this->getNameSpace().'\\View'; $view=new $viewClass(); if( is_array($this->view_data) && count($this->view_data) ){ foreach( $this->view_data as $key=>$value ){ $view->set($key,$value); } } if( $this->action ) $view->set('action',$this->action); $view->set('uri',('/'.$this->uri)); if( defined('ADMIN') ){ // admin_action.php if( $this->action ) $view->set('admin_action','admin_'.$this->action); } return $view->render(); } public function __toString(){ return $this->render(); } } 


Main class - Controller extension:
To uncover
 namespace lib\Controller; class Main extends \lib\Controller { protected $controllers=array(); //array( 'uri'=>array( 'title'=>'','class'=>''),... protected $content_controller; //   protected $auth_class='\\lib\\auth\\BasicAdmin'; // ,   function __construct(){ $this->checkAdmin(); $this->init(); $a_uri=$this->read_URI(); $this->call_controller($a_uri); } protected function read_URI(){ $arr=explode('?',$_SERVER["REQUEST_URI"]); $uri=$arr[0]; $dirt_uri=explode('/',$uri); $a_uri=array(); foreach( $dirt_uri as $i){ if( !empty($i) ) $a_uri[]=$i; } return $a_uri; } protected function call_controller($a_uri){ /*       action,  action ,           ,  ,  ,      $this->content_controller      ,    action404   $a_uri ,       ,   index */ //   , ..       action $this->action=null; $this->params=null; if( is_array($a_uri) && count($a_uri) ){ $method='action_'.$a_uri[0]; //    ,     action_show if( method_exists($this,$method) || is_numeric($a_uri[0]) ){ parent::__construct($a_uri); }else{ //  ,   $controller_uri=array_shift( $a_uri ); if( isset($this->controllers[ $controller_uri ]) ){ $this->content_controller=new $this->controllers[ $controller_uri ]['class']( $a_uri , $controller_uri ); }else{ $this->action404(); } } }else{ parent::__construct(); //   action_index } } protected function checkAdmin(){ $auth=new $this->auth_class(); if( $auth->login() ){ define('ADMIN',true); $this->prepareForAdmin(); //     } } public function render(){ /*     View,    AJAX,         AJAX    content- */ if( defined('AJAX') ){ if( $this->content_controller ){ return $this->content_controller->render(); }else{ return ' -   '; } }else{ if( $this->content_controller ) $this->view_data['content']=$this->content_controller->render(); //$this->view_data['controllers']=$this->controllers; return parent::render(); } } public function action_ajax(){ /*   uri /ajax/...   AJAX */ define('AJAX',true); $this->call_controller( $this->params); //   } public function action_login(){/*        login.php */} public function action_logout(){ $auth=new $this->auth_class(); $auth->logout(); $this->action('index'); } public function init(){ /*   */} protected function prepareForAdmin(){/*     */} } 


View class:
To uncover
 namespace lib; class View extends NameSpaceAdapter { static $public_uri; //  ,   ,     static $scripts=array(); //   ,    static $styles=array(); //   ,    static $global_data=array(); //   protected $data=array(); //  ,     protected $templates_dir='templates'; //    ,    protected $template='template.php'; //     ,    action='template' static function render_styles(){ /*   ,         \View::render_styles() */ $html=''; if( is_array(static::$styles) && count(static::$styles) ){ foreach( static::$styles as $style ){ $html.='<link href="'.static::$public_uri.'styles/'.$style.'" rel="stylesheet" type="text/css" />'."\n"; } } return $html; } static function render_scripts(){ $html=''; if( is_array(static::$scripts) && count(static::$scripts) ){ foreach( static::$scripts as $script ){ $html.='<script language="javascript" src="'.static::$public_uri.'scripts/'.$script.'"></script>'."\n"; } } return $html; } protected function getActionFile(){ $dir=$this->getDirectory().$this->templates_dir.'/'; if( defined('ADMIN') && isset($this->data['admin_action']) ){ //  admin_action.php $file=$dir.$this->data['admin_action'].'.php'; if( !file_exists($file) ) $file=$dir.$this->data['action'].'.php'; }elseif( isset($this->data['action']) ){ $file=$dir.$this->data['action'].'.php'; } if( $file && file_exists($file)){ return $file; }else{ return null; } } public function set( $name, $value=null ){ if( is_array($name) ){ foreach( $name as $key=>$value ){ $this->data[$key]=$value; } }else{ $this->data[$name]=$value; } return $this; } public function render(){ /*           (templates),       template.php,  admin_template.php    View::$global_data -    */ $dir=$this->getDirectory().$this->templates_dir.'/'; if( defined('ADMIN') ){ $template=$dir.'admin_'.$this->template; //   admin_template.php if( !file_exists($template) ) $template=$dir.$this->template; }else{ $template=$dir.$this->template; } extract( static::$global_data ); extract( $this->data, EXTR_OVERWRITE ); $public_uri=static::$public_uri; ob_start(); require ($template); return ob_get_clean(); } static function microRender( $template, $data ){ /*        ,   ""  template.php */ $dir=dirname( \Autoload::findClass(get_called_class()) ); $file=$dir.'/templates/'.$template.'.php'; if( file_exists($file) ){ if( is_array($data) ) extract($data); ob_start(); require ($file); return ob_get_clean(); }else{ return "     $file<br>"; } } public function __toString(){ return $this->view->render(); } } 



Further, a very brief implementation example.

Implementation of the main controller:
To uncover
 namespace main; class Controller extends \lib\Controller\Main { protected $controllers=array( 'pages'=>array( 'title'=>'', 'class'=>'\\pages\\Controller' ) ); protected function action_index(){ $static_page=new \static_pages\Controller(array('get_page','index')); $this->view_data['content']=$static_page->render(); } protected function action_about(){ $static_page=new \static_pages\Controller(array('get_page','about')); $this->view_data['content']=$static_page->render(); } protected function action_contacts(){ $static_page=new \static_pages\Controller(array('get_page','contacts')); $this->view_data['content']=$static_page->render(); } protected function prepareForAdmin(){ if( defined('ADMIN') ){ //    ,     uri   $this->controllers['banners']=array( 'title'=>'', 'class'=>'\\banners\\Controller' ); $this->controllers['static_pages']=array( 'title'=>' ', 'class'=>'\\static_pages\\Controller' ); } return true; } protected function before_view(){ if( !defined('AJAX') ){ /*          View */ } } } 


View main controller, about the same look View the rest of the triads
To uncover
 namespace main; class View extends \View { function __construct(){ static::$scripts['jquery']='jquery-1.9.0.js'; static::$styles['main']='main.css'; } } 


An example of the main template.php template called controller
To uncover
 <div class='pages'> <?php if( $file=$this->getActionFile() ){ require $file; }else{ echo " "; } ?> </div> 



The resulted code is part of my project "in the evenings", it is a little cut off in a habr-editor, therefore something can not work, it is not ready to spread demo - it is not present yet.

So I got what I wanted:

True, there is no model and caching, and also, you need to think very well about security.
I would like to hear the opinion of professionals about the shortcomings, and maybe not even reinvent the wheel.

Source: https://habr.com/ru/post/170739/


All Articles