How to Read/Render URLs Like Zend Framework - php

I am working on creating my own framework in php.
Everything is going as planned except working the framework according to the URL.
I cannot understand this:
Normal URL loading from www.mydomain.com/folderone/foldertwo/index.php
Zend Framework URL loading from the same URL would be
www.mydomain.com/folderone(controller)/folder2(action)/variables
how can i create that logic?
What am i missing?
I am really dedicated to create this framework.

I had the same task as I setup my framework. This is a solution work for me.
Create first your .htaccess file. Set up your rewrite conditions and exclude your template path. You can do it like that (just copy & paste):
RewriteEngine On
Options +Indexes
Options +FollowSymLinks
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}/index\.php -f
RewriteCond %{DOCUMENT_ROOT}/template -d
RewriteRule ^(.*)$ index\.php?$1 [QSA]
Before I go ahead, we have to create five directories at least:
/var/www/models/
/var/www/controllers/
/var/www/classes/
/var/www/system/
/var/www/template/
Now, I added an auto loading in my index.php:
<?php
error_reporting(E_ALL ^ E_NOTICE);
session_start();
function autoload($class_name)
{
$autoloadDirs = array('models', 'classes', 'controllers');
foreach($autoloadDirs as $dir)
{
if(file_exists($dir.'/'.$class_name.'.php'))
{
require_once($dir.'/'.$class_name.'.php');
}
}
}
spl_autoload_register('autoload');
require_once('system/Calling.php');
Calling::Run();
?>
At the script above you'll see that require_once within Calling.php
class Calling
{
public static function Run($querystring = null)
{
//1. Parameter = Contollername
//2. Parameter = Action
$qString = preg_replace('/(\/$|^\/)/','',$querystring === null ? $_SERVER['QUERY_STRING'] : $querystring);
$callParam = !empty($qString) ? explode('/', $qString) : array();
$controllerName = count($callParam) > 0 ? (class_exists(ucfirst($callParam[0]).'Controller') ? ucfirst(array_shift($callParam)) : 'Error') : 'Main';
//All controllers have suffix "Controller" -> NameController.php
//and class name ike NameController
//If no controller name given, use MainController.php
$controllerClassName = $controllerName.'Controller';
//All public methods have suffix "Action" -> myMethodAction
//If there is no method named, use DefaultAction
$actionName = count($callParam) > 0 && method_exists($controllerClassName, ucfirst($callParam[0]).'Action') ? ucfirst(array_shift($callParam)) : 'Default';
$actionFunctionName = $actionName.'Action';
//Fetch the params
$param = new stdClass();
for($i = 0; $i < count($callParam); $i += 2)
{
$param->{$callParam[$i]} = isset($callParam[$i + 1]) ? $callParam[$i+1] : null;
}
////////////////////////////////////////////////////////////
//Init the Controller
$controller = new $controllerClassName($controllerName, $actionName);
$controller->$actionFunctionName($param);
////////////////////////////////////////////////////////////
//If you adapt this code: Is up to you to extends your controller
//from an internal controller which has the method Display();
$controller->Display();
}
}
Further, in your controller directory add your first controller namend MainController.php
//--> just better if you have also an internal controller with your global stuff
//--> class MainController extends Controller
class MainController
{
/** This is the default action
* #param $params
* #access public
* #return
*/
public function DefaultAction(stdClass $params)
{
//-> Do your staff here
}
/** This is the second action
* #param $params
* #access public
* #return
*/
public function SecondAction(stdClass $params)
{
//-> Do your staff here
}
/** This is the view handling method which has to run at least
* and I recommend to set up an internal controller and to extend
* all other controller from it and include this method in your
* internal controller
* #param
* #access
* #return
*/
public function Display()
{
//-> Run your template here
}
?>
Now, you can call the controller, methods and params like that:
//-> Load the main controller with default action
www.example.com/
//-> Load the main controller with default action
www.example.com/main/default/
//-> Load the main controller with second action
www.example.com/main/second/
//-> Load the main controller with second action and gives two params
www.example.com/main/second/key1/value1/key2/value2/
And now you'll have the following files and directories to start up your own framework.
/var/www/.htaccess
/var/www/index.php
/var/www/controllers/MainController.php
/var/www/system/Calling.php
/var/www/models/
/var/www/classes/
/var/www/template/
Enjoy your new basic framework kit

Zend FrameWork and most MVC frameworks use a BootStrap.
It routes the URL (using .htaccess) to one file let's say (index.php) by using something like that:
RewriteEngine on
RewriteRule !\.(js|ico|gif|jpg|png|css|html)$ index.php
Which loads the bootstrap and the routing class that takes whatever data it needs from the URL.
According to ZEND manual:
Routing is the process of taking a URI endpoint (that part of the URI
which comes after the base URL) and decomposing it into parameters to
determine which module, controller, and action of that controller
should receive the request. This values of the module, controller,
action and other parameters.
Routing occurs only once: when
the request is initially received and before the first controller is
dispatched.
EDIT: Answer for your comment:
Far away from ZEND FRAMEWORK, that's a code to test in a new PHP file:
<?php
$url = 'http://test.com/test1/test2/news.php';
$parse = parse_url($url);
$tokens = explode("/", $parse[path]);
print_r($tokens);
?>

If you want to build a router, you could look at Glue PHP for giving you examples.
It's a micro-framework that just do the routing part.

Related

How To Use Multiple Cache Folder In CodeIgniter

I have a multiple language website I want to create a specific cache folder for every language. How Can I do this?
I currently using one cache folder with this code.
Can You Help Me?
$lang = $CI->session->userdata('language');
$cache_path .= md5($uri).'-'.$lang;
the parameter that configures the cache folder is in config.php and its called cache_path. For modifying in during runtime, we can use the $this->config->set_item function. Obviously, the cache folder switch should be done as early as possible in the controller function, before the caching function call.
Here is a sample implementation, controller Test:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Test extends CI_Controller {
/**
* Index Page for this controller.
*
* Maps to the following URL
* http://example.com/index.php/welcome
* - or -
* http://example.com/index.php/welcome/index
* - or -
* Since this controller is set as the default controller in
* config/routes.php, it's displayed at http://example.com/
*
* So any other public methods not prefixed with an underscore will
* map to /index.php/welcome/<method_name>
* #see https://codeigniter.com/user_guide/general/urls.html
*/
public function index()
{
$user_language = 'french';
$this->config->set_item('cache_path', 'IS_ROOT/cache/' . $user_language . '/');
$this->output->cache(1);
$this->load->view('welcome_message');
}
}
when you run it, you can see in the logs, that the directory "french" under the "global" IS_ROOT/cache was used:
INFO - 2018-06-09 22:12:39 --> File loaded: /php_basedir/CodeIgniter_3_1_8/application/views/welcome_message.php
DEBUG - 2018-06-09 22:12:39 --> Cache file written: IS_ROOT/cache/french/bc3ad60292ed776397da07cac67ddd28
INFO - 2018-06-09 22:12:39 --> Final output sent to browser
DEBUG - 2018-06-09 22:12:39 --> Total execution time: 0.0138
hope it helps
I Find a solution. we must edit output.php in core folder like this.
public function _write_cache($output){
$CI =& get_instance();
$lang = $CI->session->userdata('language');
$path = $CI->config->item('cache_path');
$cache_path = ($path === '') ? APPPATH.'cache_'.$lang.'/' : $path;
//other code
}
public function _display_cache(&$CFG, &$URI){
$CI =& get_instance();
$lang = $CI->session->userdata('language');
$cache_path = ($CFG->item('cache_path') === '') ? APPPATH.'cache_'.$lang.'/' : $CFG->item('cache_path');
//other code
}

Is there a way in Laravel for all links/redirects to be with trailing slash?

I'm redesigning old website which is built with custom code, and now we are using Laravel framework (version 5.3). The problem is that old website has all links with trailing slashes. It has great SEO and many visits from search engines so removing trailing slashes is not the option.
In my routes/web.php file i have following routes:
$router->get('texts/', 'TextController#index');
$router->get('texts/{slug}/', 'TextController#category');
$router->post('texts/search/', 'TextController#searchPost');
$router->get('texts/search/', 'TextController#search');
Showing links on html/blade views with trailing slashes is not the problem, problem is redirecting to route links.
App/Http/Controllers/TextController.php
public function searchPost()
{
...
return $this->response->redirectToAction('TextController#search');
}
This redirect me to "texts/search" instead on "texts/search/". Is there any options to turn on/off trailing slashes in Laravel or some hacky way to fix this ? .htaccess redirect is not the solution because it adds one more redirect and slows down website.
Figured it out, I needed to extend UrlGenerator class.
Created TrailingSlashUrlGenerator.php inside App/Library folder:
namespace App\Library;
use Illuminate\Routing\UrlGenerator;
class TrailingSlashUrlGenerator extends UrlGenerator
{
/**
* Format the given URL segments into a single URL.
*
* #param string $root
* #param string $path
* #param string $tail
* #return string
*/
protected function trimUrl($root, $path, $tail = '')
{
return parent::trimUrl($root, $path, $tail).'/';
}
}
Create RoutingServiceProvider in App/Providers folder:
public function register()
{
$this->app['url'] = $this->app->share(function($app) {
$routes = $app['router']->getRoutes();
$app->instance('routes', $routes);
$url = new TrailingSlashUrlGenerator(
$routes, $app->rebinding('request', $this->requestRebinder())
);
$url->setSessionResolver(function ($app) {
return $app['session'];
});
return $url;
});
}
Register provider in config/app.php:
App\Providers\RoutingServiceProvider::class,

How to set default controller inside n subfolder in codeigniter 3

-Myproject
-application
-controllers
-subfolder1
-subfolder2
-subfolder3
-subfolder(..n)
Controller.php
And need to set routes.php
$route['default_controller'] = 'subfolder1/subfolder2/subfolder3/subfolder(..n)/controller';
According to the examples in the docs it does not appear that you can put a Controller more than one level deep inside a subdirectory.
example.com/index.php/subdirectory/controller/function
I also don't think your route looks correct. You would not have home/ at the start of the route unless "home" is a controller or subdirectory name. See examples here.
$route['default_controller'] = 'subdirectory/controller';
I have researched and found in codeigniter 3 system/core/Router.php library file's method _set_default_controller() can not support default controller in subfolder. So i have overrided/ customized this method _set_default_controller() and now it supports n level subfolders and working fine for me.
I have created application/core/MY_Router.php with the below code to override this method _set_default_controller()
<?php
/**
* Override Set default controller
*
* #author : amit
*
* #return void
*/
protected function _set_default_controller()
{
if (empty($this->default_controller))
{
show_error('Unable to determine what should be displayed. A default route has not been specified in the routing file.');
}
// Is the method being specified?
$x = explode('/', $this->default_controller);
$dir = APPPATH.'controllers'; // set the controllers directrory path
$dir_arr = array();
foreach($x as $key => $val){
if(!is_dir($dir.'/'.$val)){
// find out class i.e. controller
if(file_exists($dir.'/'.ucfirst($val).'.php')){
$class = $val;
if(array_key_exists(($key+1), $x)){
$method = $x[$key+1]; // find out method i.e. action
}else{
$method = 'index'; // default method i.e. action
}
}else{
// show error message if the specified controller not found
show_error('Not found specified default controller : '. $this->default_controller);
}
break;
}
$dir_arr[] = $val;
$dir = $dir.'/'.$val;
}
//set directory
$this->set_directory(implode('/', $dir_arr));
$this->set_class($class);
$this->set_method($method);
// Assign routed segments, index starting from 1
$this->uri->rsegments = array(
1 => $class,
2 => $method
);
log_message('debug', 'No URI present. Default controller set.');
}
?>
Now we can set default controller in application/config/routes.php like below
// without action method name
$route['default_controller'] = 'subfoler1/subfoler2/subfolder3/subfolder(..n)/controller';
OR
// with action method name
$route['default_controller'] = 'subfoler1/subfoler2/subfolder3/subfolder(..n)/controller/action';
If we will pass the action method name it will detect that method as action and if we will not pass action name then it will assume index as a action.

could not unset url after display the MVC page

i am new to MVC i have created a routing class below, it is working fine but when i go to the index page the navigation anchor href are correct. but when i move to other controller the url first string which is url-0 , is still the previous controller, which change all navigation href address base+previous controller, for e.g if i am on indexController/index which will display all the pages froom database. and when i want to call logincontroller through navigation anchor the logincontroller href change it becomes indecontroller/loginController/login. the correct login href address is loginController/login. my htaccess and routing class is below.and folder structure.
mvc app controllers indexcontroller.php userController.php
model user.php page.php
lib
core App.php Controller.php
includes navigation.php
style style.css
images
js javscript
index.php
I hope some one can help me, i tried but so success yet Please Help Thanks in advance.
RewriteEngine On
RewriteBase /socialNetwork
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^([a-zA-Z0-9\/\-_]+)\.?([a-zA-Z]+)?$ index.php?url=$1&extension=$2 [QSA,L]
class App
{
protected $controller = 'indexController';
protected $method = 'index';
protected $params = array();
public function __construct()
{
$url = $this->parseUrl();
//print_r($url);
if (isset($url[0]))
{
if (file_exists('app/controllers/'.$url[0].'.php'))
{
//$url = ('../app/controllers/'.$url[0].'.php');
$this->controller = $url[0];
//echo ($this->controller);
unset($url[0]);
}
}
require_once('app/controllers/'.$this->controller.'.php');
$this->controller = new $this->controller;
if (isset($url[1]))
{
if (method_exists($this->controller,$url[1]))
{
$this->method = $url[1];
unset($url[1]);
}
}
$this->params = $url ? array_values($url) : array();
call_user_func_array(array($this->controller,$this->method),$this->params);
}
public function parseUrl()
{
if (isset($_GET['url']))
{
return $url =explode('/',filter_var(rtrim($_GET['url'],'/'),FILTER_SANITIZE_URL));
}
}
}
this is my index page.
<header>
<img width="960" height="100" src="http://localhost/socialNetwork/images/bgheader.png">
</header>
<?php
include_once("include/navigation.php");?>
<section class="content">
<?php
require_once('app/init.php');
$app = new App;
?>
</section>
You should not have relative url's in your view files (html-files). Check out some common MVC-like PHP frameworks like Laravel or Symfony. They all have methods which provides you the full qualified url for you vew-files like: http://domain.tld/controller/param/... instead of /cobntroller/..
Implementing this should solve your problem.
For example check out Laravel. It's very easy to understand and pretty flexible. There you have access to all your route with functions like Redirect::route('your-route-name'). The routing engine will then translate this into the full url!
EDIT: Just to be more precise, laravel is not a real mvc by definition. But in some parts it acts like a mvc.
Hope this helps

Friendly URL's with an IndexController

My current router / FrontController is setup to dissect URL's in the format:
http://localhost/controller/method/arg1/arg2/etc...
However, I'm not sure how to get certain requests to default to the IndexController so that I can type:
http://localhost/contact
or
http://localhost/about/portfolio
Instead of:
http://localhost/index/contact
or
http://localhost/index/about/portfolio
How is this accomplished?
<?php
namespace framework;
class FrontController {
const DEFAULT_CONTROLLER = 'framework\controllers\IndexController';
const DEFAULT_METHOD = 'index';
public $controller = self::DEFAULT_CONTROLLER;
public $method = self::DEFAULT_METHOD;
public $params = array();
public $model;
public $view;
function __construct() {
$this->model = new ModelFactory();
$this->view = new View();
}
// route request to the appropriate controller
public function route() {
// get request path
$basePath = trim(substr(PUBLIC_PATH, strlen($_SERVER['DOCUMENT_ROOT'])), '/') . '/';
$path = trim(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH), '/');
if($basePath != '/' && strpos($path, $basePath) === 0) {
$path = substr($path, strlen($basePath));
}
// determine what action to take
#list($controller, $method, $params) = explode('/', $path, 3);
if(isset($controller, $method)) {
$obj = __NAMESPACE__ . '\\controllers\\' . ucfirst(strtolower($controller)) . 'Controller';
$interface = __NAMESPACE__ . '\\controllers\\' . 'InterfaceController';
// make sure a properly implemented controller and corresponding method exists
if(class_exists($obj) && method_exists($obj, $method) && in_array($interface, class_implements($obj))) {
$this->controller = $obj;
$this->method = $method;
if(isset($params)) {
$this->params = explode('/', $params);
}
}
}
// make sure we have the appropriate number of arguments
$args = new \ReflectionMethod($this->controller, $this->method);
$totalArgs = count($this->params);
if($totalArgs >= $args->getNumberOfRequiredParameters() && $totalArgs <= $args->getNumberOfParameters()) {
call_user_func_array(array(new $this->controller, $this->method), $this->params);
} else {
$this->view->load('404');
}
}
}
You can use your URLs by one of two methods:
Establish the controllers the way your routing defines them
example.com/contact => Have a "contact" controller with default or index action
example.com/about/portfolio => Have an "about" controller with a "portfolio" action
Because your currently available routing says your URL is treated like "/controller/method", there is no other way.
Establish dynamic routing to allow multiple URLs to be handled by a single controller
Obviously this needs a bit of configuration because one cannot know which URLs are valid and which one should be redirected to the generic controller, and which ones should not. This is somehow a replacement for any of the rewriting or redirecting solutions, but as it is handled on the PHP level, change might be easier to handle (some webserver configurations do not offer .htaccess because of performance reasons, and it generally is more effort to create these).
Your configuration input is:
The URL you want to be handled and
The controller you want the URL passed to, and it's action.
You'll end up having an array structure like this:
$specialRoutes = array(
"/contact" => "IndexController::indexAction",
"/about/portfolio" => "IndexController::indexAction"
);
What's missing is that this action should get the current URL passed as a parameter, or that the path parts become designated parameters within your URL schema.
All in all this approach is a lot harder to code. To get an idea, try to look at the routing of common MVC frameworks, like Symfony and Zend Framework. They offer highly configurable routing, and because of this, the routing takes place in multiple classes. The main router only reads the configuration and then passes the routing of any URL to the configured routers if a match is detected.
Based on your code snippet I'd do it like this (pseudo php code):
$handler = get_controller($controller);
if(!$handler && ($alias = lookup_alias($path))) {
list($handler, $method) = $alias;
}
if(!$handler) error_404();
function lookup_alias($path) {
foreach(ALL_CONTROLLERS as $controller) {
if(($alias = $controller->get_alias($path))) {
return $alias;
}
}
return null;
}
So basically in case there is no controller to handle a certain location you check if any controller is configured to handle the given path as an alias and if yes return that controller and the method it maps to.
You can create a rewrite in your webserver for these exceptions. For example:
RewriteRule ^contact$ /index/contact
RewriteRule ^about/portfolio$ /about/portfolio
This will allow you to have simplified URLs that map to your regular structure.
You could have a dynamic rule if you are able to precisely define what should be rewritten to /index. For example:
RewriteRule ^([a-z]+)$ /index/$1
Try this dynamic htaccess rewrite rule:
RewriteRule ^(.+)/?$ /index/$1 [QSA]
The QSA flag in the above rule allows you to also add a query string to the end if you want, like this:
http://localhost/contact?arg1=1&arg2=2
EDIT: This rule would also handle cases such as /about/portfolio:
RewriteRule ^(.+)/?(.+)?$ /index/$1 [QSA]

Categories