Kohana/PHP Custom exception handling not working in Kohana 3.3 - php

How is it possible to replace the stock exception page(red and gray one) with the one i designed.
I followed the following steps
Step 1) Copied System/classes/Kohana/Exception.php to Application/classes/Kohana/Exception.php and added the following snippet
class Kohana_Exception extends Kohana_Kohana_Exception {
public static function handler(Exception $e) {
echo 'here'; die;
switch (get_class($e)) {
case 'HTTP_Exception_404':
echo 'here';
$this->HandleException(404, 'errors/404');
return TRUE;
break;
default:
return Kohana_Kohana_Exception::handler($e);
break;
}
}
I checked the Application/bootstrap.php and ensured the presence of following code
Kohana::init(array(
'base_url' => '/web/',
'index_file' => '',
'errors' => true,
'profile' => (Kohana::$environment == Kohana::DEVELOPMENT),
));
I just wanted to see the value 'here' getting displayed which i have given inside the handler method of Kohana_Exception, but the execution is never hitting that line.
I also noticed that giving a false value to the init(errors) is not turning off the stock exception page.

I was using outdated exception handling routines. In Kohana 3.3 one must use the method explained in the following link
http://kohanaframework.org/3.3/guide/kohana/tutorials/error-pages

Related

mockery with PHP 7 and return types

I'm using return type declaration and I update mockery to version 1.1 depends on this post but still, it's not working properly.
I have a factory with a method:
public function getScrapperByUrl($type):AppScrapperInterface
{
$this->validate($type);
switch ($type) {
case self::ITUNES:
return app(ITunesScrapper::class);
break;
case self::PLAYSTORE:
return app(PlayStoreScrapper::class);
break;
default:
throw new AppScraperException("Can't scrap info");
}
}
and in tests, I'm mocking behavior of ITunesScrapper:
$m = m::mock(ITunesScrapper::class);
$scrapedInfo = [
'name' => "comico",
'downloads' => ""
];
$m->shouldReceive('getOfferAnchor')->with(m::any())->andReturn($scrapedInfo['name']);
$m->shouldReceive('getOfferDownloads')->with(m::any())->andReturn($scrapedInfo['downloads']);
App::instance(ITunesScrapper::class, $m);
and I'm getting the error like this
TypeError: Return value of Scrappers\ScrapperFactory::getScrapperByUrl() must be an instance of Scrappers\AppScrapperInterface, instance of Mockery_2__Adgate_Components_AppstoreFetchers_Itunes_ITunesScrapper returned.
Am I doing something wrong or I need address this issue to the bug report?
This error can be solved by using the alias prefix with a valid class name. Like the following:
$m = m::mock('alias:ITunesScrapper');
More information can be found at the official documentation http://docs.mockery.io/en/latest/reference/creating_test_doubles.html#aliasing

Yii2 : How to Customize Error Pages like 404 and 503

I have following config of errorHandler
'errorHandler' => [
'errorAction' => 'page/error',
],
In the Controller page, in the Action error I want to check, that I got 404 error "page not found"?
How can I check it?
If you are trying to customize an Error page and want to get the errors code separately inside the view then you have the $exception,$name and $message variables available inside the view, but in case if you use yii\web\ErrorAction, before I go ahead you need to see in which category you fall.
CASE 1 Using \yii\web\ErrorAction
Inside your PageController you should have an actions() function like below.
public function actions() {
return [
'error' => [
'class' => 'yii\web\ErrorAction' ,
]
];
}
If you haven't created a separate layout for error add one now, it better to keep your error layout separate. Just copy the layouts/main.php and remove all extra CSS and js files or create frontend/assets/ErrorAsset.php and register on top of your layout file.
Add beforeAction() function inside your PageController like below.
Sample Code
public function beforeAction( $action ) {
if ( parent::beforeAction ( $action ) ) {
//change layout for error action after
//checking for the error action name
//so that the layout is set for errors only
if ( $action->id == 'error' ) {
$this->layout = 'error';
}
return true;
}
}
Now as you have specified the 'page/error' inside your errorHandler component's config so the action name would be error and so would be the view file, this view file should be inside the page folder this should be the path page/error.php. You have the $exception variable available which holds the exception object in your case yii\web\NotFoundHttpException Object. and you can call $exception->statusCode to check which status code has been thrown for the exception, in your case, it would show 404.
CASE 2 Using Custom Action for displaying Errors
Another Way is to use custom action inside the controller rather than using the yii\web\ErrorAction in that case you do not need to add the actions() function and inside your custom error function you should call
$exception = Yii::$app->getErrorHandler()->exception;
and use the $exception->statusCode. Make sure you check for the exact action name for inside your beforeAction() function change your check accordingly for the line
if ( $action->id == 'error' ) {
CASE 3 SHUTUP just Give me the Exception
If you don't want any of above and just want to check the Exception code inside the controller's beforeAction() you have to access the same exception object above but with a shorthand via config's erorHandler component.
public function beforeAction($action) {
$exception = Yii::$app->getErrorHandler()->exception;
if(parent::beforeAction($action)) {
$hasError = $action->id == 'error' && $exception !== NULL;
if($hasError) {
echo $exception->statusCode;
return false;
}
}
return true;
}

Yii2 Routing rules conflicts with each other

I have the following 2 rules:
'blog/<action>' => 'blog/default/<action>',
'blog/<slug:[0-9a-zA-Z\-.]+>' => 'blog/default/view',
Also I have the following actions:
public function actionCheckSlug($slug) {
}
public function actionCreate() {
}
public function actionView($slug) {
return $this->render("view");
}
When I try to access this URL for example (action URL):
/blog/check-slug?slug=test
It's working without any problems but when I try to access this URL for example (Slug URL):
/blog/test-test-test
I will get an error:
yii\base\InvalidRouteException: Unable to resolve the request: blog/default/test-test-test
Because the fist rules is being parsed instead of the second one.
I tried to reverse them for example but it didn't work (always one is not working), also tried others scenarios but no success
Any idea how to make it works?
i would suggest first of all not using the same url convention for both actions
'blog/<action>' => 'blog/default/<action>',
'blog/<slug:[0-9a-zA-Z\-.]+>' => 'blog/default/view',
could easily become
'blog/<action>' => 'blog/default/<action>',
'post/<slug:[0-9a-zA-Z\-.]+>' => 'blog/default/view',
or use 'blog/page-<slug:[0-9a-zA-Z\-.]+>', blog/post/ .. or really just any convention that doesn't clash with your existing structure
if that's not something just you wanna do, or cant? in your app, you can just use the slug to check for existing app structure.
public function view($slug){
$model = $this->findBySlug($slug);
return $this->render('view', ['model' => $model]);
}
private function findBySlug($slug){
if ($this->hasMethod('action' . Inflector::classify($slug))
// this should prevent recursion
&& $slug != $this->action->id){
$this->runAction($slug);
return null;
}
return Post::find()->where(['slug' => $slug])
}
note: this is just an example of how to (or how not to?). don't run my bad, untested code in any production environment

Overriding Kohana_Exception::_handler() for Production - 3.3

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...

How do I catch a HTTP_Exception_404 Error in Kohana

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.

Categories