Skip to content

Instantly share code, notes, and snippets.

@matthijn
Created January 3, 2014 18:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save matthijn/8243747 to your computer and use it in GitHub Desktop.
Save matthijn/8243747 to your computer and use it in GitHub Desktop.
Curl emulator for App Engine. This is in no way a complete emulation, but it is enough to make the Podio PHP API Client work.
<?php
/* A quick emulator for common curl function so code based on CURL works on AppEngine */
if(!function_exists('curl_init'))
{
// The curl option constants, when there is no curl they are not defined so we define them here
// Some options don't do anything, they just prefent crashes when they are here (see the setOpt method for the support of different options)
define('CURLOPT_RETURNTRANSFER', 'CURLOPT_RETURNTRANSFER');
define('CURLOPT_SSL_VERIFYPEER', 'CURLOPT_SSL_VERIFYPEER');
define('CURLOPT_SSL_VERIFYHOST', 'CURLOPT_SSL_VERIFYHOST');
define('CURLOPT_USERAGENT', 'CURLOPT_USERAGENT');
define('CURLOPT_HEADER', 'CURLOPT_HEADER');
define('CURLOPT_CUSTOMREQUEST', 'CURLOPT_CUSTOMREQUEST');
define('CURLOPT_POST', 'CURLOPT_POST');
define('CURLOPT_POSTFIELDS', 'CURLOPT_POSTFIELDS');
define('CURLOPT_HTTPHEADER', 'CURLOPT_HTTPHEADER');
define('CURLOPT_URL', 'CURLOPT_URL');
// curl info constants
define('CURLINFO_HEADER_SIZE', 'CURLINFO_HEADER_SIZE');
define('CURLINFO_HTTP_CODE', 'CURLINFO_HTTP_CODE');
define('CURLINFO_HEADER_OUT', 'CURLINFO_HEADER_OUT'); // This seems to be an option?
define('CURLINFO_TOTAL_TIME', 'CURLINFO_TOTAL_TIME');
class AECurl
{
// Storing the result in here
private $result;
// The headers of the result will be stored here
private $responseHeader;
public function AECurl($url)
{
$this->CURLOPT_URL = $url;
}
public function setOpt($option, $value)
{
$this->$option = $value;
}
public function getInfo($opt=0)
{
if(!$this->result)
{
$this->fetchResult();
}
if($opt == CURLINFO_HEADER_SIZE)
{
// Calculate header size
$responseSize = 0;
foreach($this->responseHeader as $header)
{
$responseSize += (strlen($header) + 1); // The one is for each newline
}
return $responseSize;
}
if($opt == CURLINFO_HTTP_CODE)
{
// Return the header status code
$matches = array();
preg_match('#HTTP/\d+\.\d+ (\d+)#', $this->responseHeader[0], $matches);
return intval($matches[1]);
}
else
{
throw new \Exception("No support in Curl wrapper for: " . $opt);
}
}
public function exec()
{
$this->fetchResult();
// Curl normally returns the headers with the content, so that is what we are doing here
$headers = implode("\n", $this->responseHeader);
$fullResult = $headers . "\n" . $this->result;
if($this->CURLOPT_RETURNTRANSFER == false)
{
echo $fullResult;
}
else
{
return $fullResult;
}
}
private function fetchResult()
{
$parsedUrl = parse_url($this->CURLOPT_URL);
// Create the context for this request based on the curl parameters
// Determine the method
if(!$this->CURLOPT_CUSTOMREQUEST && $this->CURLOPT_POST)
{
$method = 'POST';
}
else
{
$method = $this->getValue(CURLOPT_CUSTOMREQUEST, 'GET');
}
// Add the post header if type is post and it has not been added
if($method == 'POST')
{
if(is_array($this->getValue(CURLOPT_HTTPHEADER)))
{
$found = false;
foreach($this->CURLOPT_HTTPHEADER as $header)
{
if(strtolower($header) == strtolower('Content-type: application/x-www-form-urlencoded'))
{
$found = true;
}
}
}
else
{
$this->CURLOPT_HTTPHEADER[] = 'Content-type: application/x-www-form-urlencoded';
}
}
// Determine the content which can be an array or a string
if(is_array($this->CURLOPT_POSTFIELDS))
{
$content = http_build_query($this->CURLOPT_POSTFIELDS);
}
else
{
$content = $this->CURLOPT_POSTFIELDS;
}
// 'http' instead of $parsedUrl['scheme']; https doest work atm
$options = array(
'http' => array(
'method' => $method,
'header' => $this->getValue(CURLOPT_HTTPHEADER),
'content' => $content
)
);
// SSL settings when set
// if($parsedUrl['scheme'] == 'https')
// {
// $context['https']['ssl'] = array(
// 'verify_peer' => $this->getValue(CURLOPT_SSL_VERIFYPEER, false)
// );
// }
$context = stream_context_create($options);
try
{
$this->result = file_get_contents($this->CURLOPT_URL, false, $context);
}
catch(\exception $e)
{
$this->result = null;
}
$this->responseHeader = $http_response_header;
}
private function getValue($value, $default = null)
{
if($this->$value)
{
return $this->$value;
}
return $default;
}
}
function curl_init($url = null)
{
return new AECurl($url);
}
function curl_setopt($ch, $option, $value)
{
$ch->setOpt($option, $value);
}
function curl_exec($ch)
{
return $ch->exec();
}
function curl_getinfo($ch, $opt=0)
{
return $ch->getInfo($opt);
}
}
@mickaelramanitrera
Copy link

Hi! Very useful piece! :) I'm using it on GAE for a podio php implementation. Everything works fine locally...However, when I deploy, I got errors such as "Invalid headers. Must be a string" on file_get_contents in curlEmulator.php. Is that something you ever encounter?

Thanks!

@tzmartin
Copy link

tzmartin commented Jul 2, 2014

Re: "Invalid headers. Must be a string"

Lines 113, 141 - Try changing the associative header array to a string (without url encoding):

if (is_array($this->getValue(CURLOPT_HTTPHEADER))) {
      array_walk($this->getValue(CURLOPT_HTTPHEADER), create_function('&$i,$k','$i="$k: $i";'));
      $this->CURLOPT_HTTPHEADER[] = implode($this->getValue(CURLOPT_HTTPHEADER),"\r\n");      
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment