Background Information
I want to download photos of a specific property using PHPRETS.
My PHPRETS configuration works successfully for downloading property data. The problem is only with downloading media files.
I am able to download photos of the property using RETS Connector, the Windows application, meaning the images exist.
The value that I pass as the object key (N3273704) is the value that I've got from the KeyField (ml_num)
KeyField
ml_num
The code I run
$photos = $rets->GetObject("Property", "Photo", "N3273704", "*", 0);
var_dump($photos);
foreach ($photos as $photo) {
$listing = $photo['Content-ID'];
$number = $photo['Object-ID'];
if ($photo['Success'] == true) {
file_put_contents("image-{$listing}-{$number}.jpg", $photo['Data']);
} else {
echo "({$listing}-{$number}): {$photo['ReplyCode']} = {$photo['ReplyText']}\n";
}
}
Raw header and response
[2015-07-29 13:16:09] PHRETS.DEBUG: Sending HTTP Request for http://rets.torontomls.net:6103/rets-treb3pv/server/getobject (GetObject) {"query":{"Resource":"Property","Type":"Photo","ID":"N3273704:*","Location":0},"headers":{"User-Agent":"PHRETS/2.0","RETS-Version":"RETS/1.5","Accept-Encoding":"gzip"},"cookies": ..."]} []
[2015-07-29 13:16:09] PHRETS.DEBUG: Response: HTTP 200 [] []
Output on screen (result of var_dump)
array(20) {
[0]=>
object(PHRETS\Models\Object)#32 (10) {
["content_type":protected]=>
string(8) "text/xml"
["content_id":protected]=>
string(4) "null"
["object_id":protected]=>
string(4) "null"
["mime_version":protected]=>
NULL
["location":protected]=>
NULL
["content_description":protected]=>
NULL
["content_sub_description":protected]=>
NULL
["content":protected]=>
string(192) "<?xml version="1.0" standalone="no"?>
<!DOCTYPE RETS SYSTEM "RETS-20041001.dtd">
<RETS ReplyCode="20403" ReplyText="No Object Found: No matching object was found to satisfy the request."/>"
["preferred":protected]=>
NULL
["error":protected]=>
object(PHRETS\Models\RETSError)#45 (2) {
["code":protected]=>
int(20403)
["message":protected]=>
string(69) "No Object Found: No matching object was found to satisfy the request."
}
}
[1]=> ...
I was able to figure out the issue and I'm writing this in the hope it saves somebody's time.
After monitoring HTTP traffic and comparing raw headers sent from PHRETS and RETS connector, the Windows application, it turned out that some RETS servers (in this case, Toronto Real Estate Board) require the "Accept" field to be present in the header. So, the problem is solved by adding the following line to the constructor of Session class:
'Accept' => '*/*',
I wanted to contribute one note... TREB has the KeyField misconfigured. They are using the DBName version of the Property's identifying field (ml_num), whereas the KeyField is supposed to be the SystemName version of that field (Ml_num).... just a matter of difference in case, but enough to be a breaking change for RETS clients that would derive the KeyField automatically from the RETS metadata.
Related
I've been using the Salesforce SOAP API for a number of years, but the original PHP client that Salesforce provided doesn't work with PHP8.1. I've attempted to incorporate the necessary elements of the client into my code since I'm only performing one simple action (creating a QuoteLineItem). I've been able to get the client to properly connect and login, but my create command doesn't get any response (not even a NULL value).
I've compared everything being sent from the PHP7.4 solution and my 8.1 implementation, and they appear almost identical.
Create statement:
$result = $this->sforcePartnerClient->create($arg);
$arg value:
object(stdClass)#1331 (2) { ["__CLASS__"]=> string(8) "stdClass" ["sObjects"]=> array(1) { [0]=> string(26) "\Module\Controller\SObject" } }
Why am I not getting even an error response?
I have a CRM set to crm.example.com And a active store on example.com (on the same server)
Both running with NGINX.
Im trying to request the whole categories list (43 categories) but i'm getting only 10. (The default value set by WooCommerce).
I tried using the per_page parameter but it seems like the server is ignore the GET.
I checked the request sent from crm.example.com and got that:
object(Automattic\WooCommerce\HttpClient\Request)#85 (5) {
["url":"Automattic\WooCommerce\HttpClient\Request":private]=>
string(67) "https://example.com/wp-json/wc/v2/products/categories/?per_page=100"
["method":"Automattic\WooCommerce\HttpClient\Request":private]=>
string(3) "GET"
["parameters":"Automattic\WooCommerce\HttpClient\Request":private]=>
array(1) {
["per_page"]=>
int(100)
}
["headers":"Automattic\WooCommerce\HttpClient\Request":private]=>
array(2) {
["Accept"]=>
string(16) "application/json"
["User-Agent"]=>
string(32) "WooCommerce API Client-PHP/1.3.0"
}
["body":"Automattic\WooCommerce\HttpClient\Request":private]=>
string(0) ""
}
But yet there is only 10 results.
i'm thinking maybe the NGINX server is ignoring the GET parameters as a security policy?
I am using file_get_contents() to perform a POST to an API service. This is working for most of the queries. However, recently I had a failure in which file_get_contents() returned HTTP headers in the content!
For the code:
$resp = file_get_contents("http://10.72.18.21:8000",false, $context);
var_dump($resp);
var_dump($http_response_header);
I get the following content and headers every time:
string(114328) "Server: Spark Proxy Server
Content-Length: 114272
{"records":....}
"
array(1) {
[0]=>
string(31) "HTTP/1.0 200 Spark Proxy Server"
}
Notice that inside the $resp you can see headers (the first two lines) which should normally be parsed in $http_response_header.
I have also tried two different approaches to do the POST: (a) fopen + while loop and (b) fopen + stream_get_contents. In all three cases the results are the same. The common thing between all three is the stream context which I create using:
$opts = array('http' =>
array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded;charset=utf-8',
'content' => http_build_query($params)
)
);
$context = stream_context_create($opts);
Now, querying the same API with the same code but with a single parameter modified, everything works as expected and the complete headers are:
array(3) {
[0]=>
string(31) "HTTP/1.0 200 Spark Proxy Server"
[1]=>
string(26) "Server: Spark Proxy Server"
[2]=>
string(19) "Content-Length: 288"
}
Finally, I have tried the same call that fails with python and cUrl and in both cases the results are correct, so I am pretty sure it is php related issue.
Questions:
Has anyone seen this behavior before?
Is there a way to perform the same POST without using stream_context_create?
Can it be time related? The example that fails takes up to 1.4 minutes to complete
I have tried this again today... and instead of headers mixed in the content, I got empty content and partial headers. This error though is a lot easier to google and lead me to this SO answer talking about a similar problem (weird file_get_contents behavior).
As the first comment hinted, changing the default socket timeout solves my problem (it was set to 60):
ini_set("default_socket_timeout", 600);
I am not quite sure how the socket can timeout since the data and the headers received seemed complete...
I'm using google app engine locally and the request returns what it should.
Then I try on google app engine (the appspot service myapp.appspot.com) and the exact same call simply returns false without any errors.
The php documentation says I should get an error or a warning but I really don't see anything.
http://www.php.net/manual/en/function.file-get-contents.php
I found this gae php documentation below but I really can't figure out what could be wrong.
https://developers.google.com/appengine/docs/php/urlfetch/
I'm trying to fetch an url of another app from the appspot and the gae doc says:
Making requests to another App Engine app
If you are making requests to another App Engine app, you should
consider telling the URL Fetch service to not follow redirects when
invoking it.
Does anyone know how do I do that, or maybe what could even be the real problem ?
Here is my snippet:
$content = file_get_contents($url, 0, $context);
if (false === $content) {
$error = error_get_last();
throw new ClientException($error['message']);
}
UPDATE:
Here is my php.ini:
google_app_engine.enable_functions = "php_sapi_name, php_uname, getmypid"
google_app_engine.allow_include_gs_buckets = "storage_bucket_name"
allow_url_include=1
allow_url_fopen=1
I tried with the following code:
error_reporting(-1);
ini_set('display_errors', 1);
$content = file_get_contents($url, 0, $context);
if (false === $content) {
$error = error_get_last();
throw new ClientException($error['message']);
}
Now I get this error: "failed to open stream: Invalid headers. Must be a string."
This is my $context variable which is passed through stream_context_create():
http://www.php.net/manual/fr/function.stream-context-create.php
array(2) {
["http"]=>
array(7) {
["method"]=>
string(3) "GET"
["header"]=>
string(0) ""
["content"]=>
NULL
["protocol_version"]=>
float(1)
["ignore_errors"]=>
bool(true)
["max_redirects"]=>
int(5)
["timeout"]=>
int(5)
}
["ssl"]=>
array(1) {
["verify_peer"]=>
bool(true)
}
}
This is leading me to that old thread:
Using file_get_contents invalid headers issue on GAE
Since I had the error "failed to open stream: Invalid headers. Must be a string." I focused on the header part of the context.
The $context['http']['headers'] was obviously a string(0) ""
I tried to unset the $context['http']['headers'] if it was empty and the problem is solved.
Note: this happens only in Google App Engine (GAE)
I'm using Deezer APIs
$xml = simplexml_load_file('http://api.deezer.com/2.0//search/artist/?q=eminem&index=0&nb_items=1&output=xml');
var_dump($xml);
$xml = simplexml_load_file('http://api.deezer.com/2.0/artist/393/top/&output=xml');
var_dump($xml);
The first call works in the same way both on my local machine (Mac PHP 5.3.15) and online on a dream-host server (PHP 5.3.13), the second call works on my local machine, reporting all 5 track objects, but not on-line, where I just get
object(SimpleXMLElement)#4 (2) {
["data"]=> object(SimpleXMLElement)#1 (0) { }
["total"]=> object(SimpleXMLElement)#3 (0) { }
}
It seems to me very weird, do you have any clue?
Thanks
Deezer use geolocation for Artist Request.
So, if your server is geolocated in US for example, the first request have the same result between local and online but de second one (http://api.deezer.com/2.0/artist/393/top/&output=xml) don't have the same result local/online.
Request with FR Ip :
<?xml version="1.0" encoding="utf-8"?><root><data><track><id><![CDATA[2114267]]></id><readable><![CDATA[1]]></readable><title><![CDATA[My Life]]></title><link><![CDATA[http://www.deezer.com/track/2114267]]></link><duration><![CDATA[321]]></duration><rank><![CDATA[674751]]></rank><preview><![CDATA[http://cdn-preview-2.deezer.com/stream/2ecb4b24f51cdbfdaea89630f1978529-0.mp3]]></preview><artist><id><![CDATA[393]]></id><name><![CDATA[The Game]]></name></artist><type><![CDATA[track]]></type></track><track><id><![CDATA[2307182]]></id><readable><![CDATA[1]]></readable><title><![CDATA[Hate It Or Love It]]></title><link><![CDATA[http://www.deezer.com/track/2307182]]></link><duration><![CDATA[207]]></duration><rank><![CDATA[654207]]></rank><preview><![CDATA[http://cdn-preview-3.deezer.com/stream/393350005d03712abc9adfbe2bcfe2d3-0.mp3]]></preview><artist><id><![CDATA[393]]></id><name><![CDATA[The Game]]></name></artist><type><![CDATA[track]]></type></track><track><id><![CDATA[2294433]]></id><readable><![CDATA[1]]></readable><title><![CDATA[How We Do]]></title><link><![CDATA[http://www.deezer.com/track/2294433]]></link><duration><![CDATA[235]]></duration><rank><![CDATA[610398]]></rank><preview><![CDATA[http://cdn-preview-c.deezer.com/stream/c3683a1fc1899c9d1b128b222d0e080f-1.mp3]]></preview><artist><id><![CDATA[393]]></id><name><![CDATA[The Game]]></name></artist><type><![CDATA[track]]></type></track><track><id><![CDATA[62751648]]></id><readable><![CDATA[1]]></readable><title><![CDATA[Ali Bomaye]]></title><link><![CDATA[http://www.deezer.com/track/62751648]]></link><duration><![CDATA[373]]></duration><rank><![CDATA[571988]]></rank><preview><![CDATA[http://cdn-preview-1.deezer.com/stream/1bb2606c722235eeecb2b1caa039f5c1-0.mp3]]></preview><artist><id><![CDATA[393]]></id><name><![CDATA[The Game]]></name></artist><type><![CDATA[track]]></type></track><track><id><![CDATA[61571949]]></id><readable><![CDATA[1]]></readable><title><![CDATA[Celebration]]></title><link><![CDATA[http://www.deezer.com/track/61571949]]></link><duration><![CDATA[290]]></duration><rank><![CDATA[559639]]></rank><preview><![CDATA[http://cdn-preview-f.deezer.com/stream/f1f535359bd60ce3ec77d59fcfda4ebd-1.mp3]]></preview><artist><id><![CDATA[393]]></id><name><![CDATA[The Game]]></name></artist><type><![CDATA[track]]></type></track></data><total><![CDATA[5]]></total></root>
The same request with US location :
<?xml version="1.0" encoding="utf-8"?><root><data></data><total><![CDATA[0]]></total></root>
To complete the answer. You can change Geolocation of your request (and have result from US Server) with access_token of user located in non-restricted country because when you request the deezer api with access_token, the api will use the user country and not the server country.
To get an access token from user :
http://developers.deezer.com/api/oauth
Don't forget on each request when you want to use the User geolocation to add this :
?access_token=XXXXX
EDIT :
I am part of the deezer team.
if you want to access xml by http protocol you will need to set allow_url_fopen ON in php.ini or
ini_set('allow_url_fopen ','ON');
in your code. or you can also do this if you are using php version <5
$temp = file_get_contents($url);
$XmlObj = simplexml_load_string($temp);
try:
$xml = simplexml_load_file('http://api.deezer.com/2.0/artist/393/top/?output=xml');
(i.e. change the '&' to '?')
This may or may not be the problem.