I develop a pretty big web application using laravel. Logging to a single file or daily files seems too messy for me.
My purpose is to create error logs which will be categorised and easy for retrieval to show on front-end for tech support. E.g. user entered wrong api-key, email, whatever.
P.S. Users don't always understand meaning of the error showed so they contact tech support.
Example for better understanding:
Suppose I have model relations: User->hasMany->Project and in some
project appears an exception I want it to be written and related to
the project. e.t.c. So I could do something like $some_project_model->logs()->get() and tech support got all logs related to
certain project.
What I have in my mind is separate table for logs which is related to certain Eloquent models. But to do so ExceptionHandler needs to know to which model an Exception is related to.
So how this can be implemented? Maybe I could assign custom ExceptionHandler to certain class?
Thank you for any suggestions.
So I've actually found a proper solution:
First of all it's needed to create Log table with polymorphic relation. Then to catch all the exceptions in certain context __call magic method can be used. __call method allows to wrap every object method call with custom code. Try-catch in this case.
Example:
class Loggable
{
public function __call($method, $arguments)
{
try {
return call_user_func_array([$this->certain_obj, $method], $arguments);
} catch (Exception $e) {
//creating new $log orm
$this->get_orm()->loggable()->save($log);
}
}
protected function do_something() {
//something is going on
throw new \Exception();
}
}
$loggable_obj = new Loggable();
$loggable_obj->do_something();
To make it work, you must make all loggable methods private or protected.
In case if you think that 'encapsulation' is not just another difficult word you can achieve the same result using proxy class.
Example:
class Proxy
{
private $some_obj;
public function __construct($some_obj)
{
$this->some_obj = $some_obj;
}
public function __call($method, $arguments)
{
try {
return call_user_func_array([$this->some_obj, $method], $arguments);
} catch (Exception $e) {
//creating new $log orm
$this->some_obj->get_orm()->loggable()->save($log);
}
}
}
$proxy = new Proxy($some_obj);
$proxy->whatever_method($foo, $bar);
So, in both cases I can get all logs related to some exact orm by calling ->logs()->get() on it.
Not a rocket science at all.
One approach might be to create your own custom exception (sub)class which you could add model and/or project information to at the point of throwing the exception. Then this information would be available in the ExceptionHandler.
You wouldn't have it for built in or third-party exception types though. For those you'd end up having to catch and rethrow where possible (and necessary), wrapping in your custom exception class, which probably isn't ideal.
Related
I've been searching for an existing question that already asks this, but I wasn't able to find any questions that quite ask what I'm trying to figure out. The most similar question I could find was this: php 5.3 avoid try/catch duplication nested within foreach loop (code sandwich)
Okay so the place I work at has a web application with a PHP back end. We use an MVC type structure. I'm writing a controller that has multiple methods and in each of my methods I'm wrapping my code with identical try / catch code. In the catch, I pass the exception, a reference to the class, and a reference to the function to a method that builds an error message so that the error messages are formatted the same across the application. It looks something this:
class MyController {
public function methodA() {
try {
// code for methodA
} catch(Exception $e) {
$errorMessage = Tasks::buildErrorMessage($e, __CLASS__, __FUNCTION__);
throw new Exception($errorMessage);
}
}
public function methodB() {
try {
// code for methodB
} catch(Exception $e) {
$errorMessage = Tasks::buildErrorMessage($e, __CLASS__, __FUNCTION__);
throw new Exception($errorMessage);
}
}
public function methodC() {
try {
// code for methodC
} catch(Exception $e) {
$errorMessage = Tasks::buildErrorMessage($e, __CLASS__, __FUNCTION__);
throw new Exception($errorMessage);
}
}
}
So the buildErrorMessage function prevents each method from repeating the code that formats the error message, but there is something that really bothers me about have the same code spread through out every method in the class. I know that PHP doesn't support python-like decorator syntax, but just to demonstrate what I'm envisioning conceptually; I want the code to behave something more like this:
class MyController {
#DefaultErrorHandling()
public function methodA() {
// code for methodB
}
#DefaultErrorHandling()
public function methodB() {
// code for methodB
}
#DefaultErrorHandling()
public function methodC() {
// code for methodC
}
}
Where the #DefaultErrorHandling decorator would wrap each method in that standard try / catch. Is there a way I could achieve this behavior so I don't have to have all of these methods that have repeated code? Or am I thinking about error handling incorrectly?
Thanks to anyone who takes the time to answer this.
Have you looked at a writing a custom exception handler and using set_exception_handler?
What you are doing seems a bit like reinventing the wheel. Does the Exception not already have the info you are collecting in the trace? See: Exception::getTrace
Maybe buildErrorMessage does more? Anyway, I assume a custom exception handler is what you are after.
Not sure if there is a better way to solve this or not, but I created a logging class that formatted the log for me. Then just called this in my catch block.
To log the correct Class and Method, I the debug_backtrace() function. See this answer for more information.
Entry point that calls controller methods can wrap those calls with try / catch. That being said, if you are planning to use different type of error handlers on those methods then you can implement something in your base controller (or use trait) that keeps track of which handler should be invoked on each particular method. Something like
<?php
class MyController extends Controller
{
function __construct()
{
$this->setActionErrorHandler('function_name', 'handler');
}
}
Or just call it at the beginning of action method body. Keeping this type of configuration within class itself will help with readability. Not as neat as python example but better than somewhere in configuration files.
More generic error handlers can be implemented in php by using set_exception_handler mentioned by others.
I'm not really getting why there is such a requirement.
I have a project I created using structural programming that I want to refactor as a object oriented project in the most "bestpractices" way. I will probably be the only one using this project, it's not actually meant for others. But I might show it to others as example of excellence ;)
My questions are pretty general, but I hope this is OK.
I'm thinking about having it split in three ways; backend (the main class), frontend (get and posts check, call class functionality), visual (using Twig templating).
My project will be using an external intgration for IPS forum software (the user sessions will be kept there).
See below for my code idea how to structure this.
My questions:
Is my general structure ok with the class separated from "frontend" like this?
Is my idea of having the member lookup/handling from IPS outside of class ok, as I later can switch to some other member functionality in frontend without messing with backend? Just put the member object into class from wherever, making sure what class use is always set at least.
Should I send the member data as parameter to class (construct), or keep it like now and set a public class var from frontend?
Should my class throw exceptions on errors or return true/false and setting an error message?
Should the frontend also be a class? Extend the main class?
Setting error messages in __construct like this is ok, or should that be done somewhere else?
Should the MyProject class be split into multiple classes? The current project in structural code is 10000 lines, the new class may be about half since I'm taking out a lot of visual rendering stuff. Maybe classes for MyProjectDisplayData and MyProjectCreateData and such?
If answer to 7 is yes, should I have one core class for messages, db and general functionality, which the other specific classes "extends"?
Is there something else one might want to do different?
myproject_class.php:
namespace MySpace;
use \PDO;
use \PDOException;
use \Exception;
class MyProject {
public $projectdata;
public $errormessages;
public $ips_member;
function __construct () {
//set up vars for error messages
$this->errormessages["database_queryfailed"] = "Query failed";
$this->errormessages["general_missingdata"] = "Missing data";
$this->errormessages["handling_something"] = "Some error";
}
public function displaySomeData ( $id ) {
if ($id == ""){
throw new Exception($this->$errormessages["general_missingdata"]);
}
try{
$sql = "GET SOME DATA FROM DB";
//PDO execute
}catch (PDOException $e) {
throw new Exception($this->$errormessages["database_queryfailed"] . " SQL: " . $sql);
}
$this->projectdata = array();
$this->projectdata["one"] = "cool";
$this->projectdata["two"] = "verycool";
if ($someerror){
throw new Exception($this->$errormessages["handling_something"]);
}
}
public function createSomeData(){
try{
$sql = "INSERT SOME DATA IN DB";
//PDO execute
}catch (PDOException $e) {
throw new Exception($this->$errormessages["database_queryfailed"] . " SQL: " . $sql);
}
}
}
Frontend index.php:
require_once 'vendor/autoload.php';
require_once 'myproject_class.php';
require 'forum/init.php';
//forum initialize
\IPS\Session\Front::i();
$ips_member = \IPS\Member::loggedIn();
//load class
try {
$myproj = new MySpace\MyProject();
$myproj->ips_member = $ips_member;
} catch (Exception $e) {
die($e->getMessage()); //not die, but handle in some way
}
//check get or post var to decide what to do
if ($_GET["dowhat"] == "display"){
try {
$myproj->displaySomeData($_GET["id"]);
} catch (Exception $e) {
die($e->getMessage()); //not die, but handle in some way
}
}
//twig rendering
$loader = new Twig_Loader_Filesystem('template');
$twig = new Twig_Environment($loader);
$template = $twig->load('myproject.html');
echo $template->render(array('projectdata' => $myproj->projectdata, 'member' => $ips_member));
Thank you for your help!
If your codebase is about 10k lines, there is no way you can stuff that in two or three classes (well, apparently you can, but it's a terrible idea).
First of all, you should extract your HTML in templates. Twig is a nice choice and should serve you well. But next step would probably be introduction of routing logic, that would let you automate the choosing of which template to render.
Regarding your general understanding of OOP, I would recommend you watch this and this lecture. Because I am getting a feeling, that you do not really understand OOP paradigm as a whole.
And don't abuse extends keywords. There is this old quote in OOP: "You should favour composition over inheritance". And that sums it up quite well.
Regarding error handling, I wrote about it just few days ago, so I will just be lazy and direct you to an older post, that covered briefly the common approaches and touched upon some of the drawback in each.
And finally, for dealing with DB: each class, that requires access to DB, should have an instance of PDO (or MySQLi) be passed in it's constructor. If you have more than one such class, reading this post might help with sharing that connection instance.
Consider the following method:
function m1()
{
$ent = new Entity;
...
try {
$ent->save();
} catch (QueryException $e) {
...
}
I've got to trigger an exception. Preferably with mockery. How do I do that?
P.S. I can't pass $ent into the method.
UPD Let me describe my particular case to confirm if I do need to trigger an exception. Here I'm trying to test controller's action that is triggered by payment system to notify that user has made a payment. In it I, among other things, store in database all the data coming from payment system in PaymentSystemCallback model, and link it to Order model, which is created before redirecting user to the payment system. So, it goes like this:
function callback(Request $request)
{
$c = new PaymentSystemCallback;
$c->remote_addr = $request->ip();
$c->post_data = ...;
$c->headers = ...;
...
$c->save();
$c->order_id = $request->request->get('order_id');
$c->save();
}
But if incorrect order_id comes in, foreign constraint fails, so I change it this way:
try {
$c->save();
} catch (QueryException $e) {
return response('', 400);
}
But it doesn't look good to handle any database exception this way, so I'm seeking for a way to rethrow the exception unless $e->errorInfo[1] == 1452.
And here's what I came up with:
/**
* #runInSeparateProcess
* #preserveGlobalState disabled
*/
function testExceptionOnSave()
{
$this->setUpState();
Mockery::mock('overload:App\PaymentSystemCallback')
->shouldReceive('save')
->andReturnUsing(function() {}, function() {
throw new QueryException('', [], new Exception);
});
$this->doRequest();
$this->assertBalanceDidntChange();
$this->assertNotProcessed();
$this->seeStatusCode(500);
}
I use #runInSeparateProcess because preceding tests trigger the same action, and therefore the class is loaded before mockery has a chance to mock it.
As for #preserveGlobalState disabled it doesn't work without it. As phpunit's documentation put it:
Note: By default, PHPUnit will attempt to preserve the global state from the parent process by serializing all globals in the parent process and unserializing them in the child process. This can cause problems if the parent process contains globals that are not serializable. See the section called “#preserveGlobalState” for information on how to fix this.
I deviate a little from what mockery's documentation says when I'm marking only one test to run in a separate process, since I need it only for one test. Not the whole class.
Constrictive criticism is welcome.
The easiest way around this is to call a factory method that creates a mock instance of your Entity. Something like:
function testSomething()
{
$ent = $this->getEntity();
...
try {
$ent->save();
} catch (QueryException $e) {
...
}
}
function getEntity()
{
$mock = $this->createMock(Entity::class);
$mock
->method('save')
->will($this->throwException(new QueryException));
return $mock;
}
Your method is not designed for test. Fix that. If you can't, then you have to monkey patch, which PHP does not support natively.
My recommended approach would be to have your test suite install its own priority autoloader. Have your test case register a mock class into that autoloader, associated with class name Entity. Your mock class will do its magic to throw an exception. If you're using PHP 7, you have access to anonymous classes, which makes fixtures easier: new class Entity {}.
Per the accepted answer, Mockery supports this autoloading trick using the overload: quantifier on mocked classes. This saves a lot of work on your part!
I'm trying to clean up some of my controllers by moving the functionality from the controller methods to reusable commands that I can just pass on to a command bus. As I've understood, though, a command is not supposed to return data (or did I misunderstand something?). But how else would you suggest for the controller to act on the outcome of the command?
For instance I have a Create endpoint in my ClientController. The controller method dispatches a CreateClient command, and then I'd like to redirect the user to the new Client's edit page. Something like
class ClientController extends Controller
{
public function create($request)
{
try {
$this->dispatch(new CreateClient(/*get something from the request to pass*/));
} catch (\Exception $e) {
return $this->json(['error' => $e->getMessage()]);
}
$client = ...;
$this->redirect('/client/' . $client->id);
}
}
If the creation failed I can throw an exception, which can be handled by the controller. I could of course create a method in my ClientRepository to get the newest created Client, but that seems error prone. But how would you suggest that I got a hold of the newly created client?
Edit:
At the end of https://www.youtube.com/watch?v=fbSYZFZCFS0 they discus this actual problem. Their main suggestions are using UUIDs so the ID can be generated by the controller, or using an asynchronous method to poll for the new item.
They also mention the possibility of using event listeners, but argue that it's an anti-pattern in PHP.
I'm still undecided on how I'll prefer to do it.
Working on a symfony application that uses nusoap (is this the best method for integrating soap work with php/symfony?) for taking credit card payments.
I've simplified an example of my code below.
What I'm struggling with is the best way to handle exceptions. The example below only has 1 custom exception (where should my custom exceptions reside within the directory structure of symfony? (lib/exception?)) But what happens when there are several different types of exceptions that handle a specific error? It's not very elegant to have a try/catch block with 20 odd exceptions.
I'm also not sure of where I should be throwing and catching. I need to set some user flashes to alert the user of any problems, so I figure the catching should be done in the actions controller rather than within the class that handles the soap call.
Could anyone please advise where I might be going wrong?
I hate messy code/solutions and want to stick to the DRY principle as much as possible. I think I might also be missing some built in symfony functionality that might help with this but whenever I search I usually find examples that are for symfony 1.2, I'm using 1.4.
Some examples would be great, thanks.
lib/soap_payment.class.php
class SoapPayment
{
public function charge()
{
/*assume options are setup correctly for sake of example*/
try
{
$this->call();
}
catch (SoapPaymentClientFaultException $e)
{
/* should this be caught here? */
}
}
private function call()
{
$this->client->call($this->options);
if ($this->client->hasFault())
{
throw new SoapPaymentClientFaultException();
}
}
}
apps/frontend/payment/actions/actions.class.php
class paymentActions extends sfActions
{
public function executeCreate(sfWebRequest $request)
{
/* check form is valid etc */
$soap_payment = new SoapPayment();
try
{
$soap_payment->charge();
}
catch (SoapPaymentClientFaultException $e)
{
/* or throw/catch here? */
$this->getUser()->setFlash('error', ...);
$this->getLogger()->err(...);
}
/* save form regardless, will set a flag to check if successful or not in try/catch block */
}
}
One not very well known feature of Symfony is that exceptions can manage the content sent in a response. So you could do something like this:
class SoapException extends sfException
{
public function printStackTrace() //called by sfFrontWebController when an sfException is thrown
{
$response = sfContext::getInstance()->getResponse();
if (null === $response)
{
$response = new sfWebResponse(sfContext::getInstance()->getEventDispatcher());
sfContext::getInstance()->setResponse($response);
}
$response->setStatusCode(5xx);
$response->setContent('oh noes'); //probably you want a whole template here that prints the message that was a part of the SoapException
}
}
If you need a cleaner handling of SOAP exceptions, like setting flashes, etc. you'll probably have to catch each exception. One idea here might be to create a generic SoapException class that is extended by more specific SoapExceptions so you don't have to catch a bunch of different types. The above code may be a useful fallback mechanism as well.
Finally, yes, you should place custom exceptions in lib/exception.