Where to log database errors in MVC Architecture - php

Or any framework for that matter.
Using Zend Framework 2 as an example, I have the following table class:
<?php
namespace Contact\Model;
use Zend\Db\TableGateway\TableGateway;
use Zend\Db\TableGateway\AbstractTableGateway;
use Zend\Log\Logger;
class UserContactsTable extends AbstractTableGateway
{
protected $tableGateway;
/**
*
* #var \Zend\Log\Logger Instance
*/
protected $logger;
public function __construct(TableGateway $tableGateway, Logger $logger )
{
$this->tableGateway = $tableGateway;
$this->logger = $logger;
}
/**
* Save a contact
*
* #param \Sms\Model\UserContact $userContact
*/
public function saveUserContact(UserContact $userContact)
{
$data = array(
'user_id' => $userContact->user_id,
'contact_id' => $userContact->contact_id
);
try {
$this->tableGateway->insert($data);
} catch (\Exception $e) {
//log
$this->logger->crit($omeErrMsg);
}
}
}
?>
Should I be logging here? Should I tie my logger in to the table class?
Should I let the saveUserContact function throw an exception if insert fails and catch in the controller and log there?
What are the best practises?
My original idea was to create a class with some constant error messages, such as insert and update failures to be used in the table class by the logger, but I'm not sure what is the correct process here.
This is not really limited to PHP or Zend Framework 2 but just so happens to be the language I am using.

I'd be of the opinion that individual components of a system should be as decoupled as possible. So in this example, if saveUserContact happens to fail then it should probably result in an exception being thrown because this isn't the expected behaviour. This class doesn't need to know about what will happen 'further up the chain', such as error logging.
As you mentioned, it would be better to throw the exception and catch it in your controller (or some other form of listener perhaps), which would then handle the logging.
The benefit of such an approach is that your system will be much easier to test because you'll have less objects to stub when constructing your UserContactsTable (mock) object to test.

Generally, i feel like you should log failures where they happen (unless they're expected, in which case that's noisy) but propagate an exception up the stack (or a wrapper exception) so the caller can decide whether to ignore/retry/fail (and log its own, more business-logic-relevant message).

Related

How to make PHPStan understand exception handling for child methods?

TL;DR
My questions being :
Is it a good practice to have exceptions inside an overriden methods ?
If so, how should I document and handle exceptions so that PHPStan doesn't block my pipelines ?
If not, how is the best way(s) to handle this kind of cases ?
Full context
I have some trouble regarding Handling Exception with PHPStan.
I have this abstract class :
abstract class GenericClass {
final public function handle(): void {
$this->myMethod();
}
abstract protected function myMethod(): void;
}
and this child class :
class MyClass extends GenericClass {
/**
* #throws MyException
**/
protected function myMethod(): void
{
throw new MyException();
}
}
Finally, I inject this child class inside a service class :
class ServiceClass {
private MyClass $myClass;
protected function __construct(MyClass $myClass)
{
$this->myClass = $myClass;
}
public function handle(): void
{
try {
$this->myClass->handle();
} catch(MyException $e) {
//error handling here
}
}
}
Obviously, abstract method myMethod cannot guess what kind of exception will be thrown (if any). It all a matter of context. Only the calling Service with the corresponding class can know the context in which the exception is thrown.
However, when PHPStan run through this code, it says that my catch is a dead code because it's never thrown.
This is a simple case with not many exception thrown but on a larger scale with multiple child classes and different exceptions thrown on these overriden methods, it could be time-consuming to update PHPDoc on parent method.
How do I make PHPStan understand that it is not dead code.
I only want to add #phpstan-ignore-next-line if no other solutions is possible.
Thanks in advance for all your replies

How to neatly handle Exceptions in Artisan Commands

