src/Application.php
1b7661ed
 <?php
 
 namespace elanpl\L3;
 
 class Application{
c6173b1c
     public $config; // L3 Config object
     public $request; // build from received HTTP request or created in L3 Config object
     public $response; // response to be send
     public $router; // routing engine
     public $serialization; // serialization engine
cdfe8eaf
     public $viewEngines; // view engines configuration
9b815c0a
     public $baseDirectory; // directory root of the application
0621d624
     public $controllersDirectory; // subdirectory/namespace part added to controller class 
     public $viewsDirectory; // subdirectory to search for the view files
c6173b1c
     public $applicationDirectory; // subdirectory/namespace part of the application
840fc5a6
     public $controller; //Active controller
     public $module; //Active module
     public $action; //Active action
1b7661ed
 
a2bd90a3
     public function __construct($config){
         //set L3 application config
c6173b1c
         $this->config = $config;
02246d64
         // directory root of the application
9b815c0a
         $this->baseDirectory = $config->getBaseDirectory();
02246d64
         // subdirectory/namespace part added to controller class
0621d624
         $this->controllersDirectory = $config->getControllersDirectory();
02246d64
         // subdirectory to search for the view files 
0621d624
         $this->viewsDirectory = $config->getViewsDirectory();
02246d64
         // subdirectory/namespace part of the application
c6173b1c
         $this->applicationDirectory = $config->getApplicationDirectory();
1b7661ed
         //create request object
c6173b1c
         $this->request = $this->config->getRequest();
a2bd90a3
         //create router object
c6173b1c
         $this->router = new Router($this->config->getRouting());
1b7661ed
         //create response objcet
c6173b1c
         $this->response = new Response();
a2bd90a3
 
1b7661ed
         //create serialization object
c6173b1c
         $this->serialization = new Serialization(); 
cdfe8eaf
         //create view engines configuretion object
         $this->viewEngines = new ViewEngine();
 
         //perform configuration operations
         $this->config->configure($this);
1b7661ed
     }
 
     public function basePath(){
223a036e
         if(isset($this->request->path)&&$this->request->path!=''){
51c015e5
             $base = explode('%23',explode('?',$_SERVER['REQUEST_URI'],2)[0],2)[0];
             if (substr($base,-strlen($this->request->path))===$this->request->path) $base = substr($base, 0, strlen($base)-strlen($this->request->path));
             return $base;
c60962c6
         }
8a764473
         else
0b4ba5a8
             return explode('%23',explode('?',$_SERVER['REQUEST_URI'],2)[0],2)[0];
1b7661ed
     }
 
b0b06371
     public function getServerURL(){
         return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://{$_SERVER['HTTP_HOST']}";
     }
 
     public function getURL(){
         return $this->getServerURL().$_SERVER['REQUEST_URI'];
     }
 
1b7661ed
     public function relPath($file){
c6173b1c
         return ($this->router->depth>0 ? str_repeat("../", substr($this->request->path,-1)=='/'?$this->router->depth:$this->router->depth-1) : "" ).$file; 
1b7661ed
     }
 
     public function link($name, $parameters){
c6173b1c
         return $this->router->link($name, $parameters);
1b7661ed
     }
 
     /*
     //not need - PSR-4 autoload handles that
     function IncludeControler($Controller){
         include_once(_L3_CONTROLLER_PATH.$Controller.'.php');
     }*/
 
c6173b1c
     public function findControllerInCallStack($search_level=5){
1b7661ed
         //search for controller, action and module if present
         $controller = "";
         $module = "";
         $action = "";
         $match = array();
 
         //analize the call stack
           $call_stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, $search_level);
         
         for($i=0; $i<$search_level; $i++){
             if(preg_match(
d7ac946b
                     $pattern = '#('.$this->applicationDirectory.')(\\\\(?P<module>.+))?\\\\'.$this->controllersDirectory.'\\\\(?P<controller>.+)#',
1b7661ed
                     $call_stack[$i]['class'],
                     $match
                 )
             ){
                 $action = $call_stack[$i]['function'];
                 $module = $match['module'];
                 $controller = $match['controller'];
                 break;
             }
         }
 
         return array(
             'module' => $module,
             'controller' => $controller,
             'action' => $action
         );
     }
 
