A function returns another function in php - php

I dont know whether the question (the way I asked) is correct or not. I'm open for your suggestion. I want to know how exactly the following code works. If you want any details I can provide as much I want.
public function processAPI() {
if (method_exists($this, $this->endpoint)) {
return $this->_response($this->{$this->endpoint}($this->args));
}
return $this->_response("No Endpoint: $this->endpoint", 404);
}
private function _response($data, $status = 200) {
header("HTTP/1.1 " . $status . " " . $this->_requestStatus($status));
return json_encode($data);
}
private function _requestStatus($code) {
$status = array(
200 => 'OK',
404 => 'Not Found',
405 => 'Method Not Allowed',
500 => 'Internal Server Error',
);
return ($status[$code])?$status[$code]:$status[500];
}
/**
* Example of an Endpoint
*/
protected function myMethod() {
if ($this->method == 'GET') {
return "Your name is " . $this->User->name;
} else {
return "Only accepts GET requests";
}
}
Here $this->endpoint is 'myMethod' (a method I want to execute)
I pass the method which I want to execute in the url. The function catches the request process then call the exact method. I want to how it's works. Especially this line.
return $this->_response($this->{$this->endpoint}($this->args));

PHP Supports both variable functions and variable variables.
When it reaches you statement within processApi
return $this->_response($this->{$this->endpoint}($this->args));
PHP will resolve your endpoint variable, we'll replace it with myMethod which is in your example:
return $this->_response($this->myMethod($this->args));
As you can see, we're now calling a method which exists on your class. If you set the endpoint to something which didn't exist, it would throw an error.
If myMethod returns a string such as my name is bob then once $this->myMethod($this->args) executes PHP will resolve that value as the argument for $this->_response() resulting in:
return $this->_response('my name is bob');
Following that chain of events, the processAPI() method will finally return that string JSON encoded as that's what the _response method does.

Related

Is it appropriate to use a method within same object context ($this) that normally returns JSON response for client?