Using Lumen to create an API - love Laravel but all the View's that come with it were overkill for the project I am creating.
Anyway, I've made a series of Commands which go out and collect data and stores it to the database.
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use App\User;
class GetItems extends Command {
/**
* The console command name.
*
* #var string
*/
protected $name = 'GetItems';
/**
* The console command description.
*
* #var string
*/
protected $description = "Get items and store it into the Database";
/**
* Execute the console command.
*
* #return void
*/
public function fire()
{
$this->info("Collecting ...");
$users = User::all();
foreach( $users as $user)
{
$user->getItems();
}
}
/**
* Get the console command options.
*
* #return array
*/
protected function getOptions()
{
return [];
}
}
I've got 3 similar commands, each one collecting slightly different datasets.
Is there a way I can inject a middle-layer that catches an exception that comes from each of the fire() functions across my Commands? I was thinking of extending the Command Class - but wanted to see if there's already a way to do it that's recommended by the Framework creators (documentation/searching was no help).
I know the alternative would be to combine all the commands into one file and use options, but this makes it messy and harder to collaborate with.
Any suggestions?
The answer depends on what we want the application to do when the command throws an exception. The question doesn't describe a desired way to handle the exception, so let's look at a few options.
Laravel and Lumen projects include a central exception Handler class that we can use to define behaviors for different exceptions. This class handles any exceptions that bubble up from web requests and console commands.
Laravel uses the report() method in app/Exceptions/Handler.php to determine how to log an exception. We can add logic here for error reporting:
public function report(Exception $e)
{
if ($e instanceof CustomConsoleException) {
// do something specific...
}
...
}
The renderForConsole() method lets us customize how we want to display error and exception messages for console commands. The project's exception Handler usually doesn't contain this method definition, but we can override it in app/Exceptions/Handler.php if needed:
public function renderForConsole($output, Exception $e)
{
$output->writeln('Something broke!');
(new ConsoleApplication)->renderException($e, $output);
}
In the example above, $output is a reference to a Symfony\Component\Console\Output \OutputInterface object that we can use to write text to the console command's output streams.
As we might guess from above, the central exception handler is designed to deal with uncaught exceptions that our code doesn't handle at a lower level, so it's not very useful when we need to execute some specific action after an exception. In a similar fashion, we could override the reportException() and renderException() methods in app/Console/Kernel.php.
If we need to do something specific besides just acknowledging that a command threw an exception by showing a message, we really should write this logic in the command itself. To avoid duplicate code, we could use an abstract class that the three similar commands provide concrete implementations for:
abstract class AbstractGetItems extends Command
{
...
final public function fire()
{
try {
$this->getItems();
} catch (Exception $e) {
// handle exception...
}
}
abstract protected function getItems();
}
This abstract command forces child classes to implement the getItems() method, which the class calls automatically in fire(). We can add any other shared logic to this class. The child commands need only to define their specific implementation of getItems(), and the parent class will handle exceptions for them:
class GetSpecificItems extends AbstractGetItems
{
...
protected function getItems()
{
// fetch specific items...
}
}

$this['key']->method() - what is this?

I'm practicing a little of PHP OO, I don't work actively with it, so I am sort of creating my own API to create websites. I'm a big fan of Laravel and I've been using it as reference to develop it and learn more of PHP and OO in the process, however now I've hit the wall.
I'm trying to build an Exception Handler in my "API" to handle 404 errors and such. I looked into Laravel and this is how you do it:
App::missing(function($exception)
{
return Response::view('errors.missing', array(), 404);
});
So, it's using a callback, and I went to see what Laravel was doing with it, and here is the thing: I can't understand how this works.
The code can be found here: https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/Application.php
I'll just put what I was looking into:
/**
* Register a 404 error handler.
*
* #param \Closure $callback
* #return void
*/
public function missing(Closure $callback)
{
$this->error(function(NotFoundHttpException $e) use ($callback)
{
return call_user_func($callback, $e);
});
}
So far I can understand, but then there is the $this->error(....); so let's see that:
/**
* Register an application error handler.
*
* #param \Closure $callback
* #return void
*/
public function error(Closure $callback)
{
$this['exception']->error($callback);
}
This I cannot understand. $this is an object, referencing itself, but it's referenced by an index, like in an array? And more, it's calling a method.
I'm sorry, but it's the first time I see this. How do you make the $this work as an array? And even further, call a method with it? I've been googling it around but I can't find a name for this, or what is it.
I believe, further in this class, there is something related to $this['exception']->error($callback);:
/**
* Register the core class aliases in the container.
*
* #return void
*/
public function registerCoreContainerAliases()
{
$aliases = array(
'...' => '...', // other stuff
'exception' => 'Illuminate\Contracts\Exception\Handler',
'...' => '...', // other stuff
);
foreach ($aliases as $key => $aliases)
{
foreach ((array) $aliases as $alias)
{
$this->alias($key, $alias);
}
}
}
But then, if I look at 'Illuminate\Contracts\Exception\Handler', it's just an interface. As far as I know, you implement an interface and then use the implementation, you don't call the interface directly. So that's kinda confusing too.
Link for the Handler class: https://github.com/laravel/framework/blob/master/src/Illuminate/Contracts/Exception/Handler.php
As you can see in the source code, Application extends Container which in turn implements ArrayAccess:
Interface to provide accessing objects as arrays.
Instances of a class implementing this interface can be accessed as if they were arrays, which is what happens in $this['exception'].
That is because Application class in which you have found the confusing line is inherited from Container.
Container class implements ArrayAccess interface which is used to provide accessing objects as arrays.

How to make Silex\Debug return JSON inside Silex App

