Access function variable inside class method - php

I searched and there are a lot of answers but I cannot find one I need because I do not know even how to create correct question. here is the example.
$app->map('/v1/:module/:group/:action(/:id)', function ($module, $group, $action, $id = NULL) use ($app) {
$method = ucfirst($app->request->getMethod());
$file = "modules/{$module}/{$group}/{$method}{$action}.php";
if(!file_exists($file)) {
$app->halt(404, Error::_('API Processor was not found!', 404));
}
include_once $file;
$app->stop();
})
This is my API method by slim restful framework. Now for this Error::_('API Processor was not found!', 404) I have
class Error {
public static function _($msg, $code = 500) {
global $module, $group, $action;
return json_encode(array(
'error' => true,
'code' => $code,
'message' => $msg,
'module' => $module
));
}
}
What I want os to get access to $module, $group, $action variables without passing them into that function. But in my case $module is NULL.
{
"error":true,
"code":404,
"message":"API Processor was not found!",
"module":null
}
Possible?

You should be able to meet those requirements, if I understood your question correctly, by using the Slim Error Handling functionality. If it were my project, I'd create a custom exception to throw wherever you're planning on using your custom error function.
NOTE: All of the code below is untested and written off the top of my head. Caveat emptor and all that.
class CustomErrorException extends \Exception
{
}
Then I would throw that exception wherever I'd otherwise use my custom error function.
if(!file_exists($file)) {
throw new CustomErrorException('API Processor was not found!', 404);
}
Finally, I'd write an error function that looks something like this:
$app->error(function (\Exception $e) use ($app) {
if ($e instanceof CustomErrorException) {
// Parse $path to get $module, $group, and $action
// (Seems like that would work based on the route in your example: '/v1/:module/:group/:action(/:id)')
$path = $app->request->getPath();
// Possible new method signature for Error::_
Error::_($e->getMessage(), $e->getCode(), $module, $group, $action);
// Render an error page, $app->halt(), whatever.
}
});
That should help DRY your code up a bit and allow you to dump those global variables.

Related

Fatal Error on using $exception variable in render() Method in App\Exceptions\Handler.php

I am new to Laravel and have an Issue regarding the Handler.php File.
I am trying to create a class that takes an exceptions and transforms it into a JSON Response.
Sadly though, upon calling the constructor a series of Errors are thrown:
(ErrorErrorErrorErrorErrorErrorErrorErrorErrorErrorErrorSymfony\Component\ErrorHandler\Error\FatalError)
My code:
render() in Handler.php:
public function render($request, Throwable $exception)
{
$errorResource = new ErrorResource($exception);
return $errorResource->getJsonResponse();
}
class ErrorResource in ErrorResource.php:
<?php
namespace Transformers;
use Throwable;
class ErrorResource
{
private $exception;
private $defaultCodes = [TypeError::class => 400];
private $defaultMessages = [TypeError::class => 'Untgültige URL Parameter'];
function __construct(Throwable $exception)
{
$this->exception = $exception;
}
public function getJsonResponse($exception)
{
$codeToThrow = 500;
$messageToThrow = "Internal Server Error";
$type = get_class($this->exception);
if (empty($exception->getCode())) {
$codeToThrow = $this->defaultCodes[$type];
} else {
$codeToThrow = $exception->getCode();
}
if (empty($exception->getMessage())) {
$messageToThrow = $this->defaultMessages[$type];
} else {
$messageToThrow = $exception->getMessage();
}
return response()->json(array(
'Type' => $type,
'Message' => $messageToThrow
), $codeToThrow);
}
}
I have also tried to move the method getJsonResponse() to the Handler.php file and call it from there, but without any luck.
I am really confused as to why I am not allowed to do certain things with the $exception variable (I have also tried to create a clone of this object - but the same error occures)
I hope you can help me resolving this issue,
Greetins,
Franz
The issue is, that PHP is call by value. That is why it is implicitely trying to clone an unclonable object -> Error. To resolve this issue one can use wrapper objects, but I decided to simply use call by reference (https://www.javatpoint.com/php-call-by-reference)

Laravel send json response from inner function

So Im building an API.
But I keep running into returning a json response from an inner function.
If I do the following then laravel sends a backlog or error log to the client. That should never ever happen in a api. So how do you do this in Laravel?
I want to return json immediately and stop excecuting without showing the client any other information
public function functionThatIsCalledByClient($data)
{
$this->validateSomething($data);
}
private function validateSomething($data)
{
if(! $data ) ) return response()->json(['error' => 'some message'], 400)->send();
return true;
}
You could use abort helper for that or, for complex cases, error handling.
In case of abort:
private function validateSomething($data)
{
if(! $data ) // you can create a custom helper function to wrap this code.
abort(400, json_encode(['error'=>'some error']), ['Content-Type: application/json']);
return true;
}
In case of general handler:
private function validateSomething($data)
{
if(! $data )
throw new \Exception('some message');
return true;
}
Inside app/Exceptions/Handler.php # render
public function render($request, Exception $e)
{
if($e instanceof Exception)
return response()
->json(['error' => $e->getMessage()], 400);
//->send(); should not be necessary
}
throw new GeneralException('invalid required extra quantity');

