PHP Classes

File: controllers/controller.url.php

Recommend this page to a friend!
  Classes of Kristo Vaher   Wave Framework   controllers/controller.url.php   Download  
File: controllers/controller.url.php
Role: Class source
Content type: text/plain
Description: URL Controller
Class: Wave Framework
MVC framework for building Web sites and APIs
Author: By
Last change: removed references to waveframework.com
Date: 14 days ago
Size: 20,447 bytes
 

Contents

Class file image Download
<?php /** * Wave Framework <http://github.com/kristovaher/Wave-Framework> * URL Controller * * Wave Framework comes with a URL Controller and a sitemap system that is used to build a * website on Wave Framework. This URL controller is entirely optional and can be removed * from a system if you plan to implement your own URL Controller or simply use Wave * Framework for API, without a website. * * @package Tools * @author Kristo Vaher <kristo@waher.net> * @copyright Copyright (c) 2012, Kristo Vaher * @license GNU Lesser General Public License Version 3 * @tutorial /doc/pages/guide_url.htm * @since 1.0.0 * @version 3.7.0 */ class WWW_controller_url extends WWW_Factory { /** * This method is called by Data Handler to find the View that is being requested based * on the URL that (usually) comes from the user agent request URL. * * @param array $input input data sent to controller * @input [url] This is URL to be solved * @return array through returnViewData method * @output [controller] controller to use for View * @output [controller-method] what controller method to use * @output [view-method] what View method to use * @output [subview] subview variable * @output [hidden] if the page should be hidden from menu lists * @output mixed might include many other View-specific variables */ public function solve($input){ // Default view is loaded from State (this is loaded when no URL is defined) $view404=$this->getState('404-view'); // Custom request URL can be used, this is required if(isset($input['url'])){ // Request string is loaded from input $request=$input['url']; } else { // Formatting and returning the expected result array return $this->returnViewData(array('view'=>$view404,'header'=>'HTTP/1.1 404 Not Found')); } // Web root is the base directory of the website $webRoot=$this->getState('url-web'); // This setting will force that even the first language (first in languages array) has to be represented in URL $enforceSlash=$this->getState('enforce-url-end-slash'); // This setting means that URL has to end with a slash $enforceLanguageUrl=$this->getState('enforce-first-language-url'); // Default language is loaded from State $language=$this->getState('language'); // List of defined languages is loaded from State $languages=$this->getState('languages'); // Default view is loaded from State (this is loaded when no URL is defined) $viewHome=$this->getState('home-view'); // By default it is assumed that home view is used $view=$viewHome; // To solve the request GET is separated from URL nodes $requestNodesRaw=explode('?',$request,2); // This array stores all the URL nodes that will be matched against sitemap $urlNodes=array(); // This is used for testing if the returned URL should be home or not $returnHome=false; // If there is no request URL set if($requestNodesRaw[0]=='' || $requestNodesRaw[0]=='/'){ // If first language code has to be defined in URL, system redirects to URL that has it, otherwise returns home view data if($enforceLanguageUrl==true){ // User agent is redirected to URL that has just the language node set if(isset($requestNodesRaw[1])){ return array('www-permanent-redirect'=>$webRoot.$language.'/?'.$requestNodesRaw[1]); } else { return array('www-permanent-redirect'=>$webRoot.$language.'/'); } } else { // Expecting to return Home view $returnHome=true; } } else { // Request is exploded into an array that will be looped to find proper view $requestNodes=explode('/',urldecode($requestNodesRaw[0])); // If slash is enforced at the end of the URL then user agent is redirected to such an URL if($enforceSlash==true && end($requestNodes)!=''){ // If GET variables were set, system redirects to proper URL that has a slash in the end and appends the GET variables if(isset($requestNodesRaw[1])){ return array('www-permanent-redirect'=>$webRoot.$requestNodesRaw[0].'/?'.$requestNodesRaw[1]); } else { return array('www-permanent-redirect'=>$webRoot.$requestNodesRaw[0].'/'); } } // Looping through all the URL nodes from the request foreach($requestNodes as $nodeKey=>$node){ // As long as the URL node in the request is not empty, it is taken into account for finding the proper view if($node!='' || !isset($requestNodes[$nodeKey+1])){ // If this is the first request node and it is found in languages array if($nodeKey==0 && in_array($node,$languages)){ // Language was found and is defined as the request language $language=$node; // If this is the first language and language node is not required in URL, user agent is redirected to a URL without it if($enforceLanguageUrl==false && $language==$languages[0]){ // We unset the first node, as it was not required unset($requestNodes[$nodeKey]); // If GET variables were set, system redirects to URL without the language and appends the GET variables if(isset($requestNodesRaw[1])){ return array('www-permanent-redirect'=>$webRoot.implode('/',$requestNodes).'?'.$requestNodesRaw[1]); } else { return array('www-permanent-redirect'=>$webRoot.implode('/',$requestNodes)); } } } else { // If language node is required in URL and the first request node was not a language, it is added and user agent is redirected if($nodeKey==0 && $enforceLanguageUrl==true){ // User agent is redirected to the same URL as before, but with the default language node added if(isset($requestNodesRaw[1])){ return array('www-permanent-redirect'=>$webRoot.$language.'/'.$requestNodesRaw[0].'?'.$requestNodesRaw[1]); } else { return array('www-permanent-redirect'=>$webRoot.$language.'/'.$requestNodesRaw[0]); } } else { // If language node is set in request, but has no second URL node set, it is also assumed that it is default view if($requestNodes[0]==$language && $nodeKey==1 && (!isset($requestNodes[1]) || $requestNodes[1]=='')){ // Expecting to return Home view $returnHome=true; } else { // Every other request node is added to the array that looks for matching URL from URL Map if($node!=''){ $urlNodes[]=$node; } } } } } else { // Formatting and returning the expected result array return $this->returnViewData(array('view'=>$view404,'language'=>$language,'header'=>'HTTP/1.1 404 Not Found')); } } } // All nodes of URL's that were not found as modules are stored here $dynamicUrl=array(); // Number of URL nodes $urlNodeCount=count($urlNodes); // URL Map is stored in this array $siteMap=$this->getSitemapRaw($language); if(!$siteMap){ // Formatting and returning the expected result array return $this->returnViewData(array('view'=>$view404,'language'=>$language,'header'=>'HTTP/1.1 404 Not Found')); } // Array that stores information from sitemap file if(isset($siteMap[$viewHome])){ $siteMapInfo=$siteMap[$viewHome]; } else { trigger_error('Configuration is incorrect, cannot find Home view sitemap data',E_USER_ERROR); } // If home is not expected to be returned if(!$returnHome){ // This will be the actual URL match $match=false; // System loops through URL nodes and attempts to find a match in URL Map foreach($siteMap as $key=>$settings){ // URL length needs to match the URL declaration in Sitemap if($urlNodeCount!=count($settings['nodes'])){ // Unsetting incompatible node unset($siteMap[$key]); } else { // Testing every URL node for($i=1;$i<=$urlNodeCount;$i++){ // This is the actual URL node value $matchKey=$i-1; // If the node is not dynamic if($settings['nodes'][$matchKey][0]!=':'){ if(!preg_match('/^'.preg_quote($settings['nodes'][$matchKey],'/').'$/ui',$urlNodes[$matchKey])){ unset($siteMap[$key]); break; } } else { // If this is set to non-false, then dynamic URL value will be added $dynamicAdd=false; // Matching the dynamic URL's $matched=explode(':',$settings['nodes'][$matchKey],3); switch($matched[1]){ case 'numeric': if($matched[2]==''){ if(!preg_match('/^[0-9]*$/i',$urlNodes[$matchKey])){ unset($siteMap[$key]); break; } else { $dynamicAdd=$urlNodes[$matchKey]; } } else { // Finding the match parameters $parameters=explode('-',$matched[2]); if(!preg_match('/^[0-9\-\_]*$/i',$urlNodes[$matchKey]) || ($parameters[0]!='*' && intval($urlNodes[$matchKey])<$parameters[0]) || ($parameters[1]!='*' && intval($urlNodes[$matchKey])>$parameters[1])){ break; } else { $dynamicAdd=$urlNodes[$matchKey]; } } break; case 'alpha': if($matched[2]==''){ if(!preg_match('/^[[:alpha:]\-\_]*$/ui',$urlNodes[$matchKey])){ unset($siteMap[$key]); break; } else { $dynamicAdd=$urlNodes[$matchKey]; } } else { // Finding the match parameters $parameters=explode('-',$matched[2]); if(!preg_match('/^[[:alpha:]\-\_]*$/ui',$urlNodes[$matchKey]) || ($parameters[0]!='*' && strlen($urlNodes[$matchKey])<$parameters[0]) || ($parameters[1]!='*' && strlen($urlNodes[$matchKey])>$parameters[1])){ break; } else { $dynamicAdd=$urlNodes[$matchKey]; } } break; case 'alphanumeric': if($matched[2]==''){ if(!preg_match('/^[[:alnum:]\-\_]*$/ui',$urlNodes[$matchKey])){ unset($siteMap[$key]); break; } else { $dynamicAdd=$urlNodes[$matchKey]; } } else { // Finding the match parameters $parameters=explode('-',$matched[2]); if(!preg_match('/^[[:alnum:]\-\_]*$/ui',$urlNodes[$matchKey]) || ($parameters[0]!='*' && strlen($urlNodes[$matchKey])<$parameters[0]) || ($parameters[1]!='*' && strlen($urlNodes[$matchKey])>$parameters[1])){ break; } else { $dynamicAdd=$urlNodes[$matchKey]; } } break; case 'fixed': if($matched[2]!=''){ // Finding the match parameters $matches=explode(',',$matched[2]); if(!in_array($urlNodes[$matchKey],$matches)){ unset($siteMap[$key]); break; } else { $dynamicAdd=$urlNodes[$matchKey]; } } else { unset($siteMap[$key]); } break; case 'any': // Any character is accepted if($matched[2]!=''){ // In case the regular expression includes a colon symbol if(isset($matched[3])){ $matched[2].=$matched[3]; } // Testing for regular expression match if(!preg_match($matched[2],$urlNodes[$matchKey])){ unset($siteMap[$key]); break; } else { $dynamicAdd=$urlNodes[$matchKey]; } } else { $dynamicAdd=$urlNodes[$matchKey]; } break; } // If a new dynamic node was found if($dynamicAdd!==false){ $siteMap[$key]['dynamic-url'][$matchKey]=$dynamicAdd; } else { break; } } // If the cycle has not broken, then match has been found if($i==$urlNodeCount){ // If user agent setting is set then this is checked, otherwise match is found if(isset($siteMap[$key]['user-agent'])){ if(!preg_match($siteMap[$key]['user-agent'],$this->getState('client-user-agent'))){ unset($siteMap[$key]); break; } else { $match=$siteMap[$key]; } } else { $match=$siteMap[$key]; } break; } } } // Match has been found if($match){ break; } } // If all URL nodes have been matched and there's still a URL in the Sitemap array if($match){ // Getting sitemap info from matched sitemap node $siteMapInfo=$match; // Resetting the indexes in the dynamic URL array if(isset($siteMapInfo['dynamic-url'])){ $siteMapInfo['dynamic-url']=array_values($siteMapInfo['dynamic-url']); } // If sitemap has defined the view if(isset($siteMapInfo['view'])){ $view=$siteMapInfo['view']; } else { $view=''; } } else { // Formatting and returning the expected result array return $this->returnViewData(array('view'=>$view404,'language'=>$language,'header'=>'HTTP/1.1 404 Not Found')); } // If the found view is home view, then we simply redirect to home view without the long url if(empty($siteMapInfo['dynamic-url']) && $view==$viewHome){ // If first language is used and it is not needed to use language URL in first language if($enforceLanguageUrl==false && $language==$languages[0]){ // If request nodes are set in the URL if(isset($requestNodesRaw[1])){ return array('www-permanent-redirect'=>$webRoot.'?'.$requestNodesRaw[1]); } else { return array('www-permanent-redirect'=>$webRoot); } } else { // If request nodes are set in the URL if(isset($requestNodesRaw[1])){ return array('www-permanent-redirect'=>$webRoot.$language.'/?'.$requestNodesRaw[1]); } else { return array('www-permanent-redirect'=>$webRoot.$language.'/'); } } } } // Populating sitemap info with additional details $siteMapInfo['request-url']='/'.$requestNodesRaw[0]; $siteMapInfo['url-base']=$this->getState('url-base'); $siteMapInfo['url-web']=$webRoot; $siteMapInfo['url-absolute']=$siteMapInfo['url-base'].$requestNodesRaw[0]; $siteMapInfo['url-relative']=$webRoot.$requestNodesRaw[0]; if(isset($requestNodesRaw[1])){ $siteMapInfo['request-parameters']=$requestNodesRaw[1]; } else { $siteMapInfo['request-parameters']=''; } $siteMapInfo['language']=$language; // It is possible to assign temporary or permanent redirection in Sitemap, causing 302 or 301 redirect if(isset($siteMapInfo['temporary-redirect']) && $siteMapInfo['temporary-redirect']!=''){ // Dynamic parts of the URL can also be redirected if(!empty($siteMapInfo['dynamic-url'])){ foreach($siteMapInfo['dynamic-url'] as $key=>$bit){ $siteMapInfo['temporary-redirect']=str_replace(':'.$key.':',$bit,$siteMapInfo['temporary-redirect']); } } // Query string is also sent, if it has been defined if(isset($requestNodesRaw[1]) && strpos($siteMapInfo['temporary-redirect'],'?')===false){ return array('www-temporary-redirect'=>$siteMapInfo['temporary-redirect'].'?'.$requestNodesRaw[1]); } else { return array('www-temporary-redirect'=>$siteMapInfo['temporary-redirect']); } } elseif(isset($siteMapInfo['permanent-redirect']) && $siteMapInfo['permanent-redirect']!=''){ // Dynamic parts of the URL can also be redirected if(!empty($siteMapInfo['dynamic-url'])){ foreach($siteMapInfo['dynamic-url'] as $key=>$bit){ $siteMapInfo['permanent-redirect']=str_replace(':'.$key.':',$bit,$siteMapInfo['permanent-redirect']); } } // Query string is also sent, if it has been defined if(isset($requestNodesRaw[1]) && strpos($siteMapInfo['permanent-redirect'],'?')===false){ return array('www-permanent-redirect'=>$siteMapInfo['permanent-redirect'].'?'.$requestNodesRaw[1]); } else { return array('www-permanent-redirect'=>$siteMapInfo['permanent-redirect']); } } // Returning custom data if no view setting was declared in sitemap URL file if(!isset($siteMapInfo['view']) && !isset($siteMapInfo['return-type'])){ return $this->returnViewData(array('view'=>$view404,'language'=>$language,'header'=>'HTTP/1.1 404 Not Found')); } // Formatting and returning the expected result array return $this->returnViewData($siteMapInfo); } /** * This function formats and returns View data for View Controller * * @param array $data data from Sitemap * @return array */ private function returnViewData($data){ // VIEW DEFAULTS $data+=array( 'controller'=>'view', 'controller-method'=>'load', 'view-method'=>'render', 'subview'=>'', 'hidden'=>0, 'return-type'=>'html' ); // DEFAULTS FOR VIEW DATA // If dynamic URL is assigned as part of cache tag if(isset($data['cache-tag'],$data['cache-tag-dynamic']) && $data['cache-tag-dynamic']==1 && !empty($data['dynamic-url'])){ $data['cache-tag'].='-'.implode('-',$data['dynamic-url']); } // If project title is not set by Sitemap, system defines the State project title as the value if(!isset($data['project-title'])){ $data['project-title']=$this->getState('project-title'); } // Robots data is also returned to views if(!isset($data['robots'])){ $data['robots']=$this->getState('robots'); } // HEADERS // These headers will be set by API $data['www-set-header']=array(); if(isset($data['header'])){ $data['www-set-header'][]=$data['header']; } // Writing robots data to header if($data['robots']!=''){ // This header is not 'official HTTP header', but is widely supported by Google and others $data['www-set-header'][]='Robots-Tag: '.$data['robots']; } // Robots data is also returned to views if(isset($data['frame-permissions']) && $data['frame-permissions']){ $data['www-set-header'][]='Frame-Options: '.$data['frame-permissions']; } elseif($tmp=$this->getState('frame-permissions')){ $data['www-set-header'][]='Frame-Options: '.$tmp; } // Content security policy headers if(isset($data['content-security-policy']) && $data['content-security-policy']){ $data['www-set-header'][]='Content-Security-Policy: '.$data['content-security-policy']; } elseif($tmp=$this->getState('content-security-policy')){ $data['www-set-header'][]='Content-Security-Policy: '.$tmp; } // USER PERMISSIONS CHECKS // This is the best place to build your authentication module for web views // But the commands that it uses are not shown here, so it is commented out // This is essentially the boilerplate startpoint for you to implement authentication, as the actual login redirection is turned off // Attempting to get user session // if(isset($data['permissions'])){ // Permissions are exploded into an array from comma separated string in sitemap file // $data['permissions']=explode(',',$data['permissions']); // This method automatically removes all empty entries from the array // $data['permissions']=array_filter($data['permissions']); // This flag, if changed, will redirect user to log-in screen // $failed=false; // Testing if user session exists // $user=$this->getUser(); // if($user){ // Double-checking user account validity from database // $userData=$this->dbSingle('SELECT * FROM users WHERE id=? AND deleted=0',array($user['id'])); // if($userData){ // Updating user session data from database (good if it has changed) // $this->setUser($userData); // Setting user permissions based on most recent information from database // $this->setPermissions(explode(',',$userData['permissions'])); // Testing if permissions are included // if(!empty($data['permissions']) && !$this->checkPermissions($data['permissions'])){ // $failed=true; // } // } else { // $failed=true; // } // } else { // $failed=true; // } // if($failed){ // $siteMapReference=$this->getSitemap(); // Success array is used since technically URL solving has been a 'success' // return $this->resultFalse('Authentication required',array('www-temporary-redirect'=>$siteMapReference['login']['url'])); // } // } // Data about the view is returned return $data; } } ?>