Form Method Spoofing in CodeIgniter - codeigniter

So I wanna use HTTP DELETE on my route when deleting the data.
Is it possible to use method spoofing in CodeIgniter?
Maybe like Laravel does by using hidden input "_method" ?
<input type="hidden" name="_method" value="DELETE">
Is there any suggestion how to do this in CodeIgniter?
Cheers.

I just noticed this feature in Laravel docs, googled and found this question. I'll share my implementation in CI 3.0+ (maybe 2.0+).
File: application/helpers/MY_form_helper.php
<?php defined('BASEPATH') OR exit('No direct script access allowed');
if ( ! function_exists('method_field')) {
function method_field($method)
{
return '<input type="hidden" name="__method" value="'.$method.'">';
}
}
File: application/config/routes.php
<?php defined('BASEPATH') OR exit('No direct script access allowed');
$route['default_controller'] = 'home';
$route['404_override'] = '';
$route['translate_uri_dashes'] = FALSE;
// seperate controllers in HTTP VERB sub-folders
$route['(.+)']['GET'] = 'get/$1';
$route['(.+)']['POST'] = 'post/$1';
$route['(.+)']['PUT'] = 'put/$1';
$route['(.+)']['PATCH'] = 'patch/$1';
$route['(.+)']['DELETE'] = 'delete/$1';
File: application/core/MY_Router.php
<?php defined('BASEPATH') OR exit('No direct script access allowed');
protected function _parse_routes()
{
// Turn the segment array into a URI string
$uri = implode('/', $this->uri->segments);
// Get HTTP verb
$http_verb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli';
// ::: START EDIT (Form Method Spoofing) :::
$_request_method = strtolower(isset($_POST['__method']) ? $_POST['__method'] : isset($_GET['__method']) ? $_GET['__method'] : NULL);
if ( ! empty($_request_method) && in_array($_request_method, ['get', 'post', 'put', 'patch', 'delete'])) {
$http_verb = $_request_method;
}
// ::: END EDIT (Form Method Spoofing) :::
// Loop through the route array looking for wildcards
foreach ($this->routes as $key => $val)
{
// Check if route format is using HTTP verbs
if (is_array($val))
{
$val = array_change_key_case($val, CASE_LOWER);
if (isset($val[$http_verb]))
{
$val = $val[$http_verb];
}
else
{
continue;
}
}
// Convert wildcards to RegEx
$key = str_replace(array(':any', ':num'), array('[^/]+', '[0-9]+'), $key);
// Does the RegEx match?
if (preg_match('#^'.$key.'$#', $uri, $matches))
{
// Are we using callbacks to process back-references?
if ( ! is_string($val) && is_callable($val))
{
// Remove the original string from the matches array.
array_shift($matches);
// Execute the callback using the values in matches as its parameters.
$val = call_user_func_array($val, $matches);
}
// Are we using the default routing method for back-references?
elseif (strpos($val, '$') !== FALSE && strpos($key, '(') !== FALSE)
{
$val = preg_replace('#^'.$key.'$#', $val, $uri);
}
$this->_set_request(explode('/', $val));
return;
}
}
// If we got this far it means we didn't encounter a
// matching route so we'll set the site default route
$this->_set_request(array_values($this->uri->segments));
}
Note:
With the routing structure in application/config/routes.php, you can have same controller names for all HTTP VERBs!
Simply load the form helper with $this->load->helper('form') to use the <?= method_field('PUT') ?> in your views or use <input type="hidden" name="__method" value="PUT">.
I used the hidden form field name __method instead of Laravel's _method because it is possible to use such field name (+ double underscore is so CodeIgniter-ish).
This EDIT can also spoof GET requests too. EG: http://project-name/controller/action?__method=PATCH will initiate a PATCH controller instead!

Related

Adding Parameters in Routing URL Codeigniter