Error handling in PHP with custom IDs and custom messages

I'm on a project where I don't want to throw errors directly at the user. Instead I want customized messages for the error that accur.
For later I also need to keep an error number in order to customize the error messages from outside the class, like an array of error messages.
So I made my own thing where I set $error = null and then later set error to a number that later becomes a message.
Question
Is there any disadvantages with this approach? Am I better of with try/catch or something else? I would like to keep the code short and tidy if possible.
In this short code example the error handling seems to be a big part of the class. In my real code which is a few hundred lines, it's not a big part of the whole code
http://sandbox.onlinephpfunctions.com/code/623b388b70603bf7f020468aa9e310f7340cd108
<?php
class Project {
private $error = null;
public function callMeFirst($num) {
$this->nestedLevelOne($num);
$this->nestedLevelTwo($num);
$this->setResults();
}
public function callMeSecond($num) {
$this->nestedLevelTwo($num);
$this->setResults();
}
private function nestedLevelOne($num) {
// Do stuff
if($num !== 1) {
$this->error = ['id' => 1, 'value' => $num];
}
}
private function nestedLevelTwo($num) {
// Do stuff
if($num !== 20) {
$this->error = ['id' => 2, 'value' => $num];
}
}
private function message($args) {
extract($args);
$message = [
1 => "Nested level one error: $value",
2 => "Another error at level two: $value",
];
return ['id' => $id, 'message' => $message[$id]];
}
private function setResults() {
$results['success'] = ($this->error === null) ? true : false;
if($this->error !== null) {
$results['error'] = $this->message($this->error);
}
$this->results = $results;
}
}
$project = new Project();
$project->callMeFirst(1);
$project->callMeFirst(2);
print_r($project->results);
It will output
Array
(
[success] =>
[error] => Array
(
[id] => 2
[message] => Another error at level two: 2
)
)
The reason I'm asking is that I have a feeling of that I may reinvent the wheel in this case. Am I?
If there is a better solution, I would be thankful to see how that code looks like.
I would probably separate the business logic from the error handling to simplify each part more. By using exceptions, you keep your business logic simpler; you simply throw an exception whenever you encounter a case that is not permitted, thereby preventing getting into any sort of inconsistent state at all. The business logic class doesn't have to care about how this error will be processed further, it just needs to raise the error. You should then create a separate wrapper around that business logic class which simply cares about handling any errors and formatting them into an array or other sort of response which will be handled elsewhere. Something along these lines:
class ProjectException extends Exception {
public function __construct($num) {
parent::__construct(get_called_class() . ": $num");
}
}
class NestedLevelOneException extends ProjectException {
// customise __construct here if desired
}
class NestedLevelTwoException extends ProjectException {}
class Project {
public function callMeFirst($num) {
$this->nestedLevelOne($num);
$this->nestedLevelTwo($num);
}
public function callMeSecond($num) {
$this->nestedLevelTwo($num);
}
protected function nestedLevelOne($num) {
if ($num !== 1) {
throw new NestedLevelOneException($num);
}
// do stuff
}
protected function nestedLevelTwo($num) {
if ($num !== 20) {
throw new NestedLevelTwoException($num);
}
// do stuff
}
}
class ProjectService {
protected $project;
public function __construct(Project $project = null) {
$this->project = $project ?: new Project;
}
public function process($a, $b) {
try {
$this->project->callMeFirst($a);
$this->project->callMeSecond($b);
return ['success' => true];
} catch (ProjectException $e) {
return ['success' => false, 'error' => $e->getMessage()];
}
}
}
$api = new ProjectService;
print_r($api->process(1, 2));
By defining three separate exceptions, you get a lot of flexibility in how and where you want to handle errors. You can specifically catch NestedLevel*Exception, or you catch either of them with ProjectException.
By having your methods throw exceptions, you gain flexible error handling possibilities. You are free to not catch the exception and have the program terminate, as would be entirely reasonable if one of your business requirements isn't met. Alternatively, you can catch the exception at a level up that is prepared to deal with that error and turn it into something meaningful that can be acted upon.
By moving the generation of the error message into the exceptions, you keep the error type and its message self-contained. There's exactly one place where you define what kind of error may happen and what its error message will look like; instead of spreading that out over your entire codebase. And you're still free to choose some other error message in the UI, e.g. for localising different kinds of errors into multiple languages; just check the type of the exception object.
By using a separate ProjectService which cares about handling those exceptions and turning it into an array response, you narrow each class' responsibilities and make each class more flexible and simpler.

