Php Custom Exceptions with error_log() - php

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.

Related

How to change PHPUnit error messages to show custom errors

I have extended PHP Exception with my own to add additional data:
class MyException extends Exception {
public $foo = [];
public function __construct($message = '', $data = null, $code = 0) {
$this->foo = $data;
paret::__construct($message, $code);
}
}
When doing normal requests these errors logs correctly and I do not want to add any additional content to $this->message.
When running test I can throw it:
if (!$this->save()) {
throw new MyException('Internal Server Error', ['log' => 'missing data'], 500);
}
and PHPUnit will output:
MyException: Internal Server Error
I want:
MyExeption: Internal Server Error; {"log": "missing data"}
How to extend PHPUnit to be able to show $myException->foo along with error message?
Sample code:
<?php
class SampleTest extends CTestCase
{
public function testIndex()
{
$this->assertTrue($this->save());
}
protected function save()
{
$model = new Orders();
$model->addError('id', 'ID is Required');
if (!$model->validate()) {
throw new HttpApiModelException(500, 'Failed to save', $model);
}
return true;
}
}
Executed with command common/vendor/bin/phpunit --configuration tests/phpunit.xml --verbose tests/functional/SampleTest.php
And output:
I am not sure if this is the best option but you could implement test results printer, smth like:
<?php
namespace Tests;
use PHPUnit\TextUI\ResultPrinter;
class TestPrinter extends ResultPrinter
{
protected function printDefect(\PHPUnit\Framework\TestFailure $defect, $count)
{
$this->printDefectHeader($defect, $count);
$ex = $defect->thrownException();
// you can do whatever you need here,
// like check exception type, etc,
// printing just line number here
$this->write('Line #' . $ex->getLine() . "\n");
$this->printDefectTrace($defect);
}
}
and register as a printer to be used (assume xml config but could be done via command line as well):
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
printerClass="Tests\TestPrinter"
>
<!-- note printerClass attribute above -->
</phpunit>
Doing so, you would get output looking similar with:
There was 1 error:
1) Tests\SomeTest::testStuff
Line #16
LogicException: whatever
(I just made a simple test doing throw new \LogicException('whatever');)
So if you need this data to be printed everytime when you are running tests but not when it's on production then why not to extend the base Exception class and to check if you are under test environment then to concatenate the message and the data. And then all of your custom exceptions to extend this new Exception class.
class BaseException extends Exception {
public function __construct($message = '', $data = null, $code = 0) {
if (env('ENVIRONMENT') === 'test') {
$message .= ' ' . json_encode($data);
}
paret::__construct($message, $code);
}
}
But this implementation will require to change your MyException class to call the parent constructor with this data.
Instead of this
paret::__construct($message, $code);
Now you are going to have this
paret::__construct($message, $data, $code);
And also to extend the new BaseException class to these exception which you want to have this functionality

Returning useful error messages with PHP

I don't understand how to properly create and return useful error messages with PHP to the web.
I have a class
class Foo {
const OK_IT_WORKED = 0;
const ERR_IT_FAILED = 1;
const ERR_IT_TIMED_OUT = 3;
public function fooItUp(){
if(itFooed)
return OK_IT_WORKED;
elseif(itFooedUp)
return ERR_IT_FAILED;
elseif(itFooedOut)
return ERR_IT_TIMED_OUT;
}
}
And another class that uses this class to do something useful, then return the result to the user. I am just wondering where I put the string value for all my error messages.
class Bar {
public function doFooeyThings(stuff){
$res = $myFoo->fooItUp();
// now i need to tell the user what happened, but they don't understand error codes
if($res === Foo::OK_IT_WORKED)
return 'string result here? seems wrong';
elseif ($res === Foo::ERR_IT_FAILED)
return Foo::ERR_IT_FAILED_STRING; // seems redundant?
elseif($res === Foo:ERR_IT_TIMED_OUT)
return $res; // return number and have an "enum" in the client (js) ?
}
}
You should avoid returning error states whenever possible. Use exceptions instead. If you've never used exceptions before you can read about them here
There multiple ways you can utilize exceptions in your example. You could create custom exceptions for every error or for every category of error. More on custom exceptions here or you could create an instance of the default Exception class supplying it the error messages as strings.
The code below follows the second approach:
class Foo {
const OK_IT_WORKED = 0;
const ERR_IT_FAILED = 1;
const ERR_IT_TIMED_OUT = 3;
public function fooItUp(){
if(itFooed)
return OK_IT_WORKED;
else if(itFooedUp)
throw new Exception("It failed")
else if(itFooedOut)
throw new Exception("Request timed out");
}
}
I'm sure you can think of some more elegant messages than the ones I used. Anyway, you can then go ahead and handle those exceptions on the caller method using try/catch blocks:
class Bar {
public function doFooeyThings(stuff){
try
{
$res = myFoo->fooItUp();
}
catch(Exception $e)
{
//do something with the error message
}
}
}
Whatever exception is thrown from fooItUp will be "caught" by the catch block and handled by your code.
Two things you should also consider are:
It's best not to show your users detailed information about errors because those information could be used by users with malicious intent
Ideally you should have some kind of global exception handling
One solution is to use exceptions in conjunction with set_exception_handler().
<?php
set_exception_handler(function($e) {
echo "Error encountered: {$e->getMessage()}";
});
class ErrorMessageTest
{
public function isOk()
{
echo "This works okay. ";
}
public function isNotOkay()
{
echo "This will not work. ";
throw new RuntimeException("Violets are red, roses are blue!! Wha!?!?");
}
}
$test = new ErrorMessageTest();
$test->isOk();
$test->isNotOkay();
The set_exception_handler() method takes a callable that will accept an exception as its parameter. This let's you provide your own logic for a thrown exception in the event it isn't caught in a try/catch.
Live Demo
See also: set_exception_handler() documentation