I have some problem with routing and parameters.
I have routing in routes.php like this :
$route['register/(:any)'] = 'member/register/$1';
And in my controller I have like this :
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Register extends CI_Controller
{
function __construct()
{
parent::__construct();
$this->load->model('Page_model');
$this->load->model('member_model');
$this->load->model('login_model');
$this->load->library('session');
$this->load->helper('url');
$this->load->helper('html');
$this->load->helper('form');
}
public function index()
{
$slug = $this->uri->segment(1);
$type = $this->uri->segment(2);
var_dump($type);
exit();
if ($slug != NULL)
{
$data['page'] = $this->Page_model->get_page($slug);
if (empty($data['page']))
{
// show_404();
$data['page'] = new stdClass();
$data['page']->page_template = 'forofor';
$data['page']->title = 'Page Not Found';
$data['page']->meta_description = 'Page Not Found';
$data['page']->meta_keywords = 'Page Not Found';
}
else
{
$data['slider'] = $this->Page_model->get_slider($data['page']->page_id);
}
}
else
{
$data['page'] = $this->Page_model->get_page('home');
$data['slider'] = $this->Page_model->get_slider($data['page']->page_id);
}
$data['head_title'] = $data['page']->title;
// load all settings and data
$data['settings'] = $this->Page_model->get_settings();
$data['gallery'] = $this->Page_model->get_gallery();
$data['meta'] =
array(
array(
'name' => 'description',
'content' => $data['page']->meta_description
),
array(
'name' => 'keywords',
'content' => $data['page']->meta_keywords
)
);
// $data['meta'] = $this->meta;
$data['menu'] = $this->Page_model->get_menu('frontend','header');
$data['settings'] = $this->Page_model->get_settings();
$data['notice'] = $this->session->flashdata('notice');
// $this->load->view('frontend/template/index_full', $data);
$this->load->view('frontend/template/head', $data);
$this->load->view('frontend/template/pre_header');
$this->load->view('frontend/template/header');
$this->load->view('frontend/template/modal');
//$this->load->view('frontend/template/modal_registration');
/*if ($page[0]->page_template != 'forofor' && $data['page']->slider != 0)
{
$this->load->view('frontend/template/slider');
}*/
if ($slug != 'home' && $slug != NULL)
{
//$this->load->view('member/'.$data['page']->page_template);
$this->load->view('member/register');
}
else
{
$this->load->view('frontend/template/slider');
$this->load->view('frontend/page_template/homepage');
}
$this->load->view('frontend/template/pre_footer');
$this->load->view('frontend/template/footer');
$this->load->view('frontend/template/js');
$this->load->view('frontend/template/closing_body_html');
}
}
But if I input the routes in my browser it gives me 404 Page not found
This is my routes :
127.0.0.1/project/register/buyer
And 127.0.0.1/project/ is my Base URL
Anyone knows why it could be happen ?
Thank you.
Your routing is wrong. According to your post you are setting Member controller then register method.
Try below code
$route['register/(:any)'] = 'register/index/$1';
$route['register/(:any)/(:any)'] = 'register/index/$1/$2'; // Optional
The second option is only optional:
This is how routes work in Codeigniter
Typically there is a one-to-one relationship between a URL string and
its corresponding controller class/method. The segments in a URI
normally follow this pattern:
example.com/class/function/id/ In some instances, however, you may
want to remap this relationship so that a different class/method can
be called instead of the one corresponding to the URL.
For example, let’s say you want your URLs to have this prototype:
example.com/product/1/ example.com/product/2/ example.com/product/3/
example.com/product/4/ Normally the second segment of the URL is
reserved for the method name, but in the example above it instead has
a product ID. To overcome this, CodeIgniter allows you to remap the
URI handler.
Reference : https://www.codeigniter.com/userguide3/general/routing.html
So,the routes follow this syntax
$route['route_url'] = 'controller/method/$paramater';
So,your route will be
Let me know your queries
$route['register/(:any)'] = 'register/index/$1';

prevent accessing of controller directly from url which is in subdirectory in codeigniter

