Is there a way to make CI throw an exception when it encounters a DB error instead of displaying a message like:
A Database Error Occurred Error Number: 1054
Unknown column 'foo' in 'where clause' SELECT * FROM (FooBar) WHERE foo = '1'
NOTE: I only want this to happen in one controller. In the other controllers, I'm happy for it to display the DB error messages.
Use error() method:
$this->db->error();
For CodeIgniter 2, you can use the following functions which are now deprecated:
$this->db->_error_message(); (mysql_error equivalent)
$this->db->_error_number(); (mysql_errno equivalent)
Maybe this:
$db_debug = $this->db->db_debug; //save setting
$this->db->db_debug = FALSE; //disable debugging for queries
$result = $this->db->query($sql); //run query
//check for errors, etc
$this->db->db_debug = $db_debug; //restore setting
In Codeigniter 3.0 (CI3), all you have to do is $this->db->error()
If you need to get the last error that has occured, the error() method will return an array containing its code and message
http://www.codeigniter.com/user_guide/database/queries.html#handling-errors
You must turn debug off for database in config/database.php ->
$db['default']['db_debug'] = FALSE;
It is better for your website security.
I know this thread is old, but just in case there's someone else having this issue. This is a trick I used without touching the CI db classes. Leave your debug on and in your error view file, throw an exception.
So in you db config, you have :
$db['default']['db_debug'] = true;
Then in your db error view file, mine is in application/errors/error_db.php replace all content with the following:
<?php
$message = preg_replace('/(<\/?p>)+/', ' ', $message);
throw new Exception("Database error occured with message : {$message}");
?>
Since the view file will be called, the error will always get thrown as an exception, you may later add different views for different environment.
An example that worked for me:
$query = "some buggy sql statement";
$this->db->db_debug = false;
if(!#$this->db->query($query))
{
$error = $this->db->error();
// do something in error case
}else{
// do something in success case
}
...
I have created an simple library for that:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class exceptions {
public function checkForError() {
get_instance()->load->database();
$error = get_instance()->db->error();
if ($error['code'])
throw new MySQLException($error);
}
}
abstract class UserException extends Exception {
public abstract function getUserMessage();
}
class MySQLException extends UserException {
private $errorNumber;
private $errorMessage;
public function __construct(array $error) {
$this->errorNumber = "Error Code(" . $error['code'] . ")";
$this->errorMessage = $error['message'];
}
public function getUserMessage() {
return array(
"error" => array (
"code" => $this->errorNumber,
"message" => $this->errorMessage
)
);
}
}
The example query:
function insertId($id){
$data = array(
'id' => $id,
);
$this->db->insert('test', $data);
$this->exceptions->checkForError();
return $this->db->insert_id();
}
And I can catch it this way in my controller:
try {
$this->insertThings->insertId("1");
} catch (UserException $error){
//do whatever you want when there is an mysql error
}
Put this code in a file called MY_Exceptions.php in application/core folder:
<?php
if (!defined('BASEPATH'))
exit('No direct script access allowed');
/**
* Class dealing with errors as exceptions
*/
class MY_Exceptions extends CI_Exceptions
{
/**
* Force exception throwing on erros
*/
public function show_error($heading, $message, $template = 'error_general', $status_code = 500)
{
set_status_header($status_code);
$message = implode(" / ", (!is_array($message)) ? array($message) : $message);
throw new CiError($message);
}
}
/**
* Captured error from Code Igniter
*/
class CiError extends Exception
{
}
It will make all the Code Igniter errors to be treated as Exception (CiError). Then, turn all your database debug on:
$db['default']['db_debug'] = true;
Use it
$this->db->_error_message();
It is better for finding error.After completing your site.
Close the error messages
using it
$db['default']['db_debug'] = FALSE;
You will change it in your config folder's database.php
Disable debugging of errors.
$data_user = $this->getDataUser();
$id_user = $this->getId_user();
$this->db->db_debug = false;
$this->db->where(['id' => $id_user]);
$res = $this->db->update(self::$table, $data_user['user']);
if(!$res)
{
$error = $this->db->error();
return $error;
//return array $error['code'] & $error['message']
}
else
{
return 1;
}
If one uses PDO, additional to all the answers above.
I log my errors silently as below
$q = $this->db->conn_id->prepare($query);
if($q instanceof PDOStatement) {
// go on with bind values and execute
} else {
$dbError = $this->db->error();
$this->Logger_model->logError('Db Error', date('Y-m-d H:i:s'), __METHOD__.' Line '.__LINE__, 'Code: '.$dbError['code'].' - '.'Message: '.$dbError['message']);
}
In sybase_driver.php
/**
* Manejador de Mensajes de Error Sybase
* Autor: Isaí Moreno
* Fecha: 06/Nov/2019
*/
static $CODE_ERROR_SYBASE;
public static function SetCodeErrorSybase($Code) {
if ($Code != 3621) { /*No se toma en cuenta el código de command aborted*/
CI_DB_sybase_driver::$CODE_ERROR_SYBASE = trim(CI_DB_sybase_driver::$CODE_ERROR_SYBASE.' '.$Code);
}
}
public static function GetCodeErrorSybase() {
return CI_DB_sybase_driver::$CODE_ERROR_SYBASE;
}
public static function msg_handler($msgnumber, $severity, $state, $line, $text)
{
log_message('info', 'CI_DB_sybase_driver - CODE ERROR ['.$msgnumber.'] Mensaje - '.$text);
CI_DB_sybase_driver::SetCodeErrorSybase($msgnumber);
}
// ------------------------------------------------------------------------
Add and modify the following methods in the same sybase_driver.php file
/**
* The error message number
*
* #access private
* #return integer
*/
function _error_number()
{
// Are error numbers supported?
return CI_DB_sybase_driver::GetCodeErrorSybase();
}
function _sybase_set_message_handler()
{
// Are error numbers supported?
return sybase_set_message_handler('CI_DB_sybase_driver::msg_handler');
}
Implement in the function of a controller.
public function Eliminar_DUPLA(){
if($this->session->userdata($this->config->item('mycfg_session_object_name'))){
//***/
$Operacion_Borrado_Exitosa=false;
$this->db->trans_begin();
$this->db->_sybase_set_message_handler(); <<<<<------- Activar Manejador de errores de sybase
$Dupla_Eliminada=$this->Mi_Modelo->QUERY_Eliminar_Dupla($PARAMETROS);
if ($Dupla_Eliminada){
$this->db->trans_commit();
MostrarNotificacion("Se eliminó DUPLA exitosamente","OK",true);
$Operacion_Borrado_Exitosa=true;
}else{
$Error = $this->db->_error_number(); <<<<----- Obtengo el código de error de sybase para personilzar mensaje al usuario
$this->db->trans_rollback();
MostrarNotificacion("Ocurrio un error al intentar eliminar Dupla","Error",true);
if ($Error == 547) {
MostrarNotificacion("<strong>Código de error :[".$Error.']. No se puede eliminar documento Padre.</strong>',"Error",true);
} else {
MostrarNotificacion("<strong>Código de Error :[".$Error.']</strong><br>',"Error",true);
}
}
echo "#".Obtener_Contador_Notificaciones();
if ($Operacion_Borrado_Exitosa){
echo "#T";
}else{
echo "#F";
}
}else{
redirect($this->router->default_controller);
}
}
In the log you can check the codes and messages sent by the database server.
INFO - 2019-11-06 19:26:33 -> CI_DB_sybase_driver - CODE ERROR [547] Message - Dependent foreign key constraint violation in a referential integrity constraint. dbname = 'database', table name = 'mitabla', constraint name = 'FK_SR_RELAC_REFERENCE_SR_mitabla'. INFO - 2019-11-06 19:26:33 -> CI_DB_sybase_driver - CODE ERROR [3621] Message - Command has been aborted. ERROR - 2019-11-06 19:26:33 -> Query error: - Invalid query: delete from mitabla where ID = 1019.
Related
I learned that I can define my error codes like this:
class Hello
{
/** My own error codes */
const OK = 0;
const ERROR = 1;
const OTHER = 2;
function Test()
{
/** Return the error code if an error was occurred */
if(an_error_occurred)
return self::ERROR;
/** Simulate some simple result. */
return rand(0, 10);
}
}
but I have some trouble about this:
if($Hello->Test() == Hello::ERROR)
exit('Something happened.');
It'll still exit even there's no error occurred but the value which $Hello->Test() gave equals 1,
how do I solve this problem?
Or there's an better way to define my own error code?
You shouldn't mix meanings for return values. As others have mentioned, Exceptions are pretty much made for this.
class Hello
{
function Test()
{
/** Return the error code if an error was occurred */
if (an_error_occurred)
throw new Exception("An error occurred.");
/** Simulate some simple result. */
return rand(0, 10);
}
}
And when calling it, you'd do this:
try {
$foo = $hello->Test();
}
catch(Exception $e) {
echo "There was a problem: " . $e->getMessage();
}
The code inside the catch block only executes if the exception is thrown inside the Test method. You can create your own exception types to customize this further for different types of errors.
Check it out: http://php.net/manual/en/language.exceptions.php
Am getting Integrity constraint violation – yii\db\IntegrityException Error when am delete a row without deleting a row in foreign key table.
How i can show custom Error Page instead of Integrity constraint violation – yii\db\IntegrityException Error in Yii 2
How i can catch and throw exception for this in Yii 2.0
This will not actually the answer you have asked but I have customized error handler in Yii1, and hope it have same behavior in Yii2. You can change it on config/main.php as:
'errorHandler' => [
'errorAction' => 'site/error' // To error/errorHandler
],
And the Controller Class error have the following lines. You can change it on your need.
class ErrorController extends CController {
public $layout = '//layouts/column1';
/**
* This is the action to handle external exceptions.
*/
public function actionErrorHandler() {
if ($error = Yii::app()->errorHandler->error) {
if ('CDbException' != $error['type']) {
if (Yii::app()->request->isAjaxRequest)
echo $error['message'];
else
$this->commonError($error);
}else if ('CDbException' == $error['type']) {
if (Yii::app()->request->isAjaxRequest)
echo $error['message'] = 'The system is unable to resolve the database error !';
else
$this->databaseError($error);
}
}
}
/**
*
* #param type array $error
* #access : Internal, type private
* #throws: Http exception.
*/
private function commonError($error) {
$this->render('code_error', $error);
}
/**
*
* #param type array $error
* #throws Exception
*/
private function databaseError($error) {
if (empty($error)) {
$error['code'] = '404';
$error['message'] = 'Unknown error !';
$this->render('code_error', $error);
Yii::app()->end(0, true);
}
$error['message'] = 'The system is unable to resolve the database error !';
$this->render('database_error', $error);
}
}
I am testing an Custom Exception class LoggedException responsible for logging the message.
But its not working as expected, my directory structure for the log file is logs/exceptions.log
I have implemented certain checks for the existence of file and even the check for the implementation of error_log(), which tells the file is written but when I open exceptions.log there is nothing in there and the message I want to write is which thrown.
class LoggedException extends exception{
public function __construct($message,$code=0,$file='logs/exceptions.log')
{
if(file_exists($file)){
if(error_log($this->getMessage(),0,$file)){
echo "<br/>File Wirtten error message: ".$this->getMessage();
} else {
echo"<br/>cannot write";
}
} else {
echo "</br>No such file there";
}
}
}
class someClass{
private $prop="on";
public function checkState($device){
if(($this->prop)=="on"){
throw new LoggedException("The $device is ON");
}
else{
echo ("THE $device IS OFF");
}
}
}
$bulb=new SomeClass();
try{
$bulb->checkState("bulb");
}
catch(LoggedException $e){
echo "Exception message: ".$e->getMessage();
}
Browser Display:
exceptions.log:(also not complete)
Check the manual for the proper way to extend a class Extending Exceptions
First:
You are missing a parameter on your class constructor
public function __construct($message,$code=0,$file='logs/exceptions.log')
The third parameter should be Exception $previous = null like this:-
public function __construct($message, $code = 0,
Exception $previous = null,
$file='./logs/exceptions.log')
Second:
you do not call parent::__construct($message, $code, $previous); before doing your own code in the __construct method of your class.
So the Exception class is not initialised properly, but you attempt to use its properties, which of course are not yet loaded.
Third:
with the option set to 0 in the error_log() function call, the message will go to the standard PHP error log and not the one you intended.
So change 0 to 3 and it will go to the file you specify in param 3.
Fourth:
You need to add your own Newlines manually to the messages written to the log.
Change your code to
class LoggedException extends Exception{
public function __construct($message, $code = 0, Exception $previous = null, $file='./logs/exceptions.log')
{
parent::__construct($message, $code, $previous);
if(file_exists($file)){
if(error_log($this->getMessage() . PHP_EOL, 3, $file)){
echo "<br/>File Wirtten error message: ".$this->getMessage().PHP_EOL;
} else {
echo"<br/>cannot write";
}
} else {
echo "</br>No such file there";
}
}
}
And with any luck it will do roughly what you intended.
I have a problem with the accessing of classes.
index.php
include('includes/header.php');
include('includes/step1.php');
include('includes/footer.php');
header.php
session_start();
include('php/classes/Errorhandler.php');
include('php/classes/User.php');
$errorhandler = new Errorhandler();
$user = new User();
// Test
print_r($errorhandler->errors);
...html
Errorhandler.php
class Errorhandler {
public $errors = array();
...
}
User.php
class User {
public function __construct() {
if($this->grab_computerid()) {
if(!$this->grab_mandant()) {
$errorhandler->errors[] = "102: There was an error.";
}
if(!$this->grab_os()) {
$errorhandler->errors[] = "103: There was an error.";
}
} else {
$errorhandler->errors[] = "101: There was an error.";
}
}
private function grab_computerid() {
$sqlconnection = new SqlConnection();
$conn = $sqlconnection->db_connect();
if ($conn) {
$query = "SELECT Computer_Idn FROM " . DB_PC_TABLE . " WHERE DeviceName = ?";
$params = array($this->get_hostname());
if ($sqlconnection->query($query, $params)) {
$computer_id = $sqlconnection->fetchRow();
$this->set_computer_id($computer_id['Computer_Idn']);
return true;
echo "Test";
} else {
$errorhandler->errors[] = "Statement error occurred.";
}
} else {
$errorhandler->errors[] = "Can't connect to database.";
// test
print_r($errorhandler->errors);
}
$sqlconnection->db_disconnect();
}
}
The index.php includes the relevant sections to build the site. In header.php I create two objects (1. errorhandler, 2. user). The User class check the return (boolean) from the sqlconnection. I know, that I use the wrong password and get a false. So the if ($conn) prints the print_r($errorhandler->errors) correctly. But when I want to show the errors in step1.php, the array errors is empty.
step1.php
// show negative messages
if ($errorhandler->errors) {
foreach ($errorhandler->errors as $error) {
echo '<div class="alert alert-danger message"><strong>Error: </strong>' . $error . '</div>';
}
}
I tested it in header.php also and the errors array is empty too. So, the errors array is only filled in the User.php class, but I want to display the erros in step1.php. Is there a problem with the includes?
Edit:
Hope to make it clearer:
header.php
// load the class Errorhandler
require_once('php/classes/Errorhandler.php');
// load the class User
require_once('php/classes/User.php');
// create errorhandler object
$errorhandler = new Errorhandler();
// create user object
$user = new User();
print_r($errorhandler->errors);
I set an error in class User:
$errorhandler->errors[] = "Can't connect to database.";
The array $errorhandler->errors is empty in header.php.
You have to call a function. You cannot get value directly.
1st->$errorhandler = new Errorhandler();
2nd->Errorhandler.php
class Errorhandler {
function error() //create a function like this
{
$error= /*any error*/;
return $error;
}
}
3rd->
if ($err=$errorhandler->error()) {
foreach ($err as $error) {
echo '<div class="alert alert-danger message"><strong>Error:</strong>'.$error.'</div>';
}
}
Also try using require_once() instead of include();
Try using require_once instead of include. require_once will throw an error when something goes wrong and kill the script. These errors should appear in an error_log file in the same folder as the file which has the include in it. include will only issue a warning and let the script continue. I personally use require_once everywhere. Also, double check your include paths.
You should pass $errorhandler to User class.
Like this:
$user = new User($errorhandler);
And in User class:
class User {
protected $err;
public function __construct($errorhandler) {
$this->err = $errorhandler;
//rest of your code
}
//rest of your code
}
or simply add: global $errorhandler; inside User constructor.
How can this be done? I'm trying to do this for about half an hour and it's getting pretty annoying. You would this this should be an basic and easy thing to setup for a framework like this. I hope maybe there's an easy way i missed, because i'm starting to thing i should not chose this framework at all if such basic tings are so hard to setup.
This is in my bootstrap.php file that should do the trick.
if ( ! defined('SUPPRESS_REQUEST'))
{
/**
* Execute the main request. A source of the URI can be passed, eg: $_SERVER['PATH_INFO'].
* If no source is specified, the URI will be automatically detected.
*/
$request = Request::instance();
try
{
// Attempt to execute the response
$request->execute();
}
catch (Exception $e)
{
if (Kohana::$environment === Kohana::DEVELOPMENT)
{
// Just re-throw the exception
throw $e;
}
echo "ok";
// Create a 404 response
$request->status = 404;
$view = View::factory('error404');
$request->response = $view->render();
}
echo $request->send_headers()->response;
}
But i'm still getting
Fatal error: Uncaught Kohana_Request_Exception [ 0 ]: Unable to find a route to match the URI: test ~ SYSPATH\classes\kohana\request.php [ 674 ] thrown in C:\Xampp\htdocs\system\classes\kohana\request.php on line 674
instead of my custom 404 page.
And yes, Kohana::$environment is set to Kohana::PRODUCTION;
It doesn't even get to the echo "ok"; part. Why doesn't the exception get caught?
Replace the last line of bootstrap.php with:
/**
* Set the production status
*/
define('IN_PRODUCTION', FALSE);
/**
* Execute the main request. A source of the URI can be passed, eg: $_SERVER['PATH_INFO'].
* If no source is specified, the URI will be automatically detected.
*/
$request = Request::instance();
try
{
$request->execute();
}
catch (Kohana_Exception404 $e)
{
$request = Request::factory('error/404')->execute();
}
catch (Kohana_Exception403 $e)
{
$request = Request::factory('error/403')->execute();
}
catch (ReflectionException $e)
{
$request = Request::factory('error/404')->execute();
}
catch (Kohana_Request_Exception $e)
{
$request = Request::factory('error/404')->execute();
}
catch (Exception $e)
{
if ( ! IN_PRODUCTION )
{
throw $e;
}
$request = Request::factory('error/500')->execute();
}
echo $request->send_headers()->response;
Create new controller "error.php":
<?php defined('SYSPATH') or die('No direct script access.');
class Controller_Error extends Controller {
public function action_404()
{
$this->request->status = 404;
$this->request->headers['HTTP/1.1'] = '404';
$this->request->response = 'error 404';
}
public function action_403()
{
$this->request->status = 403;
$this->request->headers['HTTP/1.1'] = '403';
$this->request->response = 'error 403';
}
public function action_500()
{
$this->request->status = 500;
$this->request->headers['HTTP/1.1'] = '500';
$this->request->response = 'error 500';
}
} // End Error
Create two files (exception404.php и exception403.php) in "kohana" folder:
<?php defined('SYSPATH') or die('No direct access');
class Kohana_Exception403 extends Kohana_Exception {
public function __construct($message = 'error 403', array $variables = NULL, $code = 0)
{
parent::__construct($message, $variables, $code);
}
} // End Kohana_Exception 403
<?php defined('SYSPATH') or die('No direct access');
class Kohana_Exception404 extends Kohana_Exception {
public function __construct($message = 'error 404', array $variables = NULL, $code = 0)
{
parent::__construct($message, $variables, $code);
}
} // End Kohana_Exception 404
Now you can manually throw 404 and 403 errors (you can't throw error 500 ;)
throw new Kohana_Exception404;
throw new Kohana_Exception403;
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>
Since v3.1 the Kohana guide incldues a tutorial on custom error pages which show a much cleaner way of solving this question.