Codeigniter & PHP - forcing a 404? - php
In codeigniter, as you know, a page of the form: /class/function/ID, where class is the controller name, function is the method within the controller, and ID is the parameter to pass to that method.
The typical usage would be (for a book site for example) to pass the book id to the function which would then query the database for appropriate book. My problem is this: I was messing around and randomly (in the url string) typed in an ID that is not present in the database (with normal point and click browsing this would never happen) and I get database errors due to the residual queries I attempt to perform using a non-existent ID.
I have written code to check if there are any rows returned before attempting to use the ID, but if the ID is non-existent I would like the user to get a 404 error page rather than a blank page or something (since this seems like proper functionality). This would need to be a true 404 page (not simply loading a view that looks like a 404 page) so as not to screw with search engines. Okay - so my question is this: within normal program logic flow (as described above) how can I force a 404 error using codeigniter? Thanks.
Update: code igniter has a show_404('page') function but I don't think this will generate a true HTTP 404 error...
show_404() actually sends the proper headers for a search engine to register it as a 404 page (it sends 404 status).
Use a Firefox addon to check the headers received when calling show_404(). You will see it sends the proper HTTP Status Code.
Check the default application/errors/error_404.php. The first line is:
<?php header("HTTP/1.1 404 Not Found"); ?>
That line sets the HTTP Status as 404. It's all you need for the search engine to read your page as a 404 page.
$this->output->set_status_header('404');
to generate 404 headers.
If you want a custom error page you can do the following thing.In your Libraries create a file name MY_Exceptions and extend it with CI_Exceptions.And then override the show_404() function.In this function you can now create an instance of your Controller class using &get_instance() function.And using this instance you can load your custom 404 Error page.
class MY_Exceptions extends CI_Exceptions {
public function __construct(){
parent::__construct();
}
function show_404($page = ''){ // error page logic
header("HTTP/1.1 404 Not Found");
$heading = "404 Page Not Found";
$message = "The page you requested was not found ";
$CI =& get_instance();
$CI->load->view('/*Name of you custom 404 error page.*/');
}
Only follows these steps:
Step 1
Update your application/config/routes.php file
$route['404_override'] = 'error/error_404';
Step 2
Create your own controller in controllers folder
ex. error.php
<?php
class Error extends CI_Controller
{
function error_404()
{
$data["heading"] = "404 Page Not Found";
$data["message"] = "The page you requested was not found ";
$this->load->view('error',$data);
}
}
?>
Step 3
Create your view in views folder
ex. error.php
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252" />
<title><?php echo $heading;?></title>
</head>
<body>
<?php echo $message;?>
</body>
</html>
I had the same problem with you and I found a complete solution for this in CodeIgniter 3. Here I would like to share step by step how to solve it. Of course, we need to support a custom 404 page to satisfy SEO requirement.
Show 404 page for URLs which do not match the schema in routes
Add a new Error controller in application/controllers to support a custom 404 page.
class ErrorController extends CI_Controller
{
public function __construct()
{
parent::__construct();
}
public function index()
{
$this->output->set_status_header('404');
return $this->load->view('errors/error_404');
}
}
Add a new custom view error_404.php for 404 page in application/views/errors
<div>
<p>We are so sorry. The page you requested could not be found.</p>
</div>
Declare 404_overide in config/routes.php
$route['404_override'] = 'ErrorController';
Show 404 page for URLs which match the schema in routes but point to non-existing resource.
Set subclass_prefix in config/config.
$config['subclass_prefix'] = 'MY_';
Define your custom Exceptions class in application/core
class MY_Exceptions extends CI_Exceptions {
public function __construct() {
parent::__construct();
}
function show_404($page = '', $log_error = TRUE) {
$CI = &get_instance();
$CI->output->set_status_header('404');
$CI->load->view('errors/error_404');
echo $CI->output->get_output();
exit;
}
}
Call show_404() wherever you want. Here I created my custom supper model class in application/models and check query results there. Other models will extends the supper model and show 404 page if they could not found a resource.
abstract class MY_Model extends CI_Model
{
protected $table = 'table_name';
public function __construct()
{
parent::__construct();
$this->load->database();
}
public function find($id)
{
$result = $this->db->get_where($this->table, ['id' => $id]);
$data = $result->row_object();
if (!$data) {
show_404();
}
return $data;
}
}
Yes show_404() WILL send out a 404 but it looks like hell. There have been a few hacks suggested here, but why hack when you can use built in features?
Upgrade to CI 2.0 and you'll be able to use the amazing:
$route['404_override'] = 'errors/error_404';
Then you can have a general errors controller without having to worry about trying to load views, libraries and helpers WY too early in the CI instance to function properly.
try using this
set_status_header(404);
Create controller in your application/controllers folder.
class Error extends Controller
{
function error_404()
{
$this->load->view('error');
}
}
Then in your application/library extend the Router class by creating application/libraries/MY_Router.php
class MY_Router extends CI_Router
{
private $error_controller = 'error';
private $error_method_404 = 'error_404';
function MY_Router()
{
parent::CI_Router();
}
// this is just the same method as in Router.php, with show_404() replaced by $this->error_404();
function _validate_request($segments)
{
// Does the requested controller exist in the root folder?
if(file_exists(APPPATH.'controllers/'.$segments[0].EXT))
{
return $segments;
}
// Is the controller in a sub-folder?
if(is_dir(APPPATH.'controllers/'.$segments[0]))
{
// Set the directory and remove it from the segment array
$this->set_directory($segments[0]);
$segments = array_slice($segments, 1);
if(count($segments) > 0)
{
// Does the requested controller exist in the sub-folder?
if(!file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].EXT))
{
return $this->error_404();
}
}
else
{
$this->set_class($this->default_controller);
$this->set_method('index');
// Does the default controller exist in the sub-folder?
if(!file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.EXT))
{
$this->directory = '';
return array();
}
}
return $segments;
}
// Can't find the requested controller...
return $this->error_404();
}
function error_404()
{
$segments = array();
$segments[] = $this->error_controller;
$segments[] = $this->error_method_404;
return $segments;
}
function fetch_class()
{
// if method doesn't exist in class, change
// class to error and method to error_404
$this->check_method();
return $this->class;
}
function check_method()
{
$class = $this->class;
if (class_exists($class))
{
if ($class == 'doc')
{
return;
}
if (! in_array('_remap', array_map('strtolower', get_class_methods($class)))
&& ! in_array(strtolower($this->method), array_map('strtolower', get_class_methods($class))))
{
$this->class = $this->error_controller;
$this->method = $this->error_method_404;
include(APPPATH.'controllers/'.$this->fetch_directory().$this->error_controller.EXT);
}
}
}
}
If the page does not exist, it will be routed to error controller
Related
CodeIgniter class repeated in url allowed
With a fresh install of CodeIgniter 2 and untouched welcome.php as follows: class Welcome extends CI_Controller { public function index() { $this->load->view('welcome_message'); } } and the following requests: url http response expected actual /index.php/welcome 200 200 /index.php/welcome/wtf 404 404 /index.php/welcome/welcome 404 200 ? /index.php/welcome/welcome/welcome 404 200 ? /index.php/welcome/welcome/welcome/welcome 404 200 ? /index.php/welcome/welcome/wtf 404 200 ? Note the last four requests. Why does CodeIgniter exhibit this behaviour? Is it possible to disable? My naive quick fix is below, but wondering if there is something that can be changed globally to address other controllers. class Welcome extends CI_Controller { public function index() { $this->load->helper('url'); if (strpos(uri_string(), 'welcome/welcome') !== false) { show_404(); } $this->load->view('welcome_message'); } }
After looking in CI's code, I think this is caused by the fetch_method function of CI_Router class. Look : function fetch_method() { if ($this->method == $this->fetch_class()) { return 'index'; } return $this->method; } So, the default behavior of the router is to set the method to index if method's name is equal to class' name. You should be able to override this by creating a MY_Router.php file in your core folder. <?php class MY_Router extends CI_Router { function fetch_method() { return $this->method; } }
My guess is that the 2nd welcome must act as an alias for index which is why it is working. So it evaluates to welcome(controller)/welcome(index/method)/param(passed to method). But don't quote me on this as I'm not familiar with CI2. You might be able to do something like this (using your code): class MY_Controller extends CI_Controller { public function __construct() { $this->load->helper('url'); $seg1 = $this->uri->segment(1); $seg2 = $this->uri->segment(2); if (strpos(uri_string(), "{$seg1}/{$seg2}") !== false) { show_404(); } } } all controllers would have to extend MY_Controller which would be stored in application/core
Controller Not loading in CI
I have 2 controllers in application/controllers Welcome.php pages.php I am accessing welcome.php through this url http://opunletter.com/index.php/welcome/ But while accessing http://opunletter.com/index.php/pages/ I am getting following error 404 Page Not Found The page you requested was not found. I am Not able to figure the error. Plz help ! class Pages extends Controller { function search($search_terms = '') { // If the form has been submitted, rewrite the URL so that the search // terms can be passed as a parameter to the action. Note that there // are some issues with certain characters here. if ($this->input->post('q')) { redirect('/pages/search/' . $this->input->post('q')); } if ($search_terms) { // Load the model and perform the search $this->load->model('page_model'); $results = $this->page_model->search($search_terms); } // Render the view, passing it the necessary data $this->load->view('search_results', array( 'search_terms' => $search_terms, 'results' => #$results )); } }
Try with extends CI_Controller and construct() like following: class Pages extends CI_Controller { function __construct() { parent::__construct(); } function search($search_terms = '') { ... } } And you should use controller name Pages.php (capital first letter) instead of pages.php in CI3.
Error:404 page not found, code igniter
controller.php <?php define('_root',$_SERVER['DOCUMENT_ROOT']); include(_root.'/innoshop/application/models/model.php'); // include_once 'model.php'; class Controller { public $model; public function __construct() { $this->model = new Model(); } If i put localhost:8888/projectname, i got error like this 404 Page Not Found The page you requested was not found. anyone help me
As the guys say you should read the docs as this is very wrong. To fix do this.. class Controller extends CI_Controller{//better to give your controller a more meaningful name public function __construct(){ parent::__construct(); //use the CI loader - the model will then be available like this $this->model->some_function(); $this->load->model('model');//better to give your model a more meaningful name as well } //the index method allows you to use just the controller name in your URI eg localhost:8888/projectname/index.php/controller public function index(){ echo 'something to see'; } //an alternative controller method get it like localhost:8888/projectname/index.php/controller/your_method_name public function your_method_name(){ echo 'something to see in your method'; } } If you want rid of the index.php in the URI search for questions related to .htaccess in CodeIgniter If you want to be able to use a uri like this localhost:8888/projectname then you need to add a route in config/routes.php that defines the default controller like this $route['default']='controller';
how to create frontend controller and action in gocart
I am facing some problems in gocart codeigniter. I create a front end controller named decorate then action named products and passing slug in action. Following is the code of controller <?php class Decorate extends Front_Controller { function __construct() { parent::__construct(); //make sure we're not always behind ssl remove_ssl(); $this->load->model(array('Decorate_model')); $this->lang->load('decorate'); } function index() { $data['page_title'] = lang('decorates'); $data['decorates'] = $this->Decorate_model->get_front_all_decorates(); $this->load->view('decorate_home',$data); } /* Single Decorate */ function products($decorate_slug) { $data['page_title'] = lang('decorates'); } } ?> But when I am running url www.abc.com/gocart/index.php/decorate/products/decorate-1 then it is showing 404 error. I am not getting how to create action in gocart.
Could be Gocart routes everything to one master controller. What's in your routes.php?
How to load a controller from another controller in codeigniter?
I want to load a controller from a function in another controller because the library I integrated to my project I don't want to load it to the controller because I want to keep it clean and related. I tried using modules but I still had to put controller in the url like http://example.com/maincontroller/function http://example.com/othercontroller/function I have default controller so I can load http://example.com/function so how could I access the controller from a function from main so I don't have to put the controller in the url. I'm still willing to use HMVC if I can load the controller function from the main controller function.
yes you can (for version 2) load like this inside your controller $this->load->library('../controllers/whathever'); and call the following method: $this->whathever->functioname();
You can't load a controller from a controller in CI - unless you use HMVC or something. You should think about your architecture a bit. If you need to call a controller method from another controller, then you should probably abstract that code out to a helper or library and call it from both controllers. UPDATE After reading your question again, I realize that your end goal is not necessarily HMVC, but URI manipulation. Correct me if I'm wrong, but it seems like you're trying to accomplish URLs with the first section being the method name and leave out the controller name altogether. If this is the case, you'd get a cleaner solution by getting creative with your routes. For a really basic example, say you have two controllers, controller1 and controller2. Controller1 has a method method_1 - and controller2 has a method method_2. You can set up routes like this: $route['method_1'] = "controller1/method_1"; $route['method_2'] = "controller2/method_2"; Then, you can call method 1 with a URL like http://site.com/method_1 and method 2 with http://site.com/method_2. Albeit, this is a hard-coded, very basic, example - but it could get you to where you need to be if all you need to do is remove the controller from the URL. You could also go with remapping your controllers. From the docs: "If your controller contains a function named _remap(), it will always get called regardless of what your URI contains.": public function _remap($method) { if ($method == 'some_method') { $this->$method(); } else { $this->default_method(); } }
you cannot call a controller method from another controller directly my solution is to use inheritances and extend your controller from the library controller class Controller1 extends CI_Controller { public function index() { // some codes here } public function methodA(){ // code here } } in your controller we call it Mycontoller it will extends Controller1 include_once (dirname(__FILE__) . "/controller1.php"); class Mycontroller extends Controller1 { public function __construct() { parent::__construct(); } public function methodB(){ // codes.... } } and you can call methodA from mycontroller http://example.com/mycontroller/methodA http://example.com/mycontroller/methodB this solution worked for me
I had a similar problem. I wanted to have two controllers: homepage.php - public facing homepage home.php - home screen once a user was logged in and I wanted them both to read from 'mydomain.com' I was able to accomplish this by setting 'hompepage' as the default controller in my routes config and adding a remap function to homepage.php function _remap() { if(user_is_logged_in()) { require_once(APPPATH.'controllers/home.php'); $oHome = new Home(); $oHome->index(); } else { $this->index(); } }
I was having session file not found error while tried various ways, finally achieved like this. Made the function as static (which I want to call in the another controller), and called like require_once('Welcome.php'); Welcome::hello();
While the methods above might work, here is a very good method. Extend the core controller with a MY controller, then extend this MY controller for all your other controllers. For example, you could have: class MY_Controller extends CI_Controller { public function is_logged() { //Your code here } public function logout() { //Your code here } } Then your other controllers could then extend this as follows: class Another_Controller extends MY_Controller { public function show_home() { if (!$this->is_logged()) { return false; } } public function logout() { $this->logout(); } }
I came here because I needed to create a {{ render() }} function in Twig, to simulate Symfony2's behaviour. Rendering controllers from view is really cool to display independant widgets or ajax-reloadable stuffs. Even if you're not a Twig user, you can still take this helper and use it as you want in your views to render a controller, using <?php echo twig_render('welcome/index', $param1, $param2, $_); ?>. This will echo everything your controller outputted. Here it is: helpers/twig_helper.php <?php if (!function_exists('twig_render')) { function twig_render() { $args = func_get_args(); $route = array_shift($args); $controller = APPPATH . 'controllers/' . substr($route, 0, strrpos($route, '/')); $explode = explode('/', $route); if (count($explode) < 2) { show_error("twig_render: A twig route is made from format: path/to/controller/action."); } if (!is_file($controller . '.php')) { show_error("twig_render: Controller not found: {$controller}"); } if (!is_readable($controller . '.php')) { show_error("twig_render: Controller not readable: {$controller}"); } require_once($controller . '.php'); $class = ucfirst(reset(array_slice($explode, count($explode) - 2, 1))); if (!class_exists($class)) { show_error("twig_render: Controller file exists, but class not found inside: {$class}"); } $object = new $class(); if (!($object instanceof CI_Controller)) { show_error("twig_render: Class {$class} is not an instance of CI_Controller"); } $method = $explode[count($explode) - 1]; if (!method_exists($object, $method)) { show_error("twig_render: Controller method not found: {$method}"); } if (!is_callable(array($object, $method))) { show_error("twig_render: Controller method not visible: {$method}"); } call_user_func_array(array($object, $method), $args); $ci = &get_instance(); return $ci->output->get_output(); } } Specific for Twig users (adapt this code to your Twig implementation): libraries/Twig.php $this->_twig_env->addFunction('render', new Twig_Function_Function('twig_render')); Usage {{ render('welcome/index', param1, param2, ...) }}
Create a helper using the code I created belows and name it controller_helper.php. Autoload your helper in the autoload.php file under config. From your method call controller('name') to load the controller. Note that name is the filename of the controller. This method will append '_controller' to your controller 'name'. To call a method in the controller just run $this->name_controller->method(); after you load the controller as described above. <?php if(!function_exists('controller')) { function controller($name) { $filename = realpath(__dir__ . '/../controllers/'.$name.'.php'); if(file_exists($filename)) { require_once $filename; $class = ucfirst($name); if(class_exists($class)) { $ci =& get_instance(); if(!isset($ci->{$name.'_controller'})) { $ci->{$name.'_controller'} = new $class(); } } } } } ?>
There are many ways by which you can access one controller into another. class Test1 extends CI_controller { function testfunction(){ return 1; } } Then create another class, and include first Class in it, and extend it with your class. include 'Test1.php'; class Test extends Test1 { function myfunction(){ $this->test(); echo 1; } }
You can call Model from a Controller so put your functions inside a Model and call it from the controller . Worked for me. (codeignitor 3)