This is my controller structute:
controllers
|
|__ posts
|
|__post.php
i have removed directory name from the url by changing in the routes.php file
$route['post/posts_controller'] = 'posts/post/posts_controller';
but now i want if anybody hits the complete url with the directory name i.e. http://localhost/url_routing/posts/post/posts_controller
then the page not found should appear.
This is a real problem with CodeIgniter, which can result in a LOT of duplicate content for search engine. The only workaround I found is to overwrite the Router to only use the routes.php and not folders / controllers names.
application/core/MY_Router.php
class MY_Router extends CI_Router {
/**
* Parse Routes
*
* Matches any routes that may exist in the config/routes.php file
* against the URI to determine if the class/method need to be remapped.
*
* #return void
*/
protected function _parse_routes()
{
// Turn the segment array into a URI string
$uri = implode('/', $this->uri->segments);
// Get HTTP verb
$http_verb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli';
// Loop through the route array looking for wildcards
foreach ($this->routes as $key => $val)
{
// Check if route format is using HTTP verbs
if (is_array($val))
{
$val = array_change_key_case($val, CASE_LOWER);
if (isset($val[$http_verb]))
{
$val = $val[$http_verb];
}
else
{
continue;
}
}
// Convert wildcards to RegEx
$key = str_replace(array(':any', ':num'), array('[^/]+', '[0-9]+'), $key);
// Does the RegEx match?
if (preg_match('#^'.$key.'$#', $uri, $matches))
{
// Are we using callbacks to process back-references?
if ( ! is_string($val) && is_callable($val))
{
// Remove the original string from the matches array.
array_shift($matches);
// Execute the callback using the values in matches as its parameters.
$val = call_user_func_array($val, $matches);
}
// Are we using the default routing method for back-references?
elseif (strpos($val, '$') !== FALSE && strpos($key, '(') !== FALSE)
{
$val = preg_replace('#^'.$key.'$#', $val, $uri);
}
$this->_set_request(explode('/', $val));
return;
}
}
return;
}
}
This will result in CodeIgniter only using your routes.php, so I you still want to use CI routing, then don't use it.

Rewrite the url using CodeIgniter

I read a lot of forum topics but I did not found any answer which meets my needs.
I am running a blog system and my current urls for each article look like: www.example.com/controller/method/id. However, for each article I have a title stored in a database, and would like to use that title in the url, so it would look like this: www.example.com/title
Hello there you are asking for a whole lot of code and you did not do any research yourself.
The best way is to use id with your title: http://www.example.com/id/long-title it is much better than using only title, because:
same title can occur more than once (creates problem that can be avoided)
slow loading/searching due querying by slug instead of ID (slow performance)
user needs to remember whole title (with id+title; user can copy partially broken url www.example.com/234593/this-title-is-) / opinion based
In order to make my proposal work you need to:
set up routes (application/config/routes.php)
//leave two CodeIgniter's routes on top
$route['default_controller'] = "welcome";
$route['404_override'] = '';
//leave this two routes in this order + make them LAST routes
$route['(:num)'] = 'blog/post/$1'; //if only id is in url
$route['(:num)/(:any)'] = 'blog/post/$1/$2'; //if id and title is in url
set up controller (application/controllers/blog.php)
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Blog extends CI_Controller {
public function __construct()
{
parent::__construct();
//load models helpers etc if not autoloaded
$this->load->helper('url');
}
public function index()
{
echo 'should throw 404 or redirect home because id was not provided';
}
public function post($id = null, $title = '')
{
if (!$this->validId( $id )) redirect(); //redirect somewhere because id of post is invalid search/404/home...
if (empty(trim($title))) {
redirect(base_url().$id.'/'.$this->getTitle($id), 'location', 301); //redirect to same page search engines friendly (301 header)
}
//display what you need
echo 'params; id: ' . $id . ' title: ' . $title;
}
private function validId($id)
{
if (is_numeric($id))
return true; //use database to check validity in this case every id is valid
}
private function getTitle()
{
//id should be good to use at this point
//get title using database
return $this->seoUrl('How you react of crying babies will determine their future.'); //made up title
}
private function seoUrl($string)
{
//source: http://stackoverflow.com/a/11330527/1564365
//Lower case everything
$string = strtolower($string);
//Make alphanumeric (removes all other characters)
$string = preg_replace("/[^a-z0-9_\s-]/", "", $string);
//Clean up multiple dashes or whitespaces
$string = preg_replace("/[\s-]+/", " ", $string);
//Convert whitespaces and underscore to dash
$string = preg_replace("/[\s_]/", "-", $string);
return $string;
}
}
/* End of file blog.php */
/* Location: ./application/controllers/blog.php */
thats it, now create yourself model that can validate ID, grab title...
sample code (of model):
public function isInDb($table, $where = array())
{
$q = $this->db->get_where($table, $where);
return ($q->num_rows() > 0) ? true : false; //returns either true or false, pretty straight forward
}
public function getColumn(){}
Now you use (generate) url www.example.com/1 (this will redirect with 301 header to /1/title) or you can use (generate) www.example.com/1/title link, however if you generate url like: /1/fake-title (title is invalid for id 1 it will not redirect to the correct one)
This solution is SEO friendly.