This May sound like a silly question but, I have a method that normally returns a JSON response to the client side. But now I need the same method within the class to prevent DRY. Something like:
public function deleteChild($id){
// delete the element with given id ...
if($success){
return response()->json(['success'=>'successfully deleted'], 200);
}else{
return response()->json(['error'=>'could not be deleted'], 422);
}
}
This method is already used by the client side. But now I have another one that needs this method as well. Something like:
public function deleteMaster($id){
$master = Master::find($id);
foreach($child as $master->children){
$child_json_response = $this->deleteChild($child->id);
$response_data = $child_json_response->getData();
if($response_data->error){
// handle child error response
// ...
}
// delete master itself
}
}
Here I can extract the response data with getData() and process it.
Is this the right way to use a sibling function that returns a JSON response (actually made for client-side) or should I create another method that returns direct results serving server-side only?
You should use a Response macro
Into a service provider's boot, add this:
\Illuminate\Http\Response::macro('deleteJson', function ($success) {
return $success
? $this->json(['success'=>'successfully deleted'], 200)
: $this->json(['error'=>'could not be deleted'], 422);
}
public function deleteChild($id){
// delete element with given id ...
return response()->deleteJson($success);
}

PHPunit method expected to be called 1 time, actually called 0 times

I've been stuck on this for a while and I'm not sure why PHPunit can't see that the function is being called.
This is the code I'm trying to test:
public function handle()
{
$path = $this->request->getPath();
$requestMethod = $this->request->getMethod();
if (!$path) {
$this->redirect('home');
} else if (!$this->isMethodPathFound($path, $requestMethod)) {
$this->redirect('404');
} else {
$handler = $this->getControllerFullName($this->routes[$path]['handler']);
if (is_callable($handler)) {
call_user_func($handler);
} else {
$this->redirect('404');
}
}
}
/**
* #param string $path
* #param int $statusCode
*/
public function redirect($path, $statusCode = 303)
{
if (defined('TESTING_ENVIRONMENT') && TESTING_ENVIRONMENT) {
return;
}
header(
'Location: ' . $this->request->getProtocol() .
$this->request->getHost() . '/' . $path,
true,
$statusCode
);
die();
}
The TESTING_ENVIRONMENT variable is set for the header function so it does not trigger on running PHPunit (I don't want to create another class to have that redirect function just to be able to mock it for one test) and this is the testing code:
public function testHandlePathIsEmpty()
{
$requestMock = $this->getMockBuilder('\services\Request')->getMock();
$requestMock->expects($this->once())->method('getPath')->willReturn('');
$requestMock->expects($this->once())->method('getMethod')->willReturn('GET');
$routerMock = $this->getMockBuilder('\services\Router')
->setConstructorArgs([$this->routes, $requestMock])
->enableProxyingToOriginalMethods()
->getMock();
$routerMock->expects($this->once())->method('redirect')
->with('asdasd')->willReturn(true);
$routerMock->handle();
}
The $routerMock object should definitely invoke the "redirect" function, and it says that it does not get invoked..even though when I var_dump/die inside the function, it does go inside of it.
Thanks for the help!
Though you hesitated to show the complete output of phpunit's error, your problem is very likely not that your method is not called, but that it is not called with all the expectations you defined.
Your code
$routerMock->expects($this->once())->method('redirect')
->with('asdasd')->willReturn(true);
translates to the following expectations: The method redirect must be called exactly once with an argument 'asdasd' and will return true.
From your testcode I do not see that there is asdasd passed to the redirect method. Your test will most likely succeed when you remove the with expectation.
Just to make this clear. If you have to mock the class u want to test, your code is way to complex and you should think about implementing your logic in another way.
How about not mocking the class you are actually testing, create the new instance by passing the Request and a Router Mock (Router mock might not have any logic since you are not going to use it) and then do the following in your code:
public function handle()
{
$request = $this->request;
$path = $request->getPath();
if (!$path) {
$this->redirect('home');
} else if (!$this->isMethodPathFound($path, $request->getMethod())) {
$this->redirect('404');
} else {
$handler = $this->getControllerFullName($this->routes[$path]['handler']);
if (is_callable($handler)) {
call_user_func($handler);
} else {
$this->redirect('404');
}
}
}
In your Unit-Test, you now can just test for
$requestMock
->expects($this->never())
->method('getMethod');
I see that this would only cover the second case to not being executed but the third one could happen aswell. Thats always a point why your code is not clean enough.
You should read something about KISS and SOLID to make your code more testable. This method is just too complex as you could test it correctly.

How to get method name when i know what method i call in PHP

The point is i have getDates() method and i want to get this method's name as string but not to run this method. Actually it looks like next:
$object->getResultExecutionMethod(convertmMethodNameToString($object->findDates()));
getResultExecutionMethod($methodName) {
switch($methodName) {
case convertmMethodNameToString($this->findDates()):
return $getDatesStatus;
break;
case convertmMethodNameToString($this->anotherMethodOfThisClass()):
return $anotherMethodOfThisClassStatus;
break;
}
}
In 1 class i have a lot of methods, and a lot of variables that comply this methods execution status. Calling convertmMethodNameToString() and putting there my method i want to get execution status by this method.
So how i can implement convertmMethodNameToString() function?
You could maybe benefit from the magical __call method. You could say that if you need a status for a certain method, you would call a method with the same name, but with the suffix "Status". The nice thing is that you don't actually have to create all those methods with "Status" at the end, but can use a trap for it.
Additionally, you can use __FUNCTION__ to get the name of the function that is running. This is not interesting for getting the status, but might be for setting it.
Here is some example code:
class myClass {
// Use an array to keep the statusses for each of the methods you have:
private $statusses = [
"findDates" => "my original status",
"anotherMethodOfThisClass" => "another original status"
];
public function findDates($arg) {
echo "Executing " . __FUNCTION__ . ".\n";
// Set execution status information:
$this->statusses[__FUNCTION__] = "last executed with argument = $arg";
}
// ... other methods come here
// Finally: magic method to trap all undefined method calls (like a proxy):
public function __call($method, $arguments) {
// Remove the Status word at the end of the method name
$baseMethod = preg_replace("/Status$/", "", $method);
// ... and see if now we have an existing method.
if(method_exists($this, $baseMethod)) {
echo "Returning execution status for $baseMethod.\n";
// Yes, so return the execution status we have in our array:
return $this->statusses[$baseMethod];
}
}
}
// Create object
$object = new myClass();
// Execute method
$object->findDates("abc");
// Get execution status for that method. This method does not really exist, but it works
$status = $object->findDatesStatus();
echo "Status: $status\n";
The above code outputs this:
Executing findDates.
Returning execution status for findDates.
Status: last executed with argument = abc
See it run on eval.in

Validating HTTP Response Codes in PHPUnit

I am writing unit tests for several methods which return HTTP response codes. I cannot find a way to assert an HTTP response code. Perhaps I am missing something obvious, or I am misunderstanding something about PHPUnit.
I am using PHPUnit 4.5 stable.
Relevant part of class Message:
public function validate() {
// Decode JSON to array.
if (!$json = json_decode($this->read(), TRUE)) {
return http_response_code(415);
}
return $json;
}
// Abstracted file_get_contents a bit to facilitate unit testing.
public $_file_input = 'php://input';
public function read() {
return file_get_contents($this->_file_input);
}
Unit test:
// Load invalid JSON file and verify that validate() fails.
public function testValidateWhenInvalid() {
$stub1 = $this->getMockForAbstractClass('Message');
$path = __DIR__ . '/testDataMalformed.json';
$stub1->_file_input = $path;
$result = $stub1->validate();
// At this point, we have decoded the JSON file inside validate() and have expected it to fail.
// Validate that the return value from HTTP 415.
$this->assertEquals('415', $result);
}
PHPUnit returns:
1) MessageTest::testValidateWhenInvalid
Failed asserting that 'true' matches expected '415'.
I'm unsure why $result is returning 'true' . . . especially as a string value. Also unsure what my 'expected' argument ought to be.
According to the docs you can call the http_response_code() method with no parameters to receive the current response code.
<?php
http_response_code(401);
echo http_response_code(); //Output: 401
?>
Therefore your test should look like:
public function testValidateWhenInvalid() {
$stub1 = $this->getMockForAbstractClass('Message');
$path = __DIR__ . '/testDataMalformed.json';
$stub1->_file_input = $path;
$result = $stub1->validate();
// At this point, we have decoded the JSON file inside validate() and have expected it to fail.
// Validate that the return value from HTTP 415.
$this->assertEquals(415, http_response_code()); //Note you will get an int for the return value, not a string
}

PHP Get corresponding data, with default and error handling

I have a normal HTML menu that passes a GET statement to the url.
<li>Home</li>
Just like this, al though this is ofcourse only 1 item of the entire menu.
In a seperated file I have an function that checks if an GET or POST statement exist,
and If it exist and is not NULL then it will give the value back to where it was called.
public function getFormVariable($value){
switch (strtoupper($_SERVER['REQUEST_METHOD'])) {
case 'GET':
if (isset($_GET[$value]) && $_GET[$value] != NULL) {
return $_GET[$value];
}
else{
return false;
}
break;
case 'POST':
if (isset($POST[$value]) && $POST[$value] != NULL) {
return $POST[$value];
}
else{
return false;
}
break;
default:
return false;
}
}
And with the following code it takes the get value and finds the corrosponding class
(every class is in a seperated file, and every class is 1 link in my menu)
In this class there is just some regular functions/data that gives the content of that page.
$class = loadClass($ConfigPage->getFormVariable('Page'));
$ConfigPage->SetProperty('content', $class);
function loadClass($Page){
$class_name = 'Content' . $Page;
if(!class_exists($class_name)){
return 'Error: Content has not been found.';
}
$class = new $class_name();
return $class;
}
Explaining: The menu gives a GET value of 'Contact' which is then check by GetFormVariable() and then the corresponding class is found which gives back the content that class holds.
Now my question:
When the function LoadClass() cant find the name of the class it was given through the GET statement, it should return a error string. But this is not happening. I get a beautiful big orange error from PHP saying the following:
Fatal error: Call to a member function render() on a non-object in
E:\Program files\wamp\www\BDW\Class\Html_Page.Class.php on line 147
Line 147 is where to object is called
echo $this->content->render();
The Render function is as it says a normal return function inside the content classes.
Why do i get this error, and how do i fix it?
Second question. If there is no GET statement in the url. It gives the exact same error. which is pretty logical. But how do i make it show ContentHome when there is no GET statement in the url, and an ERROR when the value of the GET statement is incorrect.
Thank you for reading,
If there is anything unclear please tell me. English is not my native language, and after all. I am here to learn.
EDIT:
My knowledge of PHP is not great enough, so i decided when a class can not be found. it brings you back to home without any error. which i wanted in the first place.
Here is my new code which is still not working, why?
$class = loadClass($ConfigPage->getFormVariable('Page'));
$ConfigPage->SetProperty('content', $class);
function loadClass($Page){
$class_name = 'Content' . $Page;
$Default_Class = 'ContentHome';
if(!class_exists($class_name)){
//echo 'Error: Content has not been found.';
$class = new $Default_Class();
return $class;
}
else{
$class = new $class_name();
return $class;
}
$ConfigPage->Render();
}
It's happening because of this line :
if(!class_exists($class_name)){
return 'Error: Content has not been found.';
}
In the case the class doesn't exist, you're returning a string from the function, and afterwards trying to call a method render() on it. You can fix it either by changing this behaviour (don't return an error string, but use an Exception or trigger_error() ) or checking that the function loadClass() does return a valid object through is_object() for example.
Regarding your second question, you should expand your tests in loadClass() to handle the empty GET variable case, and substitute the empty string with a "Home" default value.
Update :
Example usage for is_object in your case :
$class = loadClass($ConfigPage->getFormVariable('Page'));
if(! is_object($class)) {
// if $class is not an object, then something went wrong above
throw new \Exception('Invalid data returned from loadClass()');
}
$ConfigPage->SetProperty('content', $class);

Categories