I am using Kohana 3.3 and I read in this forum post.
It says that to prevent stack traces from being shown to the end users, I need to override Kohana_Exception::_handler() to do something different with the errors that percolate up. Does that mean overriding Kohana_Exception and adding the following function?
public static function _handler(Exception $e)
{
try
{
// Log the exception
Kohana_Exception::log($e);
// Generate the response
//instead of below line:
//$response = Kohana_Exception::response($e);
$response = //what do I do here, return a 404 page or custom 500 page?
return $response;
}
//rest of function
}
If so, what do I return there?
EDIT:
bootstrap.php
/**
* Attach the file write to logging. Multiple writers are supported.
*/
Kohana::$log->attach(new Log_File(APPPATH.'logs'));
/**
* Attach a file reader to config. Multiple readers are supported.
*/
Kohana::$config->attach(new Config_File);
/**
* Attach customer error handler if we are in production
*/
if(Kohana::$environment == Kohana::PRODUCTION || Kohana::$environment == Kohana::STAGING)
{
set_exception_handler(array('My_Exception', 'handler'));
throw new Exception('text'); //this works, if removed however my exception handler does not do anything
}
My_Exception.php (in classes/My/Exception.php)
<?php
class My_Exception extends Kohana_Exception
{
public static function handler(Exception $e)
{
try
{
// Log the exception
Kohana_Exception::log($e);
// Let's try and load an error View
$class = get_class($e);
if($class == 'HTTP_Exception_404')
{
$view = View::factory('errors/404');
$view->set('error_code', 404);
}
else
{
$view = View::factory('errors/general');
$view->set('error_code', $e->getCode()); // alternatively use $e->getCode()
$view->set('error_message', $e->getMessage()); // alternatively use $e->getMessage();
}
// Let's render the output and send it to the browser
$response = $view->render();
echo $response;
}
catch (Exception $e)
{
/**
* Things are going *really* badly for us, We now have no choice
* but to bail. Hard.
*/
// Clean the output buffer if one exists
ob_get_level() AND ob_clean();
// Set the Status code to 500, and Content-Type to text/plain.
header('Content-Type: text/plain; charset='.Kohana::$charset, TRUE, 500);
// This will output the Exceptiones error message
// You may want to display something else
echo $e->getMessage();
exit(1);
}
}
}
I have actually investigated quite a bit further this issue and rewritten my asnwer from scratch now that I have a more complete understanding of Kohana's behaviour in this area.
To achieve what you're after you need to do two things:
Change the default error View (in APPPATH/bootstrap.php):
/**
* Change default error View
*/
if(Kohana::$environment == Kohana::PRODUCTION || Kohana::$environment == Kohana::STAGING)
{
Kohana_Exception::$error_view = 'errors/general';
}
Note that your template file has to use the same (and only those) variable names as Kohana's native one, i.e.:
$class
$code
$message
$file
$line
$trace
2. Create custom HTTP error pages following the tutorial you linked to in the comments.
By following these steps you assure that:
You have your own view for all Kohana's error pages.
You can have custom views for different HTTP errors.
You don't have to override Kohana's default Exception Handler (which, as this exercise proved, isn't exactly simple to implement).
I have tested the above approach and it worked like a charm for me.
I just set the Kohana_Exception::$error_view at the bootstrap file, and created the corresponding view, nothing else was necessary, all errors have redirected auto-magically...
Related
I have some custom routes
Route::get('/files/', [
'as' => 'read',
'uses' => 'Myname\MyPlugin\Http\Controllers\FilesController#read'
]);
Somewhere in my class I have a function to validate a path
private function getPath()
{
$path = Input::get('path');
if (!$path)
{
throw new MyException('parameter is missing. path required', 400);
}
return base_path().'/'.$path;
}
I have set a custom error handler with a JSOM but it's the error handler of OctoberCMS that render the error in HTML format.
Do you know a way to replace default error handler of OctoberCMS by a custom one ?
Thanks
Just found the anwser in the documentation : https://octobercms.com/docs/services/error-log#exception-handling
October provide App:error to manage Exception in your plugin.
App::error(function(MyException $exception) {
//do what you want here
}
Don't forger to create a custom Exception for your plugin. If you use the generic Exception, you'll catch all the Exceptions.
I am posting an alternative solution here for a custom error handler because of this questions's visibility on Google.
I had problems using App:error due to this issue on Github: https://github.com/octobercms/october/issues/3416
Rather than using October's App::error for a custom error handling, try the following:
Create a custom error handler that inherit's from October's error handler. For example, create the following class in plugins/{AUTHOR}/{PLUGIN}/classes/CustomHandler.php (assuming you are developing a plugin for OctoberCMS). Override the render method of the handler.
<?php
namespace {AUTHOR}\{PLUGIN}\Classes; //<-- CHANGE ME OBVIOUSLY FOR YOUR PLUGIN
use October\Rain\Foundation\Exception\Handler;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Response;
use Exception;
use Illuminate\Database\Eloquent\ModelNotFoundException; // As an example exception you want to handle ...
/* Custom error handler which replaces the default error handler for OctoberCMS. */
class CustomHandler extends Handler
{
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $exception
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
/* Custom JSON response for ModelNotFoundException exceptions. */
if($exception instanceof ModelNotFoundException){
return Response::json(['error' => 'A record was not found for the resource that was requested.'], 404);
}
/* The rest of this code is just the 'default' code from OctoberCMS' error handler. */
/* The only change is the code above this comment where I handle a specific exception in a unique way.*/
/* i.e. I decided to return JSON for the error rather than an HTML error page (in debug mode). */
if (!class_exists('Event')) {
return parent::render($request, $exception);
}
$statusCode = $this->getStatusCode($exception);
$response = $this->callCustomHandlers($exception);
if (!is_null($response)) {
return Response::make($response, $statusCode);
}
if ($event = Event::fire('exception.beforeRender', [$exception, $statusCode, $request], true)) {
return Response::make($event, $statusCode);
}
return parent::render($request, $exception);
}
}
Then, in your plugin registration file Plugin.php, add a boot method with the following code:
<?php namespace {AUTHOR}\{PLUGIN};
use System\Classes\PluginBase;
use {AUTHOR}\{PLUGIN}\Classes\CustomHandler; //<-- IMPORTANT
use Illuminate\Contracts\Debug\ExceptionHandler; //<-- IMPORTANT
class Plugin extends PluginBase
{
/**
* #var array Plugin dependencies
*/
public $require = [];
public function registerComponents()
{
}
public function registerSettings()
{
}
public function boot(){
/* Replace the default error handler of OctoberCMS to return JSON format responses instead. */
/* Also, this is used in order to control the responses for certain types of errors/exceptions. */
/* We are going about it this way because App::error (as mentioned in the documentation for OctoberCMS), */
/* was not returning the response properly presumably because of a bug. Argh... */
$this->app->bind(
ExceptionHandler::class,
CustomHandler::class
);
}
}
Since, the new CustomHandler.php was added to the classes directory, the new class should already be picked up. No need for composer dump-autoload -o or changes to the composer.json.
That is it. When the ModelNotFoundException occurs, the response given to the web browser will be JSON that you wrote (from the custom error handler we created).
Relevant links:
https://blog.sarav.co/registering-custom-exception-handler-laravel-5/
https://laravel.com/docs/5.8/container#binding
https://octobercms.com/forum/post/is-it-posible-to-bring-back-the-app-directory-for-native-laravel
I doing tutorial follow http://book.cakephp.org/3.0/en/development/errors.html#exception-renderer but it is not working and display blank page.
In config/bootstrap.php
use App\Error\AppError;
$errorHandler = new AppError();
$errorHandler->register();
In src/Error/AppError.php
<?php
namespace App\Error;
use Cake\Error\BaseErrorHandler;
class AppError extends BaseErrorHandler
{
public function _displayError($error, $debug)
{
return 'There has been an error!';
}
public function _displayException($exception)
{
return 'There has been an exception!';
}
public function handleFatalError($code, $description, $file, $line)
{
return 'A fatal error has happened';
}
}
I create my_error.ctp in src/Template/Layout/my_error.ctp. And in my src/Template/Error/error404.ctp I change layout to my_error.ctp.
$this->layout = 'my_error';
Finally, In my controller
use Cake\Network\Exception\NotFoundException;
$staff = $this->Staff->find()->where(['Staff.StaffId = '=> $id, 'Staff.PartnerId = ' =>$this->partnerId])->first();
if (empty($staff)) {
throw new NotFoundException(__('Staff not found'));
}
Whenever encountering blank pages, enabled debug mode, visit the URL again, and check your error logs.
However, problem in this case is most likely that the docs are incorrect/misleading, as the example app error won't do anything at all. The _ prefixed methods are ment to be protected, having them return something has no effect, and handleFatalError is ment to return a boolean.
Just look at the source of Cake\Error\BaseErrorHandler and the core error handler Cake\Error\ErrorHandler, the methods that you are overwriting are ment to generate output!
You may want to report that as an issue over at GitHub.
If all you want to do, is create a custom 4xx error page, then all you need to do is to edit the src/Template/Error/error400.ctp template accordingly.
I found my mistake. :(
Because in bootstrap.php I copy below code at the end of file. Therefore Cake cannot understand it. Please close this issue. Thank you for support.
use App\Error\AppError;
$errorHandler = new AppError();
$errorHandler->register();
I wish to show custom exception pages in my application. In documentation is written:
"Note: We can also use HMVC to issue a sub-request to another page rather than generating the Response in the HTTP_Exception itself." The problem is i don't know how to do it.
Code which I am using is:
class HTTP_Exception_404 extends Kohana_HTTP_Exception_404 {
/**
* Generate a Response for the 404 Exception.
*
* The user should be shown a nice 404 page.
*
* #return Response
*/
public function get_response()
{
$view = View::factory('errors/404');
// Remembering that `$this` is an instance of HTTP_Exception_404
$view->message = $this->getMessage();
$response = Response::factory()
->status(404)
->body($view->render());
return $response;
}
}
It is working but i need to show the message in currently used template.
Regards
You shouldn't need to bother with sub-requests if all you want is to use the template. Here's the easy way:
class HTTP_Exception_404 extends Kohana_HTTP_Exception_404 {
public function get_response()
{
$view = View::factory('errors/404');
// Remembering that `$this` is an instance of HTTP_Exception_404
$view->message = $this->getMessage();
// Wrap it in our layout
$layout = $layout = View::factory('template');
$layout->body = $view->render();
$response = Response::factory()
->status(404)
->body($layout->render());
return $response;
}
}
If you really must use a sub-request, just do the sub-request normally:
class HTTP_Exception_404 extends Kohana_HTTP_Exception_404 {
public function get_response()
{
$request = Request::factory('route_to_error_page_action')
->method(Request::POST)
->post(array('message' => $this->getMessage()));
$response = $request->execute();
// If you didn't return the 404 code from the sub-request, uncomment this:
$response->status(404);
return $response;
}
}
I tried to follow the instructions here: http://kohanaframework.org/3.0/guide/kohana/tutorials/error-pages But for some reason I am unable to catch the HTTP_Exception_404 I still get a ugly error page and not my custom page.
Also when I type in the URL error/404/Message, I get a ugly Kohana HTTP 404 error message.
Here is the files structure:
modules
my
init.php
classes
controller
error_handler.php
http_response_exception.php
kohana.php
views
error.php
Code:
init.php:
<?php defined('SYSPATH') or die('No direct access');
Route::set('error', 'error/<action>(/<message>)', array('action' => '[0-9]++', 'message' => '.+'))
->defaults(array(
'controller' => 'error_handler'
));
http_response_exception.php:
<?php defined('SYSPATH') or die('No direct access');
class HTTP_Response_Exception extends Kohana_Exception {
public static function exception_handler(Exception $e)
{
if (Kohana::DEVELOPMENT === Kohana::$environment)
{
Kohana_Core::exception_handler($e);
}
else
{
Kohana::$log->add(Kohana::ERROR, Kohana::exception_text($e));
$attributes = array
(
'action' => 500,
'message' => rawurlencode($e->getMessage()),
);
if ($e instanceof HTTP_Response_Exception)
{
$attributes['action'] = $e->getCode();
}
// Error sub-request.
echo Request::factory(Route::url('error', $attributes))
->execute()
->send_headers()
->response;
}
}
}
kohana.php:
<?php defined('SYSPATH') or die('No direct script access.');
class Kohana extends Kohana_Core
{
/**
* Redirect to custom exception_handler
*/
public static function exception_handler(Exception $e)
{
Error::exception_handler($e);
}
} // End of Kohana
error_handler.php:
<?php defined('SYSPATH') or die('No direct access');
class Controller_Error_handler extends Controller {
public function before()
{
parent::before();
$this->template = View::factory('template/useradmin');
$this->template->content = View::factory('error');
$this->template->page = URL::site(rawurldecode(Request::$instance->uri));
// Internal request only!
if (Request::$instance !== Request::$current)
{
if ($message = rawurldecode($this->request->param('message')))
{
$this->template->message = $message;
}
}
else
{
$this->request->action = 404;
}
}
public function action_404()
{
$this->template->title = '404 Not Found';
// Here we check to see if a 404 came from our website. This allows the
// webmaster to find broken links and update them in a shorter amount of time.
if (isset ($_SERVER['HTTP_REFERER']) AND strstr($_SERVER['HTTP_REFERER'], $_SERVER['SERVER_NAME']) !== FALSE)
{
// Set a local flag so we can display different messages in our template.
$this->template->local = TRUE;
}
// HTTP Status code.
$this->request->status = 404;
}
public function action_503()
{
$this->template->title = 'Maintenance Mode';
$this->request->status = 503;
}
public function action_500()
{
$this->template->title = 'Internal Server Error';
$this->request->status = 500;
}
} // End of Error_handler
I really cannot see where I have done wrong. Thanks in advance for any help.
First of all, you need to make sure you are loading your module by including it in the modules section of your application/bootstrap.php file like so
Kohana::modules(array(
'my'=>MODPATH.'my'
)
);
The fact that you mentioned going directly to the url for your error handler controller triggers a 404 error makes me think your module has not been loaded.
I would also suggest a few more changes.
http_response_exception.php does not need to extend Kohana_Exception, since this class is not an exception, but an exception handler. Along those same lines, a more appropriate class name might be Exception_Handler, since the class is not representing an exception, but handling them. Secondly, because of how you've named this file, it should be located in modules/my/classes/http/response/exception.php. Other than that, the code for this class looks ok.
Similarly, because of how you've named your controller, it should be located and named a bit differently. Move it to modules/my/classes/controller/error/handler.php
Remember that underscores in a class name means a new directory, as per http://kohanaframework.org/3.2/guide/kohana/conventions
Finally, I don't think you really need to extend the Kohana_Core class here, but instead just register your own custom exception handler. You can register your custom exception handler in either your application's bootstrap file, or in your module's init file with the following generic code:
set_exception_handler(array('Exception_Handler_Class', 'handle_method'));
Here's a customer exception handler I use, which is pretty similar to yours:
<?php defined('SYSPATH') or die('No direct script access.');
class Exception_Handler {
public static function handle(Exception $e)
{
$exception_type = strtolower(get_class($e));
switch ($exception_type)
{
case 'http_exception_404':
$response = new Response;
$response->status(404);
$body = Request::factory('site/404')->execute()->body();
echo $response->body($body)->send_headers()->body();
return TRUE;
break;
default:
if (Kohana::$environment == Kohana::DEVELOPMENT)
{
return Kohana_Exception::handler($e);
}
else
{
Kohana::$log->add(Log::ERROR, Kohana_Exception::text($e));
$response = new Response;
$response->status(500);
$body = Request::factory('site/500')->execute()->body();
echo $response->body($body)->send_headers()->body();
return TRUE;
}
break;
}
}
}
You're using an outdated documentation. HTTP_Exception_404 was bundled in 3.1, and you're trying to implement a solution from 3.0.
See documentation for your version of Kohana for a solution that works.
All you need to do is set the path to a different view in your bootstrap.php add:
Kohana_Exception::$error_view = 'error/myErrorPage';
that will parse all the variables currently being parsed to the error page that lives in:
system/views/kohana/error.php
ie:
<h1>Oops [ <?= $code ?> ]</h1>
<span class="message"><?= html::chars($message) ?></span>
After a VERY LONG TIME of searching I finally found a solution to my little problem.
Here is a step by step tutorial on how to load your own custom error pages with Kohana 3.2:
Change the environment variable in the bootstrap.
Here you have multiple options:
a. Do what they say in the documentation of the bootstrap.php:
/**
* Set the environment status by the domain.
*/
if (strpos($_SERVER['HTTP_HOST'], 'kohanaphp.com') !== FALSE)
{
// We are live!
Kohana::$environment = Kohana::PRODUCTION;
// Turn off notices and strict errors
error_reporting(E_ALL ^ E_NOTICE ^ E_STRICT);
}
b. Or just add those two lines without the "if":
Kohana::$environment = Kohana::PRODUCTION;
error_reporting(E_ALL ^ E_NOTICE ^ E_STRICT);
c. I have not try this way but in the new bootstrap.php you have this code:
/**
* Set Kohana::$environment if a 'KOHANA_ENV' environment variable has been supplied.
*
* Note: If you supply an invalid environment name, a PHP warning will be thrown
* saying "Couldn't find constant Kohana::<INVALID_ENV_NAME>"
*/
if (isset($_SERVER['KOHANA_ENV']))
{
Kohana::$environment = constant('Kohana::'.strtoupper($_SERVER['KOHANA_ENV']));
}
I assume that you could just give the value "production" to "$_SERVER['KOHANA_ENV']" before those lines.
Again, like I said I haven't tried it, but it should work.
I personally just commented out those lines of codes.
2 Now you need to add a few configurations in a "ini.php" file, or in the "bootstra.php" file.
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Turn errors into exceptions.
*/
Kohana::$errors = true;
/**
* Custom exception handler.
*/
restore_exception_handler();
set_exception_handler(array('Exception_Handler', 'handler'));
/**
* Error route.
*/
Route::set('error', 'error/<action>(/<message>)', array('action' => '[0-9]++', 'message' => '.+'))
->defaults(array(
'controller' => 'exception_handler'
));
This is what was missing and made it to hard. For the rest you can easily just follow Kohana3.2 documentation or you can get the module that I added to a repo in GitHub: https://github.com/jnbdz/Kohana-error
Every underscore is a directory separator in a class name. So when naming your class Http_Response_Exception, the class should be in classes/http/response/exception.php. Otherwise the class will not be found by the autoloader of Kohana.
edit
Hmm, seems like the documentation is wrong in this aspect. classes/http_response_exception.php doesn't make sense.
I'm trying to access an FTP server from my PHP script using Codeigniter's FTP Library. These functions work great, but when testing the script I discovered that if I attempt to connect to a server that does not exist, the script does not terminate with an error message of any kind.
The page continues to execute, until the web server gives up, returning an empty document.
So I am wondering, is there a way to limit the amount of time that Codeigniter can try to connect to an FTP server, then display a message if that times out?
I tried using the php function set_time_limit(), but it does not behave how I expected it to.
Thanks for your help.
Codeigniter's ftp class uses the underlying ftp_connect php call that supports a 3rd optional parameter, timeout (http://ca2.php.net/manual/en/function.ftp-connect.php).
Codeigniter however does not use it, but allows for extending the default libraries it provides (providing that you're willing to do some work and check that any updates you do to the core will not break the functionality of your extended class). So to solve your problem you could create a new library in you application library folder:
<?php
class MY_FTP extends CI_FTP { //Assuming that in your config.php file, your subclass prefix is set to 'MY_' like so: $config['subclass_prefix'] = 'MY_';
var $timeout = 90;
/**
* FTP Connect
*
* #access public
* #param array the connection values
* #return bool
*/
function connect($config = array())
{
if (count($config) > 0)
{
$this->initialize($config);
}
if (FALSE === ($this->conn_id = ftp_connect($this->hostname, $this->port, $this->timeout)))
{
if ($this->debug == TRUE)
{
$this->_error('ftp_unable_to_connect');
}
return FALSE;
}
if ( ! $this->_login())
{
if ($this->debug == TRUE)
{
$this->_error('ftp_unable_to_login');
}
return FALSE;
}
// Set passive mode if needed
if ($this->passive == TRUE)
{
ftp_pasv($this->conn_id, TRUE);
}
return TRUE;
}
}
?>
and from your script, you could add to your configuration array the timeout option:
$this->load->library('ftp'); //if ftp is not autoloaded
$ftp_params = array('hostname'=>'1.2.3.4', 'port'=>21, 'timeout'=>10); //timout is 10 seconds instead of default 90
$ftp_conn = $this->ftp->connect($ftp_params);
if(FALSE === $ftp_conn) {
//Code to handle error
}
The ftp class is not designed to give error messages unless the debug parameter is set to TRUE in te config array, in which case it'll just display an error. However it can also be override, because all errors call the function _error() in the class. So you could set 'debug' => true in your $ftp_params array, and add a function in MY_ftp like so:
/**
* This function overrides
*/
function _error($line)
{
$this->error = $line;
}
And then have a function getError()
/**
* This function overrides
*/
function get_error()
{
return $this->error;
}
So if
$ftp_conn = $this->ftp->connect($ftp_params);
returns false, you can call
$error = $this->ftp->get_error();
to get your error and display it.
Now, you can always customize and have a more complex error handling mechanism by further customizing the class...
Hope it answers your question.
The answer is simple, don't attempt to connect to a server that doesn't exist.