codeigniter config form_validation with subfolders not working

I have using a lot config form_validation file. It's working good!
But now I'm trying to get it work with controller in subfolder
/controllers/panel/users.php
My form_validation config file looks like
$config = array(
'panel/users/edit/' => array(
array('field' => 'login', 'label' => 'Логин', 'rules' => "trim|required|valid_email")
)
And my Users controller is
public function edit($user_id = FALSE)
{
if ($this->input->post('save'))
{
$this->load->library('form_validation');
if ($this->form_validation->run())
{
// Do some
}
}
}
But $this->form_validation->run() is always return FALSE
It isn't designed to work this way, there was a relevant change to ruri_string() #122 which would have fixed this but it had other repercussions and needs to be rethought.
You can call your validation rule group explicitly (drop the trailing slash from your rule group name)
if ($this->form_validation->run('panel/users/edit'))
or, if appropriate in your situation, workaround this by prepending uri->segment(1) to the auto-detected rule group.
application/libraries/MY_Form_validation.php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class MY_Form_validation extends CI_Form_validation {
function run($group = '')
{
// Prepend URI to match subfolder controller validation rules
$uri = ($group == '') ? $this->CI->uri->segment(1) . $this->CI->uri->ruri_string() : $group;
return parent::run($uri);
}
}

KO3 - Kohana 3 - How can I pass $_POST data from a controller/action back to the view/form that called it?

I am trying to validate a form submission in Kohana 3. I have the form::open point to my action_create in my controller which successfully validates the data posted to it from the form in my view. If the data passes validation, a new item is created as intended, and the user is redirected to the item that was just created. This works correctly. If the data fails validation, however, I would like the user to be directed back to the originating view/page while retaining a variable containing the data that was posted so that I can repopulate the form and display errors.
In short, how can I pass data from a view -> controller -> original view?
Thank you, everyone!
The user also posed this question on the Kohana forums.
Those seeking an answer to this should have look over there.
I assume you're using Controller_Template.
File views/form.php:
// Set default variables if variables not passed to this view
$username = isset($username) ? $username : '';
echo Form::open('login');
// Input: username
echo Form::label('username', 'Username');
echo Form::input('username', $username);
echo isset($errors['username']) ? $errors['username'] : '';
// Input: username
echo Form::label('password', 'Password');
echo Form::input('password', $password);
echo isset($errors['password']) ? $errors['password'] : '';
echo Form::close();
File views/template.php
<html>
<head><title>My Website</title></head>
<body>
<?php echo isset($content) ? $content : ''; ?>
</body>
</html>
File classes/controller/user.php
Class Controller_User extends Controller_Template {
public $template = 'template';
public function index()
{
$this->template->content = $this->display_form('form');
}
public function login()
{
// Setup validation & rules here
// Check validation, assume $validation is Validation object
if ($validation->check()
{
// Validation succeeded. Do anything you want here
}
else
{
// Validation failed. Display form with entered values
$form_vars = $_POST;
$form_vars['errors'] = $validation->errors();
// Display form
$this->template->content = $this->display_form('form', $form_vars);
}
}
// Displaying form
private function display_form($form_file, $form_vars=NULL)
{
$form = View::factory($form_file);
if ($form_vars != NULL)
{
foreach($form_vars as $key => $value)
{
$form->$key = $value;
}
}
return $form;
}
}
Hope that helps!

Resources