I wrote an API in PHP. It executes pretty fast for my purpose (3s) when I call it using the browser. However if I call it using another PHP script (which i wrote to do testing) it takes a looong time (24s) for each request! I use curl to call the URL. Anybody knows whats happening ?
System Config :
Using WAMP to run the PHP.
Hosted on local computer.
Solutions tried :
Disabled all firewalls
Added the option curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
I even wrote a python script to call the PHP API and it also takes a long time. Seems like browser gives the best response time.
Any help is appreciated.
Updated with the code :
<?php
// Class to handle all Utilities
Class Utilities{
// Make a curl call to a URL and return both JSON & Array
public function callBing($bingUrl){
// Initiate curl
$ch = curl_init();
// Disable SSL verification
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
// Will return the response, if false it print the response
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Set the url
curl_setopt($ch, CURLOPT_URL,$bingUrl);
// Performance Tweak
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12');
session_write_close();
// Execute
$bingJSON=curl_exec($ch);
// Closing
curl_close($ch);
$bingArray = json_decode($bingJSON,true);
return array( "array" => $bingArray , "json" => $bingJSON );
}
}
?>
<?php
// The Test script
include_once('class/class.Utilities.php');
$util = new Utilities();
echo "<style> td { border : thin dashed black;}</style>";
// Test JSON
$testJSON = '
{
"data" : [
{ "A" : "24324" , "B" : "64767", "expectedValue" : "6.65" , "name" : "Test 1"},
{ "A" : "24324" , "B" : "65464", "expectedValue" : "14" , "name" : "Test 2"}
]
}
';
$testArray = json_decode($testJSON, TRUE);
echo "<h1> Test Results </h1>";
echo "<table><tr><th>Test name</th><th> Expected Value</th><th> Passed ? </th></tr>";
$count = count($testArray["data"]);
for ($i=0; $i < $count ; $i++) {
$url = "http://localhost/API.php?txtA=".urlencode($testArray["data"][$i]["A"])."&txtB=".urlencode($testArray["data"][$i]["B"]);
$result = $util->callutil($url);
if($testArray["data"][$i]["expectedValue"] == $result["value"])
$passed = true;
else
$passed = false;
if($passed)
$passed = "<span style='background:green;color: white;font-weight:bold;'>Passed</span>";
else
$passed = "<span style='background:red;color: white;font-weight:bold;'>Failed</span>";
echo "<tr><td>".$testArray["data"][$i]["name"]."</td><td>".$testArray["data"][$i]["expectedValue"]."</td><td>$passed</td></tr>";
}
echo "</table>";
?>
There is an overhead cost involved in starting up the interpreter and parsing the code (whether php, python, ruby, etc). When you have the code running in a server process that startup cost is payed when the server starts initially, and the application logic (plus some minor request/response overhead) is simply executed on the request. When running the code manually, however, that additional startup overhead happens before you code can be run and causes the slowness you are seeing. This is the reason that mod_php, and mod_wsgi exist (as opposed to frameworks that use the CGI api).
Related
I have a small php script: domain1.com/script1.php
//my database connections, check functions and values, then, load:
$variable1 = 'value1';
$variable2 = 'value2';
if ($variable1 > 5) {
$variable3 = 'ok';
} else {
$variable3 = 'no';
}
And I need to load the variables of this script on several other sites of mine (different domains, servers and ips), so I can control all of them from a single file, for example:
domain2.com/site.php
domain3.com/site.php
domain4.com/site.php
And the "site.php" file needs to call the variable that is in script1.php (but I didn't want to have to copy this file in each of the 25 domains and edit each of them every day):
site.php:
echo $variable1 . $variable2 . $variable3; //loaded by script.php another domain
I don't know if the best and easiest way is to pass this: via API, Cookie, Javascript, JSON or try to load it as an include even from php, authorizing the domain in php.ini. I can't use get variables in the url, like ?variable1=abc.
My area would be php (but not very advanced either), and the rest I am extremely layman, so depending on the solution, I will have to hire a developer, but I wanted to understand what to ask the developer, or maybe the cheapest solution for this (even if not the best), as they are non-profit sites.
Thank you.
If privacy is not a concern, then file_get_contents('https://example.com/file.php') will do. Have the information itself be passed as JSON text it's the industry standard.
If need to protect the information, make a POST request (using cURL or guzzle library) with some password assuming you're using https protocol.
On example.com server:
$param = $_REQUEST("param");
$result = [
'param' => $param,
'hello' => "world"
];
echo json_encode($data);
On client server:
$content = file_get_contents('https://example.com/file.php');
$result = json_decode($content, true);
print_r ($result);
For completeness, here's a POST request:
//
// A very simple PHP example that sends a HTTP POST to a remote site
//
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,"http://www.example.com/file.php");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,
"postvar1=value1&postvar2=value2&postvar3=value3");
// In real life you should use something like:
// curl_setopt($ch, CURLOPT_POSTFIELDS,
// http_build_query(array('postvar1' => 'value1')));
// Receive server response ...
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$server_output = curl_exec($ch);
curl_close ($ch);
$result = json_decode($server_output , true);
I have a limit of 25 requests/min from PUBGs official API. For some reason instead of it requesting twice for each search its using up 4 requests. I can't figure out why. I have checked that the code isn't running twice. Only once, but still it's requesting 4 times.
UPDATE:
I tried making a separate page and apparently there is a bug somewhere calling my function twice. Still don't know why but I'm now 99% sure it's not the function itself.
Code For My Request
function getProfile($profileName, $region, $seasonDate){
// Just check if there is an acctual user
if($profileName === null){
$data->error = "Player Not Found";
$data->noUser = true;
return $data;
}else{
$season = "division.bro.official.".$seasonDate;
/*
Get The UserID
*/
$ch = curl_init("https://api.pubg.com/shards/$region/players?filter[playerNames]=$profileName");
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Authorization: Bearer APIKEY',
'Accept: application/vnd.api+json'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$rawData = json_decode(curl_exec($ch), true);
$data->playerId = $rawData["data"][0]["id"];
curl_close($ch);
// Testing if user exists
if($rawData["errors"][0]["title"] === "Not Found"){
$data->noUser = true;
$data->error = "Player Not Found";
return $data;
}else{
/*
Get The acctual stats
*/
$ch = curl_init("https://api.pubg.com/shards/$region/players/$data->playerId/seasons/$season");
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Authorization: Bearer APIKEY',
'Accept: application/vnd.api+json'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$data->playerDataJSON = curl_exec($ch);
$data->playerData = json_decode($data->playerDataJSON, true);
curl_close($ch);
return $data;
}
}
}
This is how it's getting called
if (isset($_POST['search-username'])) {
$username = $_POST['search-username'];
header("Location: /profile/$username/pc-na/2018-01/overall/tpp");
die();
}
In The actual profile php
$data = getProfile($page_parts[1], $page_parts[2], $page_parts[3]);
absolutely sure it's only called once? set a lock on it. change it to
function getProfile($profileName, $region, $seasonDate){
static $once=true;
if($once!==true){
throw new \LogicException("tried to run getProfile() twice!");
}
$once=false;
Shortly after I figure out it's not the function I realized that the culprit was an empty script I was calling. I knew this script created an error which I didn't really care about since it was empty and I had no idea why it was creating the error. For some obscure reason this script created the error. I'll just make a lesson out of it to always fix the smallest errors.
I've also been seeing this behavior - a script with a single curl_exec() request gets called twice.
The strange thing is this was only happening when running on localhost (under a wampp installation) but when run from any other webserver was fine and it is just called once.
I never managed to debug it completely but it seems to be an issue with the local server so test elsewhere if you are seeing this.
I have searched for a while and found many similar situations occurring for others; however, I cannot find a solution that works for me.
I have the latest Ubuntu, Apache, and PHP versions running on my server. I checked for updates and after installing them, nothing improved. Here is my code:
$ip = getServerIP($stand).":8080";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$ip);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'server-id: '.$stand,
'script: '.$script,
'device: '.$device,
'api-key: '.file_get_contents(getcwd() .'/../secure/serverkey')
));
curl_exec($ch)
curl_close($ch);
$stand is a parameter the parent function takes. Everything works fine up until I call curl_exec. This code runs when a form has been submitted. Chrome hangs on the form page for about 2 minutes, and then it finally goes to the page this code is on. Any guidance?
Update: The problem was on the C# side; this question is no longer relevant. Sorry, I am working with someone else and they are handling the C#, so I don't know what the issue was.
This is too bulky for comments, so here is I setup verbose CURL in my ApiHelper class:
$st = microtime(true);
$verbiage = null;
if ($this->verbose) {
// write out the curl debug stuff
curl_setopt($ch , CURLINFO_HEADER_OUT , false);
curl_setopt($ch , CURLOPT_VERBOSE , true);
$verbiage = fopen('php://temp' , 'w+');
curl_setopt($ch , CURLOPT_STDERR , $verbiage);
}
$resp = curl_exec($ch);
$end = microtime(true); // get as float
$delta = 1000.0 * ($end - $st); // treat as float
if (Config::getCurrentConfig()->options->logServerResponseTimes) {
$this->getInstanceLogger()->debug("WS Round trip took " . sprintf("%.2f" , $delta) . " ms.");
}
if ($this->verbose) {
// rewind and log the verbose output
rewind($verbiage);
$verboseLog = stream_get_contents($verbiage);
$this->getInstanceLogger()->debug("Verbose cURL : \n$verboseLog");
fclose($verbiage);
}
curl_close($ch);
return $resp;
Finally, xdebug is the protocol suite in support of symbolic debugging of php processes. It slows down things some, but mostly tries to initiate outbound connexions all the time between the running (under debug) php process and a listening process. Look in your php.ini files (apache, php-fpm, and cli), and turn off xdebug if it is there.
I'm trying to find a way to only quickly access a file and then disconnect immediately.
So I've decided to use cURL since it's the fastest option for me. But I can't figure out how I should "disconnect" cURL.
With the code below, Apache's access logs says that the file I tried accessing was indeed accessed, but I'm feeling a little iffy about this, because when I just run the while loop without breaking out of it, it just keeps looping. Shouldn't the loop stop when cURL has finished fetching the file? Or am I just being silly; is the loop just restarting constantly?
<?php
$Resource = curl_init();
curl_setopt($Resource, CURLOPT_URL, '...');
curl_setopt($Resource, CURLOPT_HEADER, 0);
curl_setopt($Resource, CURLOPT_USERAGENT, '...');
while(curl_exec($Resource)){
break;
}
curl_close($Resource);
?>
I tried setting the CURLOPT_CONNECTTIMEOUT_MS / CURLOPT_CONNECTTIMEOUT options to very small values, but it didn't help in this case.
Is there a more "proper" way of doing this?
This statement is superflous:
while(curl_exec($Resource)){
break;
}
Instead just keep the return value for future reference:
$result = curl_exec($Resource);
The while loop does not help anything. So now to your question: You can tell curl that it should only take some bytes from the body and then quit. That can be achieved by reducing the CURLOPT_BUFFERSIZE to a small value and by using a callback function to tell curl it should stop:
$withCallback = array(
CURLOPT_BUFFERSIZE => 20, # ~ value of bytes you'd like to get
CURLOPT_WRITEFUNCTION => function($handle, $data) {
echo "WRITE: (", strlen($data), ") $data\n";
return 0;
},
);
$handle = curl_init("http://stackoverflow.com/");
curl_setopt_array($handle, $withCallback);
curl_exec($handle);
curl_close($handle);
Output:
WRITE: (10) <!DOCTYPE
Another alternative is to make a HEAD request by using CURLOPT_NOBODY which will never fetch the body. But it's not a GET request.
The connect timeout settings are about how long it will take until the connect times out. The connect is the phase until the server accepts input from curl and curl starts to know about that the server does. It's not related to the phase when curl fetches data from the server, that's
CURLOPT_TIMEOUT The maximum number of seconds to allow cURL functions to execute.
You find a long list of available options in the PHP Manual: curl_setoptĀDocs.
Perhaps that might be helpful?
$GLOBALS["dataread"] = 0;
define("MAX_DATA", 3000); // how many bytes should be read?
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://www.php.net/");
curl_setopt($ch, CURLOPT_WRITEFUNCTION, "handlewrite");
curl_exec($ch);
curl_close($ch);
function handlewrite($ch, $data)
{
$GLOBALS["dataread"] += strlen($data);
echo "READ " . strlen($data) . " bytes\n";
if ($GLOBALS["dataread"] > MAX_DATA) {
return 0;
}
return strlen($data);
}
I'm trying to convert this PHP cURL function to work with my rails app. The piece of code is from an SMS payment gateway that needs to verify the POST paramters. Since I'm a big PHP noob I have no idea how to handle this problem.
$verify_url = 'http://smsgatewayadress';
$fields = '';
$d = array(
'merchant_ID' => $_POST['merchant_ID'],
'local_ID' => $_POST['local_ID'],
'total' => $_POST['total'],
'ipn_verify' => $_POST['ipn_verify'],
'timeout' => 10,
);
foreach ($d as $k => $v)
{
$fields .= $k . "=" . urlencode($v) . "&";
}
$fields = substr($fields, 0, strlen($fields)-1);
$ch = curl_init($verify_url); //this initiates a HTTP connection to $verify_url, the connection headers will be stored in $ch
curl_setopt($ch, CURLOPT_POST, 1); //sets the delivery method as POST
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields); //The data that is being sent via POST. From what I can see the cURL lib sends them as a string that is built in the foreach loop above
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); //This verifies if the target url sends a redirect header and if it does cURL follows that link
curl_setopt($ch, CURLOPT_HEADER, 0); //This ignores the headers from the answer
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //This specifies that the curl_exec function below must return the result to the accesed URL
$result = curl_exec($ch); //It ransfers the data via POST to the URL, it gets read and returns the result
if ($result == true)
{
//confirmed
$can_download = true;
}
else
{
//failed
$can_download = false;
}
}
if (strpos($_SERVER['REQUEST_URI'], 'ipn.php'))
echo $can_download ? '1' : '0'; //we tell the sms sever that we processed the request
I've googled a cURL lib counterpart in Rails and found a ton of options but none that I could understand and use in the same way this script does.
If anyone could give me a hand with converting this script from php to ruby it would be greatly appreciated.
The most direct approach might be to use the Ruby curb library, which is the most straightforward wrapper for cURL. A lot of the options in Curl::Easy map directly to what you have here. A basis might be:
url = "http://smsgatewayadress/"
Curl::Easy.http_post(url,
Curl::PostField.content('merchant_ID', params[:merchant_ID]),
# ...
)