c6173b1c
     function runControllerAction($controllerAction, $parameters){
1b7661ed
         
         //prepare object and action name
         /*
         TODO ...
         if(is_object($this->controler) && (is_a($this->controler,'L2_controler') || is_subclass_of($this->controler,'L2_controler'))){
             if(is_string($this->action)){
                 $controler_object = $this->controler;
                 $controler_action = $this->action;
             }
         }
         else if(is_string($this->controler)){
             if(is_string($this->action)){
                 $this->includeControler($this->controler);
                 $controler_object = new $this->controler();
                 $controler_action = $this->action;
             }
         }*/
 
fae0882f
         $field_pattern ='#^{(?<field>[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)}$#';
931621db
 
c6173b1c
         if(is_array($controllerAction)){
1b7661ed
             //$this->IncludeControler($ControllerAction['controller']);
931621db
             if(isset($controllerAction['module'])){
                 if(\preg_match($field_pattern, $controllerAction['module'], $match) ){
e71573cb
                     $module = $this->router->parsedParameters[$match['field']]."\\";
931621db
                 }
                 else{
                     $module = $controllerAction['module']."\\";
                 }
             }
             else{
                 $module = "";
             }
 
             
             if(\preg_match($field_pattern, $controllerAction['controller'], $match) ){
                 $controller = $this->router->parsedParameters[$match['field']];
             }
             else{
                 $controller = $controllerAction['controller'];
             }
             
 
c6173b1c
             $controller_class = "\\".$this->applicationDirectory."\\"
931621db
                 .$module
d7ac946b
                 .$this->controllersDirectory."\\"
931621db
                 .$controller;
d32f9a3a
             
             if(class_exists($controller_class,true)){
6f062c47
                 $controller_object = new $controller_class($this);
af738874
             }
d32f9a3a
             else{
                 return $this->handle404($this->request);
af738874
             }
931621db
 
             if(\preg_match($field_pattern, $controllerAction['action'], $match) ){
                 $controller_action = $this->router->parsedParameters[$match['field']];
             }
             else{
                 $controller_action = $controllerAction['action'];
             }
             
1b7661ed
         }
 
         //fire the action
         if(isset($controller_object) && isset($controller_action)){
931621db
             
             // method not found - 404
             if(!method_exists($controller_object,$controller_action)){
6f062c47
                 return $this->handle404($this->request);
931621db
             }
 
a364859c
             $this->module = trim($module, '\\');
840fc5a6
             $this->controller = $controller;
c6173b1c
             $this->action = $controller_action;
1b7661ed
             // prepare args and start the action
             if(!isset($reflection)){
                 $reflection = new \ReflectionMethod($controller_object, $controller_action);
             }
             if($reflection->getNumberOfParameters()){
                 $fire_args=array();
 
                 foreach($reflection->getParameters() AS $arg)
                 {
c6173b1c
                     if(isset($parameters[$arg->name]) || (is_array($controllerAction) && isset($controllerAction['defaults'][$arg->name])))
                         if(isset($parameters[$arg->name]))
                             $fire_args[$arg->name]=$parameters[$arg->name];
1b7661ed
                         else
c6173b1c
                             $fire_args[$arg->name]=$controllerAction['defaults'][$arg->name];
1b7661ed
                     else
                     {
                         if($arg->isDefaultValueAvailable()){
                             $fire_args[$arg->name]=$arg->getDefaultValue();
                         }
                         else{
                             $fire_args[$arg->name]=null;
                         }
                     }
                 }
                 return $reflection->invokeArgs ($controller_object , $fire_args );
             }
             else{
                 return $reflection->invoke($controller_object);
             }
         }
     }
 
c6173b1c
     public function handleActionResult($result){
1b7661ed
         if(is_string($result)){
             $content = $result;
         }
         else if(is_object($result)){
             if($result instanceof ViewModel){
c6173b1c
                 if($serializationContentType = $this->serialization->match($this->request->acceptTypes, $result)){
                     $content = $this->serialization->serialize($serializationContentType, $result);
1b7661ed
                     if(is_object($content)){
c6173b1c
                         if($content instanceof response){
                             $this->response = $content;
1b7661ed
                             unset($content);
                         }
                     }
                 }
                 else{
c6173b1c
                     echo "Serializer not defined for Content-Type: ".$this->request->accept."<br>".PHP_EOL;
1b7661ed
                     throw new \Exception("Not implemented!");
                 }
             }
             else if($result instanceof View){
                 $content = $result->render();
             }
             else if($result instanceof Response){
c6173b1c
                 $this->response = $result;
1b7661ed
             }  
         }
         if(isset($content))
c6173b1c
                 $this->response->withBody($content);
         $this->response->send();
1b7661ed
     }
 
