I've been handed a PHP class, and I'm not interested in fully restructuring it. (it works!)
But I'd like to add a slight modification inside a few methods.
Here is one of the many methods inside the class:
<?php
class SomeFunClass {
public function getAccountInfo()
{
$request = $this->prepareRequest('get_account_info');
$response = $this->execute($request);
return $response;
}
}
?>
The return $response is a string value.
I've come to a point that I need to return the $request string, which happens to be a json string.
The prepareRequest() method always returns a json string, which is then passed to the exec() method, which simply sends the data via cURL to a domain.
I'd like to extract the $request string (when I call the getAccountInfo() method), for later review.
Here's what I'm doing now:
<?php
$api = new SomeFunClass();
$curlresponse = $api->getAccountInfo();
?>
Obviously, the example immediately above only gives me back what the cURL response would be.
Would be nice to call a method that lets me see what the $request looks like.
I'm open to suggestions.
Just return an array with the request and the response:
<?php
class SomeFunClass {
public function getAccountInfo()
{
$request = $this->prepareRequest('get_account_info');
$response = $this->execute($request);
return array('request' => $request, 'response' => $response);
}
}
?>
You can modify those methods to store the last request into an attribute of the current class :
<?php
class SomeFunClass {
$last_request;
...
public function getAccountInfo()
{
$request = $this->prepareRequest('get_account_info');
$last_request = request;
$response = $this->execute($request);
return $response;
}
public function getLastRequest()
{
return $this -> last_request;
}
}
?>
Or, better, if prepareRequest is a method of yours, then just modify this one to store the last request.
You can do something like this:
<?php
class SomeFunClass {
public $request;
public $response;
public function getAccountInfo()
{
$this->request = $this->prepareRequest('get_account_info');
$this->response = $this->execute($this->request);
return $this->response;
}
}
?>
Now, you can do something like this:
<?php
$api = new SomeFunClass();
$curlresponse = $api->getAccountInfo();
$request = $api->request;
?>
Ideally, you can do implement your class like this to take actual advantage of OOP (so that these instance variables request and response are auto-set for all your methods):
<?php
class SomeFunClass {
public $request;
public $response;
public function getAccountInfo()
{
$this->prepareRequest('get_account_info');
return $this->execute();
}
public function anotherMethod()
{
$this->prepareRequest('another_method', 'some', 'args');
return $this->execute();
}
public function prepareRequest()
{
$args = func_get_args(); // contains your arguments
$method = array_shift($args); // contains your method name
...
...
$this->request = $return // value returned by this method
}
public function execute()
{
$request = $this->request;
...
...
$this->response = $return // value returned by this method
}
}
?>
You could also do this:
<?php
class SomeFunClass {
public function reviewRequest($request)
{
return $this->prepareRequest($request);
}
}
And then:
<?php
$api = new SomeFunClass();
$request = $api->reviewRequest('get_account_info');
Related
Heres the code for the route:
<?php
use app\controllers\Home;
use app\core\App;
$app = new App();
$app->router->get('/', function(){
echo "hello there";
});
$app->router->get('user/', 'user');
$app->router->get('/contact', [Home::class,'contacts']);
$app->router->post('/contact', function(){
echo "handlecontact";
});
$app->run();
Heres the code for the router core class:
<?php
namespace app\core;
class router{
public Request $request;
public Response $response;
protected array $routes = [];
public function __construct(Request $request,Response $response)
{
$this->request = $request;
$this->response = $response;
}
public function get($path, $callback){
$this->routes['get'][$path] = $callback;
}
public function post($path, $callback){
$this->routes['post'][$path] = $callback;
}
public function resolve(){
$path = $this->request->getpath();
$method = $this->request->getmethod();
$callback = $this->routes[$method][$path] ?? false;
if($callback == false){
$this->response->setstatuscode(404);
return "not found";
}
if(is_array($callback)){
$callback[0] = new $callback[0]();
}
return call_user_func($callback,$this->request);
}
I want to be able to declare a route and pass a parameter to it. If i do it so like that it tells me route not found.
Like i want to be able to write the route like this:
$app->router->get('/contact/{id}', function(){
echo $id; // to echo it out like this from the controller
});
Can someone be of good help or also add something to the idea. Thanks in advance
I have a Service class and a test for that, follow below:
Class
class MyCustomService
{
public function job()
{
while($this->getResponseFromThirdPartyApi()->data) {
// do some stuff...
}
return ...
}
protected function getResponseFromThirdPartyApi()
{
// Here do some curl and return stdClass
// data attribute is populated only on first curl request
}
}
Test mocking getResponseFromThirdPartyApi method
class MyCustomServiceTest
{
public function testJobImportingData()
{
$myCustomServiceMock = $this->getMockBuilder('MyCustomService')
->setMethods(array('getResponseFromThirdPartyApi'))
->getMock();
$myCustomServiceMock->expects($this->any())
->method('getResponseFromThirdPartyApi')
->willReturn($this->getResponseWithData());
$jobResult = $myCustomServiceMock->job();
// here some assertions on $jobResult
}
protected function getResponseWithData()
{
$response = new \stdClass;
$response->data = ['foo', 'bar'];
return $response;
}
}
How can I change getResponseWithData return after first call on MyCustomService while loop?
I've tried creating a custom flag on MyCustomServiceTest and checking on getResponseWithData, but fails once that mocked object doesn't call getResponseWithData method again on MyCustomServiceTest.
Any direction?
As Nico Haase suggested above, the path is to use callback.
After some research I achieved passing mock object reference to method mocked and checking flag, this results on:
class MyCustomServiceTest
{
public function testJobImportingData()
{
$myCustomServiceMock = $this->getMockBuilder('MyCustomService')
->setMethods(array('getResponseFromThirdPartyApi'))
->getMock();
$myCustomServiceMock->expects($this->any())
->method('getResponseFromThirdPartyApi')
->will($this->returnCallback(
function () use ($myCustomServiceMock) {
return $this->getResponseWithData($myCustomServiceMock)
}
));
$jobResult = $myCustomServiceMock->job();
// here some assertions on $jobResult
}
protected function getResponseWithData(&$myCustomServiceMock)
{
$response = new \stdClass;
if (isset($myCustomServiceMock->imported)) {
$response->data = false;
return $response;
}
$myCustomServiceMock->imported = true;
$response = new \stdClass;
$response->data = ['foo', 'bar'];
return $response;
}
}
Then while loop will be called only once and we be able to test without forever loop.
What's wrong with my code? I can't pass variable from my Auth middleware to controller. In controller, the attribute "null".
Index:
$c = new \Slim\Container($configuration);
$api = new \Slim\App($c);
$api->group('/', function () use ($api) {
$this->post('login', '\AuthController:login');
...
$this->post('getIngredientsCategories', '\IngredientsController:getIngredientsCategories');
})->add(new ApiAuthenticateController());
$api->run();
Middleware (ApiAuthenticateController)
use \Firebase\JWT\JWT;
class ApiAuthenticateController
{
public function __invoke($request, $response, $next)
{
$jwtDecoded = null;
$req = $request->getUri()->getPath();
$_req = RequestValidatorController::isRequestEnabled($req);
if ($_req !== false) {
if ($_req['login_required']) {
$jwt = filter_var($request->getHeaderLine('AUTHORIZATION'), FILTER_SANITIZE_STRING);
$jwt = explode(" ", $jwt)[1];
try {
$jwtDecoded = JWT::decode($jwt, JWT_SECRET, array('HS256'));
} catch (\Exception $e) {
return $this->deny_access($response);
}
}
$request = $request->withAttribute('foo', $jwtDecoded);
//HERE - attribute "foo" in $request exists - checked by var_dump()..
$response = $next($request, $response);
$response = $response->withHeader('Content-type', 'application/json');
return $response;
} else {
return $this->deny_access($response);
}
}
IngredientsController
class IngredientsController extends Controller
{
private $_ingredients;
public function __construct(\Interop\Container\ContainerInterface $container)
{
parent::__construct($container);
}
}
Controller
class Controller
{
private $request;
private $response;
protected $data;
protected $method;
protected $user;
public function __construct(Interop\Container\ContainerInterface $container)
{
$this->request = $container->get('request');
$this->response = $container->get('response');
$this->data = (object)Tools::stripInput($this->request->getParsedBody());
$this->method = $this->request->getUri()->getPath();
$this->user = $this->request->getAttribute('foo');
var_dump($this->user);
// returns NULL. if $this->request->getAttributes()... returns empty
}
}
Please note, that code is "cleaned" for this post and for better readability, some functions and conditions where deleted. Code is working - sorry for possible typos.
the problem is coming from here
in the middleware ApiAuthenticateController, you should change the code like this
$request = $request->withAttribute('foo', $jwtDecoded);
// you should return the $next here not to do anything else
return $next($request, $response);
and in your IngredientsController:getIngredientsCategories part of code you should return the
function getIngredientsCategories ($request, $response, $args){
//do your job...
$response = $response->withHeader('Content-type', 'application/json');
return $response;
}
and finally, in the constructor, you have don't have the actual $request after the middleware I think so you would have the $this->request->getAttributes() inside your getIngredientsCategories function
Hope it helps.
I'm using Guzzle's asynchronous request and have them implemented in a service that I would like to test now.
My method looks like this (pseudo, so if it's not 100% valid, please excuse)
public function getPlayer(string $uiid, array &$player = [])
{
$options['query'] = ['id' => $uiid];
$promise = $this->requestAsync('GET', $this->endpoint, $options);
$promise->then(function (ResponseInterface $response) use (&$player) {
$player = $response->getBody()->getContents();
});
return $players;
}
Now I want to test it, but I don't really know how to mock the callable, because I'm always getting the error
1) tzfrs\PlayerBundle\Tests\Api\Player\PlayerServiceTest::testGetPlayer
Prophecy\Exception\InvalidArgumentException: Expected callable or instance of PromiseInterface, but got object.
This is how I have it implemented currently
/** #var ObjectProphecy|PromiseInterface $response */
$promise = $this->prophesize(PromiseInterface::class);
$promise->then()->will($this->returnCallback(function (ResponseInterface $response) use (&$player){}));
Didn't work. And this
$this->returnCallback(function (ResponseInterface $response) use (&$player){})
didn't work either. Same error. And when simply trying a dummy callback
$promise->then(function(){});
I get the error Error: Call to a member function then() on string, even after ->reveal()ing the promise first. Any ideas?
I had another idea.
Make a dependency that will make what you make now in requestAsync();
And then create it's mock that will return another mock of promise.
class PromiseMock
{
private $response;
public function __construct(ResponseInterface $response)
{
$this->response = $response;
}
public function then($callable)
{
$callable($this->response);
}
}
test looks like
public function testGetPlayer()
{
$response = new Response(200, [], "Your test response");
$promiseMock = new PromiseMock($response);
$mockDependency = $this->getMockBuilder('YourDependencyClass')
->getMock()
->expects("requestAsync")->willReturn($promiseMock);
$service = new YouServiceClass($mockDependency);
$service->getPlayer("76245914-d56d-4bac-8419-9e409f43e777");
}
And in your class changes only
$promise = $this->someNameService->requestAsync('GET', $this->endpoint, $options);
I would inject a processor to your class and call it's callable. Check it out, the rest is quite obvious:
public function __construct(Processor $processor) {
$this->processor = $processor;
}
public function getPlayer(string $uiid, array &$player = [])
{
$options['query'] = ['id' => $uiid];
$promise = $this->requestAsync('GET', $this->endpoint, $options);
$promise->then([$this->processor, "processResponse"]);
$player = $this->processor->getPlayer();
return $players;
}
And processor:
class Processor {
private $player;
public function processResponse (ResponseInterface $response) {
$this->player = $response->getBody()->getContents();
}
public function getPlayer() { return $this->player;}
}
I have the following code and am trying to find a solution:
<?php
class t1 {
public $response = array();
public $request = array();
public function getRequest() {
return $this->request;
}
public function getResponse() {
return $this->response;
}
}
class t2 extends t1 {
public function run($f) {
$this->response = $f($this);
return $this;
}
}
$delegate = function($c)
{
// PLACEHOLDER
// This is the only place to update test code
// It's not allowed to modify any other section of this code
};
$t = new t2();
print_r(array("request" => $t->run($delegate)->getRequest(), "response" => $t->getResponse()));
?>
I assume $delegate is a dynamic function. Anyone able to walk me through this.
I'm thinking in PLACEHOLDER is should be:
I assume $delegate is a dynamic function.
Yes. $delagate is a PHP Closure.
If you define your function as:
$delegate = function($c)
{
};
and pass it to $t->run, then $c will be the $t instance.