Show Exception message when passing invalid argument type

I have a function
<?php
public function setStatusToReadyToShip(array $order_item_ids, $delivery_type, $shipping_provider = '', $tracking_number = '')
{
//code
}
Now I want the caller to call this method by providing $order_item_ids in array format.
One way i could do is to check is_array($order_item_ids) and send the error to the caller. But i am to utilize Type Hinting. How would i send the user the error response when using type hinting. As currently it crashes the application and says
exception 'ErrorException' with message 'Argument 1 passed to Class::SetStatusToReadyToShip() must be of the type array, integer given
I am not familiar with the try catch stuff that much. I tried placing try-catch inside this function so that it does not crash the application but the same output received.
Thanks
It is Catchable fatal error with constant E_RECOVERABLE_ERROR
There are 2 methods to solve this problem (at least two).
Create your own exception handler and set it as main error handler.
Make type validation in your function, and throw exception from it:
function setStatusToReadyToShip($order_item_ids, $delivery_type, $shipping_provider = '', $tracking_number = '')
{
if (!is_array($order_item_ids)) {
throw new InvalidArgumentException('First parameter of function '.__FUNCTION__.' must be array');
}
// your code
}
and than catch it
try {
setStatusToReadyToShip(5, 6);
} catch(InvalidArgumentException $e) {
var_dump($e->getMessage());
}
<?php
class YourClass {
public static function _init() {
set_error_handler('YourClass::_typeHintHandler');
}
public static function _typeHintHandler($errorCode, $errorMessage) {
throw new InvalidArgumentException($errorMessage);
}
public function setStatusToReadyToShip(array $order_item_ids, $delivery_type, $shipping_provider = '', $tracking_number = '')
{
}
}
YourClass::_init();
$yo = new YourClass();
try {
$yo->setStatusToReadyToShip("test");
}catch(Exception $e) {
print $e->getMessage();
}

How do I recognize if it's an error code or just an int?

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

Handling an exception, and only executing code if an exception was not thrown

My script_a.php:
try {
Class1::tryThis();
}
catch (Exception $e) {
// Do stuff here to show the user an error occurred
}
Class1::tryThis() has something like:
public function tryThis() {
Class2::tryThat();
self::logSuccessfulEvent();
}
The problem is that Class2::tryThat() can throw an exception.
If it does throw an exception, it seems that the line self::logSuccessfulEvent(); still gets executed.
How can I refactor this code so that self::logSuccessfulEvent() only occurs when an exception is not thrown, yet at the same time letting script_a.php know when an exception has been thrown?
This function will return whether or not the operation was successful (true = success, false = failure)
public function tryThis() {
$success = true;
try {
Class2::tryThat();
self::logSuccessfulEvent();
} catch( Exception $e) {
$success = false;
}
return $success;
}
What you're describing does not seem to be the case.
Code:
<?php
class Class1 {
public function tryThis() {
echo "Class1::tryThis() was called.\n";
Class2::tryThat();
self::logSuccessfulEvent();
}
public function logSuccessfulEvent() {
echo "Class1::logSuccessfulEvent() was called.\n";
}
}
class Class2 {
public function tryThat() {
echo "Class2::tryThat() was called.\n";
throw new Exception('Exception generated in Class2::tryThat()');
}
}
try {
Class1::tryThis();
} catch (Exception $e) {
echo $e->getMessage(), "\n";
}
Output:
Class1::tryThis() was called.
Class2::tryThat() was called.
Exception generated in Class2::tryThat()
As you can see, the Class1::logSuccessfulEvent() method is never executed when an exception is generated in Class2::tryThat(), and it shouldn't (won't) either. Exceptions bubble up until they are caught or produce a fatal error. Once an exception is caught, control of the program returns to the code after the catch block. In this particular case, that would mean that control of the program never reaches the logging method.

Categories