I am using the curl option CURLOPT_HEADERFUNCTION along with a closure to perform some basic data manipulation. As per the php docs, the function / callback must return the length of the header on each call.
$headers = [];
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADERFUNCTION, function($curl, $headerLine) use (&$headers) {
$headers[] = $headerLine;
return strlen($headerLine);
});
$response = curl_exec($curl);
curl_close($curl);
return $headers;
I am using references to return the value which works fine. I am just curious if there are other ways to return this value without using references or using a callback?
You can specify the callback function to be a class method:
curl_setopt($curl, CURLOPT_HEADERFUNCTION, array($this, 'someOtherFunction'));
In that other function, you can have access to anything $this has access to:
protected function someOtherFunction($curl, $headerLine)
{
$this->headers[] = $headerLine;
return strlen($headerLine);
}
* This answer assumes you're inside of a class context to begin with
Related
I decided to write a client that sends http requests to the API. There are 3 types of requests: GET, POST, PUT. We need to write unit tests using phpunit, which will allow us to test the functionality without writing an API. My first thought was to use a mock object. Having read enough literature, I can’t understand in any way how to do this. As I understand it, I need to make a stub for the API wherever my requests go. Please tell me in which direction to move in order to solve the problem.
<?php
namespace Client;
class CurlClient implements iClient
{
private $domain;
public function __construct($domain = "http://example.com")
{
$this->domain = $domain;
}
public function getAllComments()
{
$ch = curl_init($this->domain.'/comments');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$comments = curl_exec($ch);
$comments = json_decode($comments);
curl_close($ch);
return $comments;
}
public function addNewComment($data)
{
$ch = curl_init($this->domain.'/comment');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$statusCode = (string)$statusCode;
$statusCode = (int)$statusCode[0];
curl_close($ch);
return $statusCode == 2 ? true : false;
}
public function updateComment($id, $data)
{
$ch = curl_init($this->domain.'/comment/'.$id);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$statusCode = (string)$statusCode;
$statusCode = (int)$statusCode[0];
curl_close($ch);
return $statusCode == 2 ? true : false;
}
}
Here is a simple mock example using phpunit. phpunit's mocking capabilities are extensive, for more info phpunit documentation - test doubles
<?php
use PHPUnit\Framework\TestCase;
// Use the getMockBuilder() method that is provided by the
// PHPUnit\Framework\TestCase class to set up a mock object
// for the CurlClient object.
class CurlClientTest extends TestCase
{
public function testAddNewComment()
{
// Create a mock for the CurlClient class,
// only mock the update() method.
$client = $this->getMockBuilder(CurlClient::class)
->setMethods(['addNewComment'])
->getMock();
$map = [
['hello', true],
[123, false]
];
$client->method('addNewComment')->will($this->returnValueMap($map));
$this->assertEquals(true, $client->addNewComment('hello'));
$this->assertEquals(false, $client->addNewComment(123));
}
}
When using PHP's curl API, if I accidentally use a string with the CURLOPT_HTTPHEADER curl option
curl_setopt($ch, CURLOPT_HTTPHEADER, 'User-Agent: php-curl');
PHP will scold me
Warning: curl_setopt(): You must pass either an object or an array with the CURLOPT_HTTPHEADER argument in /path/to/test.php on line 32
I know I can fix this using an array
$headers = [];
$headers[] = 'User-Agent: php-curl';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
However, the warning seems to indicate I could also be passing curl_setopt an object. Is this possible?
I've tried with both an stdClass and ArrayObject, but neither seems to work. Is this just a misleading warning message, or is there a way to use curl_setopt with an object?
Update Turns out I had a PHP extension installed that was blocking the standard behavior. ArrayObjects work fine in this function. I can't close or delete this question, so hopefully this warning will avoid confusion.
You need to pass an object that implements JsonSerializable.
class Testing implements JsonSerializable
{
protected $headers = [];
public function __construct($headers)
{
$this->headers = $headers;
}
public function jsonSerialize()
{
return $this->headers;
}
}
Then you can pass it to your CURLOPT_HTTPHEADER parameter:
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPHEADER, new Testing(['User-Agent: php-curl']));
I am trying to get a response from MPESA payment API using laravel but I am getting an error . My code is as below
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class MPESA_AUTH extends Controller
{
public function Authorize(){
$url = 'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials';
$CONSUMER_KEY= 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$CONSUMER_SECRET= 'xxxxxxxxxxxx';
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
$credentials = base64_encode($CONSUMER_KEY,$CONSUMER_SECRET);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Authorization: Basic '.$credentials)); //setting a custom header
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
$curl_response = curl_exec($curl);
$curl_json=json_decode($curl_response);
return $curl_json;
}
}
The error am getting is as below
The Base controller uses Illuminate\Routing\Controller trait which has an 'authorize()' function. Your function declaration is clashing with it.
Change your controller method name to anything else(other than 'authorize') and you should be good to go
You should use a different function name in place of "Authorize". This is because "authorize" in controllers is a preserved name used in the parent class Controller.
I've got an app which works with Digits as authentication.
Client-side works perfectly, but I'm not able to make the server user authentication through oAuth.
My server is developed with Laravel, so it's PHP.
My endpoint is under https, so everything should be ready to make the call.
I solved by myself!
Here's the code that makes correctly the authentication through Digits O-Auth.
The function that makes authentication is inside an AuthManager class and I call in this way:
$obj = AuthManager::verifyUser($request->header('X-Auth-Service-Provider'),$request->header("X-Verify-Credentials-Authorization"));
And here's the function that makes the magic:
public static function verifyUser ($xAuthServiceProvider, $xVerifyCredentialsAuthorization)
{
$curl = curl_init();
curl_setopt($curl,CURLOPT_URL, $xAuthServiceProvider);
curl_setopt($curl,CURLOPT_HTTPHEADER, array(
'Content-length: 0',
'Content-type: application/json',
'Authorization: '.$xVerifyCredentialsAuthorization,
));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$content = curl_exec($curl);
$info = curl_getinfo($curl);
curl_close($curl);
$obj = json_decode($content, true);
return $obj;
}
If you have any problem, don't hesitate to write here!
EDIT WITH EXAMPLE FUNCTION ABOUT HOW TO GET DATA FROM O-AUTH
public function authenticate (Request $request)
{
$obj = AuthManager::verifyUser($request->header('X-Auth-Service-Provider'),$request->header("X-Verify-Credentials-Authorization"));
if(isset($obj["errors"]))
{
return "error!";
}
$digits_token = $obj["access_token"]["token"];
$digitsId = $obj["id"];
/*
the variables above are returned by O-auth server-server authentication
*/
}
<?php
require_once 'Zend/Session/Namespace.php';
class ApiController extends Zend_Rest_Controller
{
public function init()
{
$this->_helper->layout->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
}
public function indexAction()
{
$query=$this->getRequest()->getParam('query');
$this->getResponse()
->appendBody("hi");
$this->getResponse()->setHttpResponseCode(200);
}
public function getAction()
{
$query=$this->getRequest()->getParam('id');
$this->getResponse()
->appendBody($query);
}
public function postAction()
{
$this->getResponse()
->setHttpResponseCode(200)
->appendBody("From postAction() creating the requested article");
}
public function putAction()
{
$this->getResponse()
->appendBody("From putAction() updating the requested article");
}
public function deleteAction()
{
$this->getResponse()
->appendBody("From deleteAction() deleting the requested article");
}
}
Above is my REST API I am trying to call it from php curl but I don't know how to call post method.
I have also made entry in bootsrap to default module\ using rest route.
Here is a snippet of my code:
<?php
$ch = curl_init('http://apanel3.newfront.local/api');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 4);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)");
$curlresult = curl_exec($curl_connection);
print_r($curlresult);
?>
I am trying to call my api using following curl code. It is calling indexAction. Even thought i have set curlopt_post to true, I am not getting desired output.
I believe there are lot of examples for php curl + post. Do you know how to access your actions (make http calls generally, without curl) ?
Here is another answer to the link to the similar question.
If you are trying to access your API from another zend based application and want to use zend inbuilt method then, you should check Zend_Http_Client there is an adapter for curl if you want to specifically use curl adapter.
EDIT:
On your client side:
<?php
//
// A very simple PHP example that sends a HTTP POST to a remote site
//
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "'http://apanel3.newfront.local/api'");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "query='where post_parameter = query'");
// receive server response ...
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$server_output = curl_exec($ch);
curl_close($ch);
// further processing ....
if($server_output == 'OK') {
echo 'Test passed';
} else {
echo $server_output;
}
?>
On your API side for indexAction:
public function indexAction()
{
$query=$this->getRequest()->getParam('query');
if($this->getRequest()->isPost()) {
// method == post, do your processing whatever required here ....
$this->_forward('post',null,null,$query);
} elseif ($this->getRequest()->getMethod() == 'GET') {
// method == get
$this->_forward('get',null,null,$query);
}else {
// bad request response code 400
$this->getResponse()
->appendBody("not a valid request");
$this->getResponse()->setHttpResponseCode(400);
}
}
Edit:
My mistake I didn't realize you were extending Zend_Rest_Controller, your code for controller seems fine. Now you probably know about making curl request via PHP.
On how to make PUT Request please check this question