I have a class that throws errors that extends from the base Exception.
The App's core is Silex Micro-Framework - but due to the fact that I am more fluent in Laravel than Symfony (despite Laravel being derived from it) I tend to use some packages from illuminate (hence the reason why you will see some use Illuminate\)
I want to throw an exception when an error occurs but I am getting the integrated Whoops instead. I would like to return JSON instead. (its not the full stack whoops but Symfony\component\debug instead)
Ideally array('status' => <code>, 'message_content');
Question is: a) how do i do it considering its silex with debug that looks like whoops (this service is on by default). b) is there a way to use my class that extends Exception bellow or am i in need of creating an entire new Exception class that suits my needs instead from scratch
namespace Api\Manager\Validation;
use Illuminate\Support\MessageBag;
class ValidationException extends \Exception {
/**
* #var MessageBag
*/
protected $errors;
/**
* #param string $message
* #param MessageBag $errors
*/
function __construct($message, MessageBag $errors)
{
$this->errors = $errors;
parent::__construct($message);
}
/**
* Get form validation errors
*
* #return MessageBag
*/
public function getErrors()
{
return $this->errors;
}
}
I'm assuming you're using the WhoopsServiceProvider to integrate into Silex - you should have the following in your app.php or index_dev.php.
$app->register(new WhoopsServiceProvider());
You can see the code for this here.
It appears to hardcode the PrettyPageHandler.
$app['whoops.error_page_handler'] = $app->share(function() {
return new PrettyPageHandler;
});
If you fork this provider/copy it into your own, then you should be able to replace this with the JsonRespondeHandler.
You will also need to remove some of the other code in the Provider. e.g. the whoops.silex_info_handler will not work for you as it includes some calls like addDataTable() which I think are specific to the PrettyPageHandler class.
$app['whoops'] = $app->share(function() use($app) {
$run = new Run;
$run->allowQuit(false);
$run->pushHandler($app['whoops.error_page_handler']);
$run->pushHandler($app['whoops.silex_info_handler']);
return $run;
});
Without actually testing the above I believe that should mean all your exceptions caught by Whoops will be rendered in JSON, so you won't (and shouldn't) need to create individual exception classes that render JSON specifically.
Update
It's actually a lot more straightforward than that. Just put the below in your index_dev.php after "$app = new Silex\Application();".
$app->error(function (\Exception $e, $code) use($app) {
return $app->json(array("error" => $e->getMessage()),$code);
});
Then exceptions will be output as JSON. The solution is actually from another SO question here.

Is it possible to define method with different parameters in a PHP interface?

I'm developing a service that is being injected a Logger object but I can have 2 different kind of loggers, I'm planning on having a syslog logger and a queue message system logger. Is this possible?
The idea is having an interface:
interface Loggable
{
public function log() ;
}
and 2 classes that implement that interface:
class Syslogger implements Loggable
{
public function log()
{
...
}
}
class QMSLogger implements Loggable
{
public function log($queueName)
{
...
}
}
The only way I could come with is having an array as a parameter and use it on one class and not using on the other one... but that is a little bit smelly :P
You're asking if it's possible: yes it is, but…
If you implement an interface, you must respect its contract.
interface Loggable
{
public function log();
}
This interface's contract is you can call log() without any parameter.
In order to respect that, you can make the parameter optional:
class QMSLogger implements Loggable
{
public function log($queueName = null)
{
...
}
}
This is perfectly valid PHP and it respects the Liskov Substitution Principle. Of course, you must not use that optional parameter when coding against the interface, else you are obviously breaking the interface. Such parameter can be useful only when you are using the implementation (e.g. in some part of the code which is tightly coupled to the QMSLogger).
However this is probably not the solution to your problem as $queueName seems to be a configuration value and it might be better to pass it in the class' constructor (as explained in the other answer).
As stated in the comments, that's not the same interface. If you cannot generalize the interface across all possible logger implementations, make the configuration differences part of the instance constructor:
class QMSLogger implements Loggable {
protected $queueName;
public function __construct($queueName) {
$this->queueName = $queueName;
}
public function log() {
...
}
}
I came across a similar case where I wanted to create an interface that simply ensure any classes that implemented it would have a method of the same name, but would allow for implementation with different parameters.
/**
* Interface Loggable
* #method log
*/
interface Loggable
{
}
Now the Loggable interface can be implemented with different parameters like so.
class Syslogger implements Loggable
{
public function log($key, $value)
{
...
}
}
You can also pass the parameters as an array , in this way you respect the contract from one hand and also be flexible to insert any values with any amount inside the array , check this out :
abstract class FeaturesAbstract
{
/**
* #param array $paramsArray
*
* #return mixed
*/
abstract public function addExecute($paramsArray);
}
And to actually use this method you could send the parameters like this :
$this->abstract->addExecute(array('paramA' => $paramA, 'paramB' => $paramB));
And then inside the concrete implementation you get the parameters like this :
/**
* #param array $paramsArray
*
* #return void
*/
public function addExecute($paramsArray)
{
$a = $paramsArray['paramA'];
$b = $paramsArray['paramB'];
$c = ...
}
Good luck :)

Categories