I have a code that uses pthreads v3.1.6 with PHP 7.0.22. The issue I'm having is that the threads do not return array values. My code is as follows
$threadCount = 1;
$url_idList = range(1,2000);
$url_idListChunked = array_chunk($url_idList, $threadCount);
class WorkerThreads extends Thread {
private $threadName, $url_id;
public function __construct($threadName,$url_id) {
$this->threadName = $threadName;
$this->url_id = $url_id;
$this->result = [];
}
public function run() {
if ($this->threadName && $this->url_id) {
printf('%sLoading URL #: %s' . "\n", $this->threadName, $this->url_id);
$this->result = send_request('GET',$this->url_id,NULL,$this->threadName);
}
}
}
while(count($url_idListChunked)){
$url_idListChunk = array_shift($url_idListChunked);
$workers = [];
foreach (range(0,count($url_idListChunk)-1) as $i) {
$threadName = "Thread #".$i.": ";
$workers[$i] = new WorkerThreads($threadName,$url_idListChunk[$i]);
$workers[$i]->start();
}
foreach (range(0,count($url_idListChunk)-1) as $i) {
$workers[$i]->join();
print_r($workers[$i]);
exit();
echo $workers[$i]['threadName']."Result for URL #: ".$workers[$i]['url_id']."\n";
}
}
function send_request($method,$url_id,$data,$threadName=NULL){
$url = 'https://www.example.tld/?id='.$url_id;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, TRUE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_TIMEOUT, 3);
if(!$data && $method=='POST'){
$data = generate_post_data();
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
$response = curl_exec($ch);
while((curl_errno($ch) == 6 OR curl_errno($ch) == 28)){
$response = curl_exec($ch);
echo $threadName.'Curl error #'.curl_errno($ch).' - ' . curl_error($ch)." Retrying.\n";
sleep(2);
}
curl_close($ch);
$result['data'] = $response;
return $result;
}
When I get try to print_t($workers) I get the following error message Uncaught RuntimeException: pthreads detected an attempt to connect to an object which has already been destroyed. Why do I lose the array results? It seems like threads have no problem passing the strings back.
Hie, I am not completely sure using start->join method is the preferred way to go for what you want to achieve? I think you need to use the pool collectable method that is made for in pthreads.
Here is an example that might inspire you in your work if you want to collect results from chunked batches. I personally use it intensively and this is the fastest method to push pthreads into its limits. Careful not to push the pool number over your CPU threads (in this example it s 10 cores).
If I may say regarding your code, never try to output something on screen from a pthreads worker, it will surely mess around. Return the object and echo it on collection. Make sure you have result has public in your class to allow returned object.
Not to mention multi-curl that might be the fastest and appropriate way of doing it.
/* pthreads batches */
$batches = array();
$nbpool = 20; // cpu 10 cores
/* job 1 */
$list = [/* data1 */];
$url_idList[] = array_chunk($list, 5000);
/* job 2 */
$list2 = [/* data2 */];
$url_idList[] = array_chunk($list, 10000);
/* final collected results */
$resultFinal = [];
/* loop across batches */
foreach ($url_idList as $key => $url_idListChunked) {
$url_idListChunk = array_shift($url_idListChunked);
/* for intermediate collection */
$data[$key] = [];
/* how many workers */
$workCount = count($url_idListChunk);
/* set pool job up to max cpu capabilities */
$pool = new Pool($nbpool, Worker::class);
/* pool cycling submit */
foreach (range(1, $workCount) as $i) {
$chunck = $url_idListChunk[$i - 1];
$pool->submit(new WorkerThreads(($i - 1), $chunck));
}
/* on collection cycling */
$collector = function (\Collectable $work) use (&$data) {
/* is worker complete ? */
$isGarbage = $work->isGarbage();
/* worker complete */
if ($isGarbage) {
$result = $work->result;
$info = $work->info;
$data[$key] = $result;
/* echo result info outside worker */
echo($info);
}
return $isGarbage;
};
do {
/* collection on pool stack */
$count = $pool->collect($collector);
$isComplete = count($data) === $workCount;
} while (!$isComplete);
/* push stack results */
array_push($resultFinal, $data);
/* close pool */
$pool->shutdown();
}
class WorkerThreads extends \Threaded implements \Collectable {
private $url_id;
private $isGarbage;
private $threadName;
public $result;
public $info;
public function __construct($i, $url_id) {
$this->threadName = "Thread #" . $i . ": ";
$this->url_id = $url_id;
}
public function run() {
if ($this->threadName && $this->url_id) {
$this->result = send_request('GET', $this->url_id, NULL, $this->threadName);
}
$this->info = $this->threadName . " Result for URL #: " . $this->url_id;
$this->isGarbage = true; // yeah, it s done
}
public function isGarbage(): bool {
return $this->isGarbage;
}
}
Related
I want to run my PHP file without any error but each time I faced some problem. I explained my problem below.
Could any one can give me any suggestion so that I can solve it with a better way!
Thanks all who participate here.
PHP Warning: curl_setopt(): supplied resource is not a valid cURL handle resource in p_detail.php on line 170
PHP Warning: curl_setopt(): supplied resource is not a valid cURL handle resource in p_detail.php on line 171
PHP Warning: curl_setopt(): supplied resource is not a valid cURL handle resource in p_detail.php on line 172
Here is my php file:
$banggoodAPI = new BanggoodAPI();
//Product Detail
$params = [
'product_id'=>1588753,
];
$banggoodAPI->setParams($params);
$result = $banggoodAPI->getProductDetail();
echo '<pre>';
var_dump($result);
class BanggoodAPI {
private $__apiKey = '***';
private $__apiSecret = '**********';
private $__domain = 'https://affapi.banggood.com/';
private $__accessToken = '';
private $__task = '' ;
private $__method = 'GET';
private $__params = array();
private $__lang = 'en-GB';
private $__currency = 'USD';
private $__waitingTaskInfo = array();
private $__ch = null;
private $__curlExpireTime = 10;
/**
* #desc Construct
* #access public
*/
public function __construct(){
$this->__ch = curl_init();
}
/**
* #desc product/detail
* #access public
*/
public function getProductDetail() {
$this->__task = 'product/detail';
$this->__method = 'GET';
$result = $this->__doRequest();
return $result;
}
/**
* #desc set params
* #access public
*/
public function setParams(Array $params) {
if (!empty($params)) {
$this->__params = $params;
}
}
/**
* #desc get access_token
* #access private
*/
private function __getAccessToken($useCache = true) {
//if access_token is empty, send request to get accessToken
if (empty($this->__accessToken)) {
if (!empty($this->__task)) {
$this->__waitingTaskInfo = array(
'task' => $this->__task,
'method' => $this->__method,
'params' => $this->__params,
);
}
$this->__task = 'getAccessToken';
$rand=rand();
$time=time();
$this->__params = [
'api_key' => $this->__apiKey,
'noncestr' => $rand,
'timestamp' => $time,
];
$preArr = array_merge($this->__params, ['api_secret' => $this->__apiSecret]);
ksort($preArr);
$signature=http_build_query($preArr);
$this->__params['signature']=md5($signature);
$this->__method = 'GET';
$result = $this->__doRequest();
if ($result['code'] == 200) {
$this->__accessToken = $result['result']['access_token'];
//resend request
if (!empty($this->__waitingTaskInfo)) {
$this->__task = $this->__waitingTaskInfo['task'];
$this->__params = $this->__waitingTaskInfo['params'];
$this->__method = $this->__waitingTaskInfo['method'];
$this->__waitingTaskInfo = array();
return $this->__doRequest();
}
} else {
$this->__requestError($result);
}
}
}
/**
* #desc handle request error
* #access private
*/
private function __requestError($error) {
var_dump($error);
exit;
}
/**
* #desc send api request
* #access private
*/
private function __doRequest() {
if (empty($this->__params)) {
$this->__requestError(array('params is empty'));
}
if ($this->__task != 'getAccessToken') {
if (empty($this->__accessToken)) {
$this->__getAccessToken();
}
//头部信息
$header = array(
'access-token:'.$this->__accessToken,
);
if (empty($this->__params['lang']))
$this->__params['lang'] = $this->__lang;
if (empty($this->__params['currency']))
$this->__params['currency'] = $this->__currency;
}
$apiUrl = $this->__domain . $this->__task;
if ($this->__method == 'GET') {
$quote = '?';
foreach ($this->__params as $k => $v) {
$apiUrl .= $quote . $k .'='. $v;
$quote = '&';
}
$preStr = http_build_query($this->__params);
}
curl_setopt($this->__ch, CURLOPT_URL, $apiUrl ); //170 line
curl_setopt($this->__ch, CURLOPT_HEADER, 0); //171 line
curl_setopt($this->__ch, CURLOPT_USERAGENT, $_SERVER ['HTTP_USER_AGENT']); //172 line
curl_setopt($this->__ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($this->__ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($this->__ch, CURLOPT_SSL_VERIFYPEER, false);
if ($this->__method == 'POST') {
curl_setopt($this->__ch, CURLOPT_POST, 1 );
curl_setopt($this->__ch, CURLOPT_POSTFIELDS, http_build_query($this->__params));
}
if ($this->__curlExpireTime > 0) {
curl_setopt($this->__ch, CURLOPT_TIMEOUT, $this->__curlExpireTime);
}
if ($header){
curl_setopt($this->__ch, CURLOPT_HTTPHEADER, $header);
}
$result = curl_exec($this->__ch);
if($error=curl_error($this->__ch)){
die($error);
}
curl_close($this->__ch);
$result = json_decode($result, true);
return $result;
}
}
?>
I want to run my PHP file without any error but each time I faced some problem. I explained my problem below.
Could any one can give me any suggestion so that I can solve it with a better way!
Thanks all who participate here.
You are "closing" your curl handle after every request (curl_close($this->__ch);), but only "initializing" it ($this->__ch = curl_init();) once in the constructor.
Once it's been "closed", you can't re-use it for another request. Since you're setting all the options for each request anyway, you probably want to run curl_init() for every request, at the start of the __doRequest method.
I am a beginner in PHP OOP.
I was wondering what is the best practice in PHP OOP and classes. Keep values in a Variables ($var) or keep them in the object ($this->var).
As you can see in below code I set the variables ($this->var) in methods and ,therefore, do not need to return anything from that method.
Is this a correct way of doing it or do I need to return somthing from method? and pass that value to the next method?
Below is a simple curl class for API that extracts a clients email address by using a code.
Pay specific attention to extract_ib_email() Method, no variable is passed to functions (Methods). Is this correct and is there a better way of doing it please?
//Parent Class
class CurlRequest
{
/* get the data from a URL */
protected function curl_get_data() {
$ch = curl_init();
//$timeout = 5;
curl_setopt($ch, CURLOPT_VERBOSE, $this->curloptVERBOSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->curloptSSLVERIFYPEER);
curl_setopt($ch, CURLOPT_URL, $this->curlURL);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, $this->curloptRETURNTRANSFER);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->timeout);
$data = curl_exec($ch);
if ($data === FALSE) {
printf("cUrl error (#%d): %s<br>\n", curl_errno($ch),
htmlspecialchars(curl_error($ch)));
}
//echo "<pre>"; print_r($data); echo "</pre>";
//var_dump($data);
curl_close($ch);
$this->data = $data;
return $data;
}
}
class GetIBEmail extends CurlRequest
{
private $ibRef;
function __construct($ibRef)
{
// Set Variables
$this->baseURL = 'https://www.example.com/api/iel/';
$this->ibRef = $ibRef;
//$this->ibCode = $ibCode;
//set curl options for this request specifically
$this->timeout = $timeout = 5;
$this->curloptVERBOSE = $curlopt_VERBOSE = true;
$this->curloptSSLVERIFYPEER = $curlopt_SSL_VERIFYPEER = false;
$this->curloptRETURNTRANSFER = $curlopt_RETURNTRANSFER = 1;
}
// IB Code form is like ib_xxx need to get the last 3 letters
private function getIBCode(){
$ibRefBits = explode('_', $this->ibRef);
$this->ibCode = $ibRefBits[1];
//return $this->ibCode;
}
//construct the curl url using the IB code
private function construct_ib_curl_url(){
//$url = 'https://www.example.com/api/iel/xxx';
//$url = $this->baseURL . $this->ibCode;
$this->curlURL = $this->baseURL . $this->ibCode;
//return $url;
}
// extract email from returned curl response
public function extract_ib_email() {
$this->getIBCode();
$this->construct_ib_curl_url();
$this->curl_get_data();
$resArr = array();
$resArr = json_decode($this->data);
echo "<pre>"; print_r($resArr); echo "</pre>";
$ib_email = $resArr[0]->email;
$this->ib_email = $ib_email;
return $ib_email;
}
}
//Set ibCode
$ibCode = 'ib_xxx'; //$_SESSION['ib_code'];
$ibEmail = new GetIBEmail($ibCode); //pass value to the construct function
echo '<br>' . $ibEmail->extract_ib_email() . '<br>';
I've been looking around hoping I could find the issue I'm stumbling upon... However I couldn't find it. I've made a class in PHP, within that class there is a function that connects to an API retrieving JSON data (this function has been tested and works like a charm). However now I'm trying to seperate the objects of the JSON data I receive into different functions.
I've only managed to parse the data now through a foreach and echo, but it'd be a hassle to do it like that constantly.
This is what it currently looks like so you get an idea.
EDIT
Issue has been resolved, the public function foo(); Had and $info variable which had a json_decode(); which interupted the next function to split the data.
class parseData{
var $name;
var $domain;
public function foo($name, $domain)
{
$this->name = $name;
$this->domain = $domain;
$ch = curl_init();
$user_agent = 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.43 Safari/537.31';
$request_headers = array();
$request_headers[] = 'User-Agent: ' . $user_agent;
$request_headers[] = 'Accept: text/html,application/xhtml+xml,application/xml,application/json;q=0.9,*/*;q=0.8';
curl_setopt($ch, CURLOPT_URL, "https://www.xxx." . $domain . "/api/public/users?name=" . $name . "");
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_URL, "https://www.xxx." . $domain . "/api/public/users?name=" . $name);
$id = json_decode(curl_exec($ch));
if (isset($id) && $id->profileVisible == 1) {
curl_setopt($ch, CURLOPT_URL, "https://www.xxx." . $domain . "/api/public/users/" . $id->uniqueId . "/profile");
$info = curl_exec($ch);
} else
$info = false;
curl_close($ch);
return $info;
}
public $response = array(), $user = array(), $friends = array(), $groups = array(), $rooms = array(), $badges = array();
public function __construct(){
$this->response = $this->foo("user", "nl");
$this->init();
}
// this function will split data into sub variables
private function init(){
$this->response =json_decode(file_get_contents($this->response), TRUE);
$this->user = $this->response['user'];
$this->friends = $this->response['friends'];
$this->groups = $this->response['groups'];
$this->rooms = $this->response['rooms'];
$this->badges = $this->response['badges'];
// free main response object
unset($this->response);
}
public function uid(){
echo $this->user['uniqueId'];
}
public function name(){
echo $this->user['name'];
}
public function membersince(){
echo $this->user['memberSince'];
}
public function motto(){
echo $this->user['motto'];
}
public function figure(){
echo $this->user['figureString'];
}
//this function will manipulate USER data.
public function user(){
echo "Naam: ".$this->user['name'].'<br/>';
echo "Motto: ".$this->user['motto'].'<br/>';
echo "Lid sinds: ".$this->user['memberSince'].'<br/>';
echo "Unique ID: ".$this->user['uniqueId'].'<br/>';
echo "figureString: ".$this->user['figureString'].'<br/>';
foreach($this->user['selectedBadges'] as $selectedBadge){
echo 'Badge index: '. $selectedBadge['badgeIndex'];
echo 'Badge code: '. $selectedBadge['code'];
echo 'Badge naam: '. $selectedBadge['name'];
echo 'Badge beschrijving: '. $selectedBadge['description'];
}
}
public function friends(){
//do stuff with $this->friends.
}
public function groups(){
//do stuff with $this->groups
}
//and other functions like badges and rooms etc.
}
$parser = new parseData();
$parser->user();
The API sends different objects back and I'd like to seperate them into different functions as "user", "friends", "groups" etc. in order to retrieve all the strings out of those objects. So my question is, is it possible to parse API data through different functions within the same class? If so, how do I do this? And will calling the function be an easier task to do as well or will it just be a hassle like doing the foreach method?
So, I've looked into your json and you can like this
class parseData{
public $response = array(), $user = array(), $friends = array(), $groups = array(), $rooms = array(), $badges = array();
public function __construct(){
$this->response = call_your_function_get_api_response();
$this->init();
}
// this function will split data into sub variables
private function init(){
$this->response = json_decode(file_get_contents('json.json'), TRUE);
//I'm using file get contents here, as I've stored your json into file.
// but you'll have to do like this
//$this->response = json_decode($this->response, TRUE);
$this->user = $this->response['user'];
$this->friends = $this->response['friends'];
$this->groups = $this->response['groups'];
$this->rooms = $this->response['rooms'];
$this->badges = $this->response['badges'];
// free main response object
unset($this->response);
}
/*
* I'm updating here with new function name(), so to print name only do this.
*/
public function name(){
echo "Name: ".$this->user['name'].'<br/>'; //This will print name only.
}
//this function will manipulate USER data.
public function user(){
echo "User's Unique ID: ".$this->user['uniqueId'].'<br/>';
echo "Name: ".$this->user['name'].'<br/>';
echo "figureString: ".$this->user['figureString'].'<br/>';
foreach($this->user['selectedBadges'] as $selectedBadge){
echo 'Badge index: '. $selectedBadge['badgeIndex'];
echo 'Badge code: '. $selectedBadge['code'];
echo 'Badge name: '. $selectedBadge['name'];
echo 'Badge description: '. $selectedBadge['description'];
}
}
public function friends(){
//do stuff with $this->friends.
}
public function groups(){
//do stuff with $this->groups
}
//and other functions like badges and rooms etc.
}
$parser = new parseData();
$parser->user();
/*
* and you can call function like this
* $parser->friends(); will process only friends data from json response
* $parser->groups(); will process only groups data from json response.
* ... rest of functions ...
*/
I've tested this with one iteration of stuff using users() function.
here is output
Edit
I've updated class with a new method name() so now to print only name,
$parser->name();
Another Edit
as per your comment
I'm putting __construct() code here again.
public function __construct(){
$get = new foo();
$this->response = $get->bar("person", "nl");
$this->init();
}
and rest of the things are same.
$parser = new parseData();
$parser->user();
$parser->name(); //will display name only.
putting all together
you need to update two functions
public function __construct(){
$this->response = json_decode($this->foo("user", "nl"), TRUE);
$this->init();
}
and in init() comment out the first line as it's not needed any more.
Hope this will help you.
I am searching products on an e-commerce website using their API. The API returns all the information in JSON format as a response. Each JSON response has 50 items and a URL that has 50 other items and so on. So to match the searched string with product titles, I need to parse all the JSON files sequentially. But if the item to be searched is on the last page, it is taking about 40 minutes to reach there. Can you please help on how I can reduce that time?
My website is currently hosted on localhost(XAMPP).
index.php
$fk = new Flipkart();
$fk->getProductFeedJason($fk->result);
for($i=0; $i<51; $i++)
{
if($fk->flag==1)
break;
$fk->curl($fk->links[$i]);
$fk->getProducts($fk->result, $sstring);
}
flipkart.php
<?php
class Flipkart
{
private $baseUrl = "https://affiliate-api.flipkart.net/affiliate/api/psblesson.json";
private $headers = array(
'Fk-Affiliate-Id: id',
'Fk-Affiliate-Token: token'
);
public $result;
public $links;
public $mainLinksCount=0;
public $pc=0;
public $id;
public $title;
public $image;
public $sellingPrice;
public $maximumRetailPrice;
public $productURL;
public $flag=0;
function __construct()
{
ini_set('max_execution_time', 0);
ini_set('memory_limit', '1024M');
$this->curl($this->baseUrl);
}
public function curl($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $this->headers);
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$this->result = curl_exec($ch);
curl_close($ch);
}
public function getProductFeedJason($res)
{
$i=0;
$productFeeds = json_decode($res, TRUE);
$this->links = $productFeeds['apiGroups']['affiliate']['apiListings'];
foreach($this->links as $keys=>$value)
{
$this->links[$i] = $value['availableVariants']['v0.1.0']['get'];
//echo $this->links[$i];
$i = $i + 1;
}
}
public function getProducts($res, $str)
{
$products = null;
$products = json_decode($res, TRUE);
$outerPart = $products['productInfoList'];
$nextUrl = $products['nextUrl'];
//echo $nextUrl.'<br />';
foreach($outerPart as $data)
{
$t = $data['productBaseInfo']['productAttributes']['title'];
similar_text($str, $t, $percent);
if($percent>70)
{
$this->id[$this->pc] = $data['productBaseInfo']['productIdentifier'] ['productId'];
//echo $this->id[$this->pc].'<br />';
$this->title[$this->pc] = $data['productBaseInfo']['productAttributes']['title'];
//echo $this->title[$this->pc].'<br />';
$this->image[$this->pc] = $data['productBaseInfo']['productAttributes']['imageUrls']['400x400'];
//echo $this->image[$this->pc].'<br />';
$this->sellingPrice[$this->pc] = $data['productBaseInfo']['productAttributes']['sellingPrice']['amount'];
//echo $this->sellingPrice[$this->pc].'<br />';
$this->maximumRetailPrice[$this->pc] = $data['productBaseInfo']['productAttributes']['maximumRetailPrice']['amount'];
//echo $this->maximumRetailPrice[$this->pc].'<br />';
$this->productUrl[$this->pc] = $data['productBaseInfo']['productAttributes']['productUrl'];
//echo $this->productUrl[$this->pc].'<br />';
$this->pc = $this->pc+1;
if($this->pc >=10)
{
$this->flag=1;
break;
}
}
}
if($nextUrl && $this->flag==0)
{
$this->curl($nextUrl);
$this->getProducts($this->result, $str);
}
}
}
?>
So I downloaded a wrapper class from this github link:
https://github.com/ignaciovazquez/Highrise-PHP-Api
and I'm just trying to get any response whatsoever. So far, I can't even authenticate with my credentials so I was wondering if any who has used the API could help me.
I tried running one of the test files on Terminal with no arguments and this is what it told me:
Usage: php users.test.php [account-name] [access-token]
Alright, so then decided to get my credentials. So this is what I understand, and, please, correct if I'm wrong:
the account-name is that part that goes in the url to your highrise account. So if your url is:
https://exampleaccount.highrisehq.com/
then your account name is: "exampleaccount"
and your access token is your authentication token that you can find by going clicking on My info > API token inside your Highrise account.
Is that right?
Well anyways, I enter this info and script terminates with a fatal error and this message:
Fatal error: Uncaught exception 'Exception' with message 'API for User returned Status Code: 0 Expected Code: 200' in /Users/me/Sites/sandbox/PHP/highrise_api_class/lib/HighriseAPI.class.php:137
Stack trace:
#0 /Users/me/Sites/sandbox/PHP/highrise_api_class/lib/HighriseAPI.class.php(166): HighriseAPI->checkForErrors('User')
#1 /Users/me/Sites/sandbox/PHP/highrise_api_class/test/users.test.php(13): HighriseAPI->findMe()
#2 {main}
thrown in /Users/me/Sites/sandbox/PHP/highrise_api_class/lib/HighriseAPI.class.php on line 137
I'm complete n00b and I don't really understand what it's saying so I was wondering if any could help. It would be greatly appreciated.
The source of the test script (users.test.php) is:
<?php
require_once("../lib/HighriseAPI.class.php");
if (count($argv) != 3)
die("Usage: php users.test.php [account-name] [access-token]\n");
$hr = new HighriseAPI();
$hr->debug = false;
$hr->setAccount($argv[1]);
$hr->setToken($argv[2]);
print "Finding my user...\n";
$user = $hr->findMe();
print_r($user);
print "Finding all users...\n";
$users = $hr->findAllUsers();
print_r($users);
?>
and the source to the Highrise API wrapper file (Highrise.API.class) is:
<?php
/*
* http://developer.37signals.com/highrise/people
*
* TODO LIST:
* Add Tasks support
* Get comments for Notes / Emails
* findPeopleByTagName
* Get Company Name, etc proxy
* Convenience methods for saving Notes $person->saveNotes() to check if notes were modified, etc.
* Add Tags to Person
*/
class HighriseAPI
{
public $account;
public $token;
protected $curl;
public $debug;
public function __construct()
{
$this->curl = curl_init();
curl_setopt($this->curl,CURLOPT_RETURNTRANSFER,true);
curl_setopt($this->curl, CURLOPT_HTTPHEADER, array('Accept: application/xml', 'Content-Type: application/xml'));
// curl_setopt($curl,CURLOPT_POST,true);
curl_setopt($this->curl,CURLOPT_SSL_VERIFYPEER,0);
curl_setopt($this->curl,CURLOPT_SSL_VERIFYHOST,0);
}
public function setAccount($account)
{
$this->account = $account;
}
public function setToken($token)
{
$this->token = $token;
curl_setopt($this->curl,CURLOPT_USERPWD,$this->token.':x');
}
protected function postDataWithVerb($path, $request_body, $verb = "POST")
{
$this->curl = curl_init();
$url = "https://" . $this->account . ".highrisehq.com" . $path;
if ($this->debug)
print "postDataWithVerb $verb $url ============================\n";
curl_setopt($this->curl, CURLOPT_URL,$url);
curl_setopt($this->curl, CURLOPT_POSTFIELDS, $request_body);
if ($this->debug == true)
curl_setopt($this->curl, CURLOPT_VERBOSE, true);
curl_setopt($this->curl, CURLOPT_HTTPHEADER, array('Accept: application/xml', 'Content-Type: application/xml'));
curl_setopt($this->curl, CURLOPT_USERPWD,$this->token.':x');
curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER,0);
curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST,0);
curl_setopt($this->curl, CURLOPT_RETURNTRANSFER,true);
if ($verb != "POST")
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $verb);
else
curl_setopt($this->curl, CURLOPT_POST, true);
$ret = curl_exec($this->curl);
if ($this->debug == true)
print "Begin Request Body ============================\n" . $request_body . "End Request Body ==============================\n";
curl_setopt($this->curl,CURLOPT_HTTPGET, true);
return $ret;
}
protected function getURL($path)
{
curl_setopt($this->curl, CURLOPT_HTTPHEADER, array('Accept: application/xml', 'Content-Type: application/xml'));
curl_setopt($this->curl, CURLOPT_USERPWD,$this->token.':x');
curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER,0);
curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST,0);
curl_setopt($this->curl, CURLOPT_RETURNTRANSFER,true);
$url = "https://" . $this->account . ".highrisehq.com" . $path;
if ($this->debug == true)
curl_setopt($this->curl, CURLOPT_VERBOSE, true);
curl_setopt($this->curl,CURLOPT_URL,$url);
$response = curl_exec($this->curl);
if ($this->debug == true)
print "Response: =============\n" . $response . "============\n";
return $response;
}
protected function getLastReturnStatus()
{
return curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
}
protected function getXMLObjectForUrl($url)
{
$xml = $this->getURL($url);
$xml_object = simplexml_load_string($xml);
return $xml_object;
}
protected function checkForErrors($type, $expected_status_codes = 200)
{
if (!is_array($expected_status_codes))
$expected_status_codes = array($expected_status_codes);
if (!in_array($this->getLastReturnStatus(), $expected_status_codes))
{
switch($this->getLastReturnStatus())
{
case 404:
throw new Exception("$type not found");
break;
case 403:
throw new Exception("Access denied to $type resource");
break;
case 507:
throw new Exception("Cannot create $type: Insufficient storage in your Highrise Account");
break;
default:
throw new Exception("API for $type returned Status Code: " . $this->getLastReturnStatus() . " Expected Code: " . implode(",", $expected_status_codes));
break;
}
}
}
/* Users */
public function findAllUsers()
{
$xml = $this->getUrl("/users.xml");
$this->checkForErrors("User");
$xml_object = simplexml_load_string($xml);
$ret = array();
foreach($xml_object->user as $xml_user)
{
$user = new HighriseUser();
$user->loadFromXMLObject($xml_user);
$ret[] = $user;
}
return $ret;
}
public function findMe()
{
$xml = $this->getUrl("/me.xml");
$this->checkForErrors("User");
$xml_obj = simplexml_load_string($xml);
$user = new HighriseUser();
$user->loadFromXMLObject($xml_obj);
return $user;
}
/* Tasks */
public function findCompletedTasks()
{
$xml = $this->getUrl("/tasks/completed.xml");
$this->checkForErrors("Tasks");
return $this->parseTasks($xml);
}
public function findAssignedTasks()
{
$xml = $this->getUrl("/tasks/assigned.xml");
$this->checkForErrors("Tasks");
return $this->parseTasks($xml);
}
public function findUpcomingTasks()
{
$xml = $this->getUrl("/tasks/upcoming.xml");
$this->checkForErrors("Tasks");
return $this->parseTasks($xml);
}
private function parseTasks($xml)
{
$xml_object = simplexml_load_string($xml);
$ret = array();
foreach($xml_object->task as $xml_task)
{
$task = new HighriseTask($this);
$task->loadFromXMLObject($xml_task);
$ret[] = $task;
}
return $ret;
}
public function findTaskById($id)
{
$xml = $this->getURL("/tasks/$id.xml");
$this->checkForErrors("Task");
$task_xml = simplexml_load_string($xml);
$task = new HighriseTask($this);
$task->loadFromXMLObject($task_xml);
return $task;
}
/* Notes & Emails */
public function findEmailById($id)
{
$xml = $this->getURL("/emails/$id.xml");
$this->checkForErrors("Email");
$email_xml = simplexml_load_string($xml);
$email = new HighriseEmail($this);
$email->loadFromXMLObject($email_xml);
return $email;
}
public function findNoteById($id)
{
$xml = $this->getURL("/notes/$id.xml");
$this->checkForErrors("Note");
$note_xml = simplexml_load_string($xml);
$note = new HighriseNote($this);
$note->loadFromXMLObject($note_xml);
return $note;
}
public function findPersonById($id)
{
$xml = $this->getURL("/people/$id.xml");
$this->checkForErrors("Person");
$xml_object = simplexml_load_string($xml);
$person = new HighrisePerson($this);
$person->loadFromXMLObject($xml_object);
return $person;
}
public function findAllTags()
{
$xml = $this->getUrl("/tags.xml");
$this->checkForErrors("Tags");
$xml_object = simplexml_load_string($xml);
$ret = array();
foreach($xml_object->tag as $tag)
{
$ret[(string)$tag->name] = new HighriseTag((string)$tag->id, (string)$tag->name);
}
return $ret;
}
public function findAllPeople()
{
return $this->parsePeopleListing("/people.xml");
}
public function findPeopleByTagName($tag_name)
{
$tags = $this->findAllTags();
foreach($tags as $tag)
{
if ($tag->name == $tag_name)
$tag_id = $tag->id;
}
if (!isset($tag_id))
throw new Excepcion("Tag $tag_name not found");
return $this->findPeopleByTagId($tag_id);
}
public function findPeopleByTagId($tag_id)
{
$url = "/people.xml?tag_id=" . $tag_id;
$people = $this->parsePeopleListing($url);
return $people;
}
public function findPeopleByEmail($email)
{
return $this->findPeopleBySearchCriteria(array("email"=>$email));
}
public function findPeopleByTitle($title)
{
$url = "/people.xml?title=" . urlencode($title);
$people = $this->parsePeopleListing($url);
return $people;
}
public function findPeopleByCompanyId($company_id)
{
$url = "/companies/" . urlencode($company_id) . "/people.xml";
$people = $this->parsePeopleListing($url);
return $people;
}
public function findPeopleBySearchTerm($search_term)
{
$url = "/people/search.xml?term=" . urlencode($search_term);
$people = $this->parsePeopleListing($url, 25);
return $people;
}
public function findPeopleBySearchCriteria($search_criteria)
{
$url = "/people/search.xml";
$sep = "?";
foreach($search_criteria as $criteria=>$value)
{
$url .= $sep . "criteria[" . urlencode($criteria) . "]=" . urlencode($value);
$sep = "&";
}
$people = $this->parsePeopleListing($url, 25);
return $people;
}
public function findPeopleSinceTime($time)
{
$url = "/people/search.xml?since=" . urlencode($time);
$people = $this->parsePeopleListing($url);
return $people;
}
public function parsePeopleListing($url, $paging_results = 500)
{
if (strstr($url, "?"))
$sep = "&";
else
$sep = "?";
$offset = 0;
$return = array();
while(true) // pagination
{
$xml_url = $url . $sep . "n=$offset";
// print $xml_url;
$xml = $this->getUrl($xml_url);
$this->checkForErrors("People");
$xml_object = simplexml_load_string($xml);
foreach($xml_object->person as $xml_person)
{
// print_r($xml_person);
$person = new HighrisePerson($this);
$person->loadFromXMLObject($xml_person);
$return[] = $person;
}
if (count($xml_object) != $paging_results)
break;
$offset += $paging_results;
}
return $return;
}
}
Sorry it's such a long file but if it helps, then so be it.
EDIT: So I guess I got it to work. I should've said that I was trying to test this library out on my local server and for some reason it would keep failing but when I moved the script to my development server on Rackspace cloud then it would work. This just puzzles me. Both servers have support for PHP curl so I can't really understand where the problem is.
EDIT: I'm not sure what the difference between the two server configurations could be but anyways here's a couple of screenshots from my phpinfo function output from both servers of my curl configuration:
Localhost server:
and the rackspace cloud server:
The fork of the API at...
https://github.com/AppSaloon/Highrise-PHP-Api
...seems more developed and better maintained.
Not so much as to provide an answer, but more a better starting point.
Ah, since there is really no HTTP error code 0 I expect that your request isn't being made to Highrise's website, or you are not correctly passing in the account name and token to the class. Can you include the source of your users.test.php class?
EDIT: tested the class and your code, and it works for me. You probably either copied the library file wrong or have your token copied wrong.
I had the same issue. I definitely had the wrong account. I had https://foo.highrisehq.com instead of just foo.