Try catch private method phpunit symfony

I have the following code:
public function addSomething($paramDto) {
try {
$this->privateMethod($param);
} catch(\Exception $e) {
return ['error' => true, 'messages' => [$e->getMessage()]];
}
return ['error' => false, 'messages' => 'success'];
}
private function privateMethod($param) {
if(!$param) {
throw new \Exception('errorMessage');
}
}
I'm trying to test the addSomething method, what the catch block returns, I don't want to test the private method.
public function testAddSomethingThrowError($paramDto) {
$param = \Mockery::mock('MyEntity');
$method = new \ReflectionMethod(
'MyService', 'privateMethod'
);
$method->setAccessible(TRUE);
$this->expectException(\Exception::class);
$this->getMyService()
->shouldReceive($method->invoke($param)
->withAnyArgs()
->andThrow(\Exception::class);
$this->getMyService()->addSomething($paramDto);
}
The thing is that if i run the test, it coverages the private method in the if statement and returns the exception, but the catch method in my addSomething method is not covered, actually it does not cover the addSomething method at all.
I am using the sebastian bergmann phpunit framework.
What am I doing wrong?
The correct answer should be Jakub Matczak's answer:
"You want to "assert if the public method is returning the message that it is indeed returning". There's no sense in doing that. Consider your tested class as a blackbox without possibility to check its source. Then make tests according to how to want it to work using its public interface. "

How to reach the exception block

So I am messing around with symfony router component and I created a small wrapper.
One thing that came up was how do I get a request to throw a 500 in unit tests? The method in question is:
public function processRoutes(Request $request) {
try {
$request->attributes->add($this->_matcher->match($request->getPathInfo()));
return call_user_func_array($request->attributes->get('callback'), array($request));
} catch (ResourceNotFoundException $e) {
return new RedirectResponse('/404', 302);
} catch (Exception $e) {
return new RedirectResponse('/500', 302);
}
}
And the test in question is:
public function testFiveHundred() {
$router = new Router();
$router->get('/foo/{bar}', 'foo', function($request){
return 'hello ' . $request->attributes->get('bar');
});
$response = $router->processRoutes(Request::create('/foo/bar', 'GET'));
$this->assertEquals(500, $response->getStatusCode());
}
Right now the test will fail because we are defined and the status code will be 200. Is there something special I can do to the Request object I create, to make it throw a 500?
I think you got several options here you can play with:
Decide that a specific path will always throw an exception.
This will force you to make some changes in your code.
public function processRoutes(Request $request) {
...
if ($request->getRequestUri() == '/path/that/throws/exception') {
throw Exception('Forced to throw exception by URL');
}
...
}
public function testFiveHundred() {
...
$response = $router->processRoutes(Request::create('/path/that/throws/exception', 'GET'));
...
}
Make a DummyRequest object that will extends your original Request class and make sure this object will raise an Exception (for example - you know for sure that you use the getPathInfo(), so you can use this).
class DummyRequest extends Request {
public function getPathInfo() {
throw new Exception('This dummy request object should only throw an exception so we can test our routes for problems');
}
}
public function testFiveHundred() {
...
$dummyRequest = new DummyRequest();
$response = $router->processRoutes($dummyRequest);
...
}
Since the function getRequestUri of our $dummyRequest throws an exception, your call to $router->processRoutes will have our dummy to throw that exception.
This is a general idea, you would probably need to play a bit with the namespaces and the functions there (I didn't test it, however this should work).

Categories