I am relatively new to PHP, which might well prove to be the main problem here - I get the feeling I'm missing something somewhat fundamental about PHP internals that would either make solving this straightforward, or make it glaringly obvious why I am wasting my time!...
Basically in the following Slim API code I would like the exception handling added by the excellent entomb/slim-json-api middleware to also apply to the subsequent myMiddleware. As implemented below, it only seems to handle failures generated in the route code...
(PHP v 5.4.17)
$ php composer.phar info
entomb/slim-json-api dev-master c11e001 Slim extension to implement fast JSON API's
slim/slim 2.6.2 Slim Framework, a PHP micro framework
API code:
require 'vendor/autoload.php';
use Slim\Middleware;
class myMiddleware extends Middleware
{
public function call()
{
$uri_array = explode('/', $this->app->request->getResourceUri());
$env = $this->app->environment;
if($uri_array[2] == 98) {
throw new \Exception("User $uri_array[2], you are not even welcome in middleware!");
} else {
$body = array('user_from_middleware' => $uri_array[2]);
$env['slim.input'] = json_encode($body);
}
$this->next->call();
}
}
///////////////////////////////////////////////////////////////////////////////////
$app = new \Slim\Slim();
$app->view(new \JsonApiView());
$app->add(new \JsonApiMiddleware());
$app->add(new myMiddleware());
$app->get('/user/:id', function($id) use ($app) {
if ($id == 99) {
throw new \Exception("User $id, you are not welcome!");
} else {
$body = json_decode($app->request->getBody());
$body->msg = "User $id welcome to my API!";
$app->render(200,(array) $body);
}
});
Here's a request that misses both Exceptions:
$ curl http://localhost:8082/test.php/user/1
{"user_from_middleware":"1","msg":"User 1 welcome to my API!","error":false,"status":200}
...this one fires the Exception in route, showing that the JsonApiMiddleware is working:
$ curl http://localhost:8082/test.php/user/99
{"msg":"ERROR: User 99, you are not welcome!","error":true,"status":500}
...but when this one fires the Exception in myMiddleware the API returns nothing:
$ curl http://localhost:8082/test.php/user/98
$
...and I can see from the log that the exception was definitely thrown:
[Mon Nov 7 21:54:08 2016] PHP Fatal error: Uncaught exception 'Exception' with message 'User 98, you are not even welcome in middleware!' in /path/to/test.php:14
Stack trace:
#0 /path/to/vendor/slim/slim/Slim/Slim.php(1302): myMiddleware->call()
#1 /path/to/test.php(42): Slim\Slim->run()
#2 {main}
thrown in /path/to/test.php on line 14
What am I missing? Apologies again if this is a tedious question.
Probably you should not $this->next->call() in MyMiddlware if an exception is thrown?..
class myMiddleware extends Middleware
{
public function call()
{
$uri_array = explode('/', $this->app->request->getResourceUri());
$env = $this->app->environment;
if($uri_array[2] == 98) {
throw new \Exception("User $uri_array[2], you are not even welcome in middleware!");
} else {
$body = array('user_from_middleware' => $uri_array[2]);
$env['slim.input'] = json_encode($body);
$this->next->call(); // call next callable only if exception was not thrown
}
}
}
Seems like you're using Slim v 2., but this is what I'd do in Slim 3.5.*
Related
I have this get organisations method in one project that talks to a central api project that handles all data like so:
public function searchOrganisations()
{
try {
return $this->client->request(.....);
} catch (Exception $ex) {
}
}
Within the api project a method is then hit and if a certain time frame criteria is hit I throw a custom exception like so:
public function searchOrganisations($searchRequest)
{
$experianCutOff = Carbon::createFromFormat('H:i:s', '06:00:00');
$now = Carbon::now()->setTime(02, 0, 0);
if (!$now->lt($experianCutOff)) {
return $data
} else {
throw new ExperianServiceException();
}
}
My custom exeption is as follows:
class ExperianServiceException extends Exception
{
public function render() {
return response()->json([
'message' => 'The Experian Service is currently unavailable, please try again at 0600 GMT'
], 503);
}
}
This works as expected and I catch the exception in the first method listed, I can access the status 503 and can see the message, however the message property of the exception always comes back in this format:
Server error: `POST http://docker.../search-organisations` resulted in a `503 Service Unavailable` response:
{"message":"The Experian Service is currently unavailable, please try again at 0600 GMT"}
It seems as though my supplied custom message has been concatenated with the standard Laravel Exception message (which I dont want). How can I make sure my message only contains what I supplied in my custom exception?
You could have your Exception implement Illuminate\Contracts\Support\Responsable and define the toResponse method to return that response. When you only have the render method the framework is still potentially going to do things to prepare the response. If it is Responsable it calls toResponse on it and returns that directly.
This causes a raw response from toResponse of your Exception to be returned without passing through any other parts of the Handler to be prepared in any way.
Maybe:
return Response::json(array(
'code' => 404,
'message' => $message
), 404);
I am at a complete loss here...The two calls bellow are using the same service yet one works great the other fails...I was hoping another set of eyeballs might catch something I am doing wrong. On the google OAuth Playground they both seem to be working fine
!! CRASHES !!
//Googles Class
class Google_Service_Proximitybeacon_BeaconsDiagnostics_Resource extends Google_Service_Resource
{
public function listBeaconsDiagnostics($beaconName, $optParams = array())
{
$params = array('beaconName' => $beaconName);
$params = array_merge($params, $optParams);
return $this->call('list', array($params), "Google_Service_Proximitybeacon_ListDiagnosticsResponse");
}
}
//Service
$service = new Google_Service_Proximitybeacon($client);
//Call
$beaconName = 'beacons/3!f7826da64fa24e9880243rgenfgv43kgekfe';
$request = $service->diagnostics->listBeaconsDiagnostics($beaconName);
!! WORKS GREAT !!
//Googles Class
class Google_Service_Proximitybeacon_Namespaces_Resource extends Google_Service_Resource
{
public function listNamespaces($optParams = array())
{
$params = array();
$params = array_merge($params, $optParams);
return $this->call('list', array($params), "Google_Service_Proximitybeacon_ListNamespacesResponse");
}
}
//Service
$service = new Google_Service_Proximitybeacon($client);
//Call
$request = $service->namespaces->listNamespaces();
Fatal error: Uncaught Error: Call to a member function listBeaconsDiagnostics() on null in /home/xtractio/public_html/test4.php:121 Stack trace: #0 {main} thrown in /home/xtractio/public_html/test4.php on line 121
This is the fatal error I am receiving, just odd that both the calls above and other calls are pretty much identical but these two extended classes attachments/diagnostics seem to be causing the most issue.
Thanks in advance!
I am a quite newbee for phpunit, so it might be stupid though ....
I google around but not found.
This is my code and I have multiple API and URL to test.
namespace Acme\TopBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class DefaultControllerTest extends WebTestCase
{
public function testIndex()
{
$client = static::createClient();
echo ("first test");
$crawler = $client->request('GET', '/api/getplaceinfo');
$this->assertTrue($client->getResponse()->isSuccessful());
echo ("second test");
echo('test :' + '/api/getmetainfo/kibichuo');
$crawler = $client->request('GET', '/api/getcat');
$this->assertTrue($client->getResponse()->isSuccessful());
echo ("third test");
$crawler = $client->request('GET', '/admin/dashboard');
$this->assertTrue($crawler->filter('html:contains("My Server")')->count() > 0);
}
}
then I test like this (I am using symfony2 framework)
whitebear$ phpunit -c app/
PHPUnit 4.8.35 by Sebastian Bergmann and contributors.
.0
Time: 3.69 seconds, Memory: 109.25MB
OK (1 test, 7 assertions)
There is no message I expected by echo("first test").
So, even error happens, I can't tell which url shows the error.
My basic idea is wrong??
You should write one test for each test and in assertTrue you can put a message there.
Example:
public function testThirdTest() {
$client = static::createClient();
$crawler = $client->request('GET', '/admin/dashboard');
$this->assertTrue($crawler->filter('html:contains("My Server")')->count() > 0, 'third test goes wrong, put message here');
}
In your test you can now see the test what goes wrong (the message in assertTrue) and see, what test is failed (the name of the test).
Hope, this helps....
I wrote a test for a controller that saves in the database some data passed by a form.
I wrote the following test method to be sure that if the form is empty an exception is thrown:
public function testRegisterNewMerchantExceptionNoDataSubmitted()
{
$client = static::createClient();
$crawler = $client->request('GET', '/getstarted');
$form = $crawler->selectButton('getStarted[submit]')->form();
$form['getStarted[email]'] = '';
$this->setExpectedException('DomainException');
$client->submit($form);
$this->assertEquals(500, $client->getResponse()->getStatusCode());
//dump($client->getResponse());die;
}
The method i'm testing is the following:
public function endAction(Request $request)
{
$form = $this->createForm(new GetStartedType());
$form->handleRequest($request);
if ($form->isValid()) {
// Get data from form
$data = $form->getData();
} else {
throw new \DomainException('No data submitted.');
}
...
I'm sure that also during tests the exception is thrown because dumping the Response object the page is a 500 error reporting the exact message "No data submitted". More, the assertEquals test on the status code is successful, so there are no doubts that the exception is correctly thrown.
But the $this->setExpectedException() test doesn't intercept it and returns a fail of the test.
Any idea about why this happens?
Using $this->setExcpectedException() tells PHPUnit to expect the given exception type to be thrown from the test method, not just that an exception of that type is thrown at some point during execution.
When you throw an exception in a controller method, the Symfony controller catches
that exception and creates a 500 response. This means the exception will not be thrown from the test method, so the test fails. Your test looks reasonable otherwise, so removing $this->setExpectedException() should solve the problem and test the behavior you intended.
After 10 hours of trying various fitbit php libraries I'm turning to stackoverflow for help.
This doesn't work: https://github.com/heyitspavel/fitbitphp
Using
$profile = $fitbit->getProfile();
with that library returns
Fatal error: Uncaught exception 'FitBitException' with message 'Your Fitbit request failed. Code: 400' in /var/www/api/fitbitphp.php:324 Stack trace: #0 /var/www/api/addFitbit.php(22): FitBitPHP->getProfile() #1 {main} thrown in /var/www/api/fitbitphp.php on line 324
This the library on the fitbit website, seems like a lot of people have a problem with this.
public function getProfile()
{
$headers = $this->getHeaders();
try {
$this->oauth->fetch($this->baseApiUrl . "user/" . $this->userId . "/profile." . $this->responseFormat, null, OAUTH_HTTP_METHOD_GET, $headers);
} catch (Exception $E) {
}
$response = $this->oauth->getLastResponse();
$responseInfo = $this->oauth->getLastResponseInfo();
if (!strcmp($responseInfo['http_code'], '200')) {
$response = $this->parseResponse($response);
if ($response)
return $response;
else
throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
} else {
throw new FitBitException($responseInfo['http_code'], 'Your Fitbit request failed. Code: ' . $responseInfo['http_code']);
}
}
I tried this here as well but it doesn't return the user token or session id https://github.com/nostra999/fitbit-api
Perhaps missed out the init step, as described in the lib README file (https://github.com/heyitspavel/fitbitphp/blob/master/README.md)
Simple working usage is:
<?php
define('FITBIT_KEY', '777'); // The application key registered
define('FITBIT_SECRET', '777'); // The application secret registered
$fitbit = new FitBitPHP(FITBIT_KEY, FITBIT_SECRET);
$fitbit->initSession('http://localhost:8080/fibit'); // callback URL
$fitbit->getProfile();
Also from the Fitbit API Documentation:
https://wiki.fitbit.com/display/API/API+Response+Format+And+Errors#APIResponseFormatAndErrors-Response
400 Bad Request Any case where either endpoint doesn't exist, resource
path parameters are invalid, POST request parameters are invalid or no
Authentication header provided. This doesn't include invalid specific
resource ids
If this does not help, please provide the full code that you run, not just
$profile = $fitbit->getProfile();