931621db
     public function handle404($request){
9317964e
         if(!$this->router->is_Match404() && ($action = $this->router->setMatch404($request))){
2eec4418
             $this->runAction($action);
         }
         else{
9317964e
             http_response_code(404);
             echo "<h1>Resource not found!</h1><br>".PHP_EOL;
2eec4418
             echo "<pre>";
             var_dump($request);
             echo "</pre>";
9317964e
             if($this->router->is_Match404())
5541c781
                 throw new \Exception("The 404 controller or action not found!");
             else
                 throw new \Exception("The 404 (not found) route not set!");
2eec4418
         }
931621db
     }
 
c6173b1c
     public function beforeAction(){
         if(!empty($this->router->routeInfo->beforeAction)){
             foreach($this->router->routeInfo->beforeAction as $event_handler){
                 $this->router->routeInfo::eventHandlerFormatCheck($event_handler, $eh);
1b7661ed
                 if(isset($eh['class'])){
                     $object = new $eh['class'];
                     $reflection = new \ReflectionMethod($object, $eh['function']);
                     $fire_args = array();
bd88f5e2
                     $fire_args[] = $this;
1b7661ed
                     if(isset($eh['arguments'])){
c6173b1c
                         $this->router->routeInfo::eventHandlerArgumentsFormatCheck($eh['arguments'], $args);
23d70f60
                         $field_pattern ='#^{(?<field>[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)}$#';
                         foreach($args as $index => $param){
88bf2f6f
                             if(\preg_match($field_pattern, $param, $match) ){
28bbf530
                                 $args[$index] = $this->router->parsedParameters[$match['field']];
23d70f60
                             }
                         }
1b7661ed
                         $fire_args = array_merge($fire_args, $args);
                     }
                     $result = $reflection->invokeArgs ($object , $fire_args );
                 }
                 else if (is_callable($eh['function'])){
                     throw new \Exception("Not implemented!");
                 }
                 if(isset($result)){
                     if(is_object($result)){
                         if($result instanceof Response){
c6173b1c
                             $this->handleActionResult($result);
1b7661ed
                             exit();
                         }
                     }
                 }
             }
         }
     }
 
2eec4418
     public function runAction($action){
         // routing match found decide what to do next...
         // BeforeAction event
         $this->beforeAction();
1b7661ed
 
2eec4418
         // function -> call it...
         if(is_callable($action)){
             $reflection = new \ReflectionFunction($action);
             if($reflection->getNumberOfParameters()){
                 $fire_args=array();
     
                 foreach($reflection->getParameters() AS $arg)
                 {
                     if(isset($this->router->parsedParameters[$arg->name]))
                         $fire_args[$arg->name]=$this->router->parsedParameters[$arg->name];
                     else
                         $fire_args[$arg->name]=null;
1b7661ed
                 }
                 
2eec4418
                 $result = call_user_func_array($action, $fire_args);
1b7661ed
             }
2eec4418
             else{
                 $result = call_user_func($action);
1b7661ed
             }
2eec4418
         }
         else if(is_object($action) && is_a($action, 'L2_controler_info')){
             // not implemented yet.
             throw new \Exception("Not implemented!");
             ///$action->runControlerAction($this->routing->param);
         }
         // array with controller, and action -> run the controller action
         else if(is_array($action) && array_key_exists('controller',$action) && array_key_exists('action', $action)){
             $result = $this->runControllerAction($action, $this->router->parsedParameters);
             
         }
         // string
         else if(is_string($action)){
             $result = $action;
         }
1b7661ed
 
2eec4418
         // Handle the action result
         if(isset($result)){
             $this->handleActionResult($result);
         }
     }
 
     public function run(){
         // Check if routing path is found
         if($action = $this->router->match($this->request)){
             $this->runAction($action);
1b7661ed
         }
         // routing path not found -> generate 404 response
         else{
931621db
             $this->handle404($this->request);
1b7661ed
         }
  
     }
 }