b443325d by fehrlich

init

1 parent c5f88e3b
1 vendor
...\ No newline at end of file ...\ No newline at end of file
1 # immoscout24-api-php 1 # immoscout24-api-php
2 2
3 An example implementation for the immoscout24 rest api in php.
...\ No newline at end of file ...\ No newline at end of file
3 An example implementation for the immoscout24 rest api in php. This Client is not complete and only implements some methods for the export/import api, but it should be quite easy to extend.
4
5 ## install
6
7 `composer install fehrlich/immoscout24-api-php`
8
9
10 ## usage
11
12 The libary contains an abstract class that needs to be extended by an user defined class and implement 4 methods, that are needed to restore and save tokens.
13
14 ### example implementation
15
16 ```
17 class MyAPI extends \fehrlich\ScoutAPI\ImmoScoutAPI
18 {
19 /**
20 * should save the request token with tokenname + secret, the token can be temporarily saved within a session
21 * this token is only needed during the "request an access token" phase.
22 *
23 * @param string $token
24 * @param string $secret
25 *
26 * @return void
27 */
28 public function saveRequestToken($token, $secret){
29 $_SESSION['is24reqtoken'] = $token;
30 $_SESSION['is24reqsecret'] = $secret;
31 }
32
33 /**
34 * restores the temporarily saved request token.
35 *
36 * @return array with 2 elements: first element is the token name, second element is the secret
37 */
38 public function restoreRequestToken(){
39 if(isset($_SESSION['is24reqtoken']) && isset($_SESSION['is24reqsecret'])){
40 return [
41 $_SESSION['is24reqtoken'],
42 $_SESSION['is24reqsecret']
43 ];
44 }
45 return null;
46 }
47
48 /**
49 * saves the access token, the information should be stored persistently, for example in a database or file.
50 * the progress of getting an access token only needs to be done once per user.
51 *
52 * @param string $token
53 * @param string $secret
54 *
55 * @return void
56 */
57 public function saveAccessToken($token, $secret){
58 file_put_contents('accessToken', serialize([$token, $secret]));
59 }
60
61 /**
62 * restores the saved access token.
63 *
64 * @return array with 2 elements: first element is the token name, second element is the secret
65 */
66 public static function restoreAccessToken(){
67 if(file_exists('accessToken')) {
68 return unserialize(file_get_contents('accessToken'));
69 }
70 return null;
71 }
72 }
73 ```
74
75 If you want to support multiple user, you can use `$this->getUser()` within the class.
76
77 ### example usage
78
79
80 ```
81 $key = 'my consumer key';
82 $secret = 'my consumer secret';
83
84 // $api->dontUseSandbox();
85
86 $api = MyAPI::createClient($key, $secret);
87 if ($api->isVerified()) {
88 // get a list of possible contacts used in a real estate
89 $contactArray = $api->getContacts();
90 var_dump($contactArray);
91 } else {
92 // this will trigger the verifiaction with immoscout24, and will try to reconnect
93 // to the current url else you can also use a paramater to specify the callback url
94 $api->verifyApplication();
95 }
96 ```
...\ No newline at end of file ...\ No newline at end of file
......
1 {
2 "name": "fehrlich/immoscout24-api-php",
3 "description": "PHP Client for interacting with the Immoscout24 REST API",
4 "type": "library",
5 "authors": [
6 {
7 "name": "fehrlich",
8 "email": "franz.ehrlich@googlemail.com"
9 }
10 ],
11 "require": {
12 "guzzlehttp/guzzle-services": "^1.1",
13 "guzzlehttp/oauth-subscriber": "^0.3.0",
14 "guzzlehttp/guzzle": "^6.3"
15 }
16 }
1 {
2 "_readme": [
3 "This file locks the dependencies of your project to a known state",
4 "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 "This file is @generated automatically"
6 ],
7 "content-hash": "18a9f9bc0a2116a603ecb4b314774471",
8 "packages": [
9 {
10 "name": "guzzlehttp/command",
11 "version": "1.0.0",
12 "source": {
13 "type": "git",
14 "url": "https://github.com/guzzle/command.git",
15 "reference": "2aaa2521a8f8269d6f5dfc13fe2af12c76921034"
16 },
17 "dist": {
18 "type": "zip",
19 "url": "https://api.github.com/repos/guzzle/command/zipball/2aaa2521a8f8269d6f5dfc13fe2af12c76921034",
20 "reference": "2aaa2521a8f8269d6f5dfc13fe2af12c76921034",
21 "shasum": ""
22 },
23 "require": {
24 "guzzlehttp/guzzle": "^6.2",
25 "guzzlehttp/promises": "~1.3",
26 "guzzlehttp/psr7": "~1.0",
27 "php": ">=5.5.0"
28 },
29 "require-dev": {
30 "phpunit/phpunit": "~4.0|~5.0"
31 },
32 "type": "library",
33 "extra": {
34 "branch-alias": {
35 "dev-master": "0.9-dev"
36 }
37 },
38 "autoload": {
39 "psr-4": {
40 "GuzzleHttp\\Command\\": "src/"
41 }
42 },
43 "notification-url": "https://packagist.org/downloads/",
44 "license": [
45 "MIT"
46 ],
47 "authors": [
48 {
49 "name": "Michael Dowling",
50 "email": "mtdowling@gmail.com",
51 "homepage": "https://github.com/mtdowling"
52 },
53 {
54 "name": "Jeremy Lindblom",
55 "email": "jeremeamia@gmail.com",
56 "homepage": "https://github.com/jeremeamia"
57 }
58 ],
59 "description": "Provides the foundation for building command-based web service clients",
60 "time": "2016-11-24T13:34:15+00:00"
61 },
62 {
63 "name": "guzzlehttp/guzzle",
64 "version": "6.4.1",
65 "source": {
66 "type": "git",
67 "url": "https://github.com/guzzle/guzzle.git",
68 "reference": "0895c932405407fd3a7368b6910c09a24d26db11"
69 },
70 "dist": {
71 "type": "zip",
72 "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0895c932405407fd3a7368b6910c09a24d26db11",
73 "reference": "0895c932405407fd3a7368b6910c09a24d26db11",
74 "shasum": ""
75 },
76 "require": {
77 "ext-json": "*",
78 "guzzlehttp/promises": "^1.0",
79 "guzzlehttp/psr7": "^1.6.1",
80 "php": ">=5.5"
81 },
82 "require-dev": {
83 "ext-curl": "*",
84 "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
85 "psr/log": "^1.1"
86 },
87 "suggest": {
88 "psr/log": "Required for using the Log middleware"
89 },
90 "type": "library",
91 "extra": {
92 "branch-alias": {
93 "dev-master": "6.3-dev"
94 }
95 },
96 "autoload": {
97 "psr-4": {
98 "GuzzleHttp\\": "src/"
99 },
100 "files": [
101 "src/functions_include.php"
102 ]
103 },
104 "notification-url": "https://packagist.org/downloads/",
105 "license": [
106 "MIT"
107 ],
108 "authors": [
109 {
110 "name": "Michael Dowling",
111 "email": "mtdowling@gmail.com",
112 "homepage": "https://github.com/mtdowling"
113 }
114 ],
115 "description": "Guzzle is a PHP HTTP client library",
116 "homepage": "http://guzzlephp.org/",
117 "keywords": [
118 "client",
119 "curl",
120 "framework",
121 "http",
122 "http client",
123 "rest",
124 "web service"
125 ],
126 "time": "2019-10-23T15:58:00+00:00"
127 },
128 {
129 "name": "guzzlehttp/guzzle-services",
130 "version": "1.1.3",
131 "source": {
132 "type": "git",
133 "url": "https://github.com/guzzle/guzzle-services.git",
134 "reference": "9e3abf20161cbf662d616cbb995f2811771759f7"
135 },
136 "dist": {
137 "type": "zip",
138 "url": "https://api.github.com/repos/guzzle/guzzle-services/zipball/9e3abf20161cbf662d616cbb995f2811771759f7",
139 "reference": "9e3abf20161cbf662d616cbb995f2811771759f7",
140 "shasum": ""
141 },
142 "require": {
143 "guzzlehttp/command": "~1.0",
144 "guzzlehttp/guzzle": "^6.2",
145 "php": ">=5.5"
146 },
147 "require-dev": {
148 "phpunit/phpunit": "~4.0"
149 },
150 "suggest": {
151 "gimler/guzzle-description-loader": "^0.0.4"
152 },
153 "type": "library",
154 "extra": {
155 "branch-alias": {
156 "dev-master": "1.0.x-dev"
157 }
158 },
159 "autoload": {
160 "psr-4": {
161 "GuzzleHttp\\Command\\Guzzle\\": "src/"
162 }
163 },
164 "notification-url": "https://packagist.org/downloads/",
165 "license": [
166 "MIT"
167 ],
168 "authors": [
169 {
170 "name": "Michael Dowling",
171 "email": "mtdowling@gmail.com",
172 "homepage": "https://github.com/mtdowling"
173 },
174 {
175 "name": "Jeremy Lindblom",
176 "email": "jeremeamia@gmail.com",
177 "homepage": "https://github.com/jeremeamia"
178 },
179 {
180 "name": "Stefano Kowalke",
181 "email": "blueduck@mail.org",
182 "homepage": "https://github.com/konafets"
183 }
184 ],
185 "description": "Provides an implementation of the Guzzle Command library that uses Guzzle service descriptions to describe web services, serialize requests, and parse responses into easy to use model structures.",
186 "time": "2017-10-06T14:32:02+00:00"
187 },
188 {
189 "name": "guzzlehttp/oauth-subscriber",
190 "version": "0.3.0",
191 "source": {
192 "type": "git",
193 "url": "https://github.com/guzzle/oauth-subscriber.git",
194 "reference": "04960cdef3cd80ea401d6b0ca8b3e110e9bf12cf"
195 },
196 "dist": {
197 "type": "zip",
198 "url": "https://api.github.com/repos/guzzle/oauth-subscriber/zipball/04960cdef3cd80ea401d6b0ca8b3e110e9bf12cf",
199 "reference": "04960cdef3cd80ea401d6b0ca8b3e110e9bf12cf",
200 "shasum": ""
201 },
202 "require": {
203 "guzzlehttp/guzzle": "~6.0",
204 "php": ">=5.5.0"
205 },
206 "require-dev": {
207 "phpunit/phpunit": "~4.0"
208 },
209 "type": "library",
210 "extra": {
211 "branch-alias": {
212 "dev-master": "0.3-dev"
213 }
214 },
215 "autoload": {
216 "psr-4": {
217 "GuzzleHttp\\Subscriber\\Oauth\\": "src"
218 }
219 },
220 "notification-url": "https://packagist.org/downloads/",
221 "license": [
222 "MIT"
223 ],
224 "authors": [
225 {
226 "name": "Michael Dowling",
227 "email": "mtdowling@gmail.com",
228 "homepage": "https://github.com/mtdowling"
229 }
230 ],
231 "description": "Guzzle OAuth 1.0 subscriber",
232 "homepage": "http://guzzlephp.org/",
233 "keywords": [
234 "Guzzle",
235 "oauth"
236 ],
237 "time": "2015-08-15T19:44:28+00:00"
238 },
239 {
240 "name": "guzzlehttp/promises",
241 "version": "v1.3.1",
242 "source": {
243 "type": "git",
244 "url": "https://github.com/guzzle/promises.git",
245 "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
246 },
247 "dist": {
248 "type": "zip",
249 "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
250 "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
251 "shasum": ""
252 },
253 "require": {
254 "php": ">=5.5.0"
255 },
256 "require-dev": {
257 "phpunit/phpunit": "^4.0"
258 },
259 "type": "library",
260 "extra": {
261 "branch-alias": {
262 "dev-master": "1.4-dev"
263 }
264 },
265 "autoload": {
266 "psr-4": {
267 "GuzzleHttp\\Promise\\": "src/"
268 },
269 "files": [
270 "src/functions_include.php"
271 ]
272 },
273 "notification-url": "https://packagist.org/downloads/",
274 "license": [
275 "MIT"
276 ],
277 "authors": [
278 {
279 "name": "Michael Dowling",
280 "email": "mtdowling@gmail.com",
281 "homepage": "https://github.com/mtdowling"
282 }
283 ],
284 "description": "Guzzle promises library",
285 "keywords": [
286 "promise"
287 ],
288 "time": "2016-12-20T10:07:11+00:00"
289 },
290 {
291 "name": "guzzlehttp/psr7",
292 "version": "1.6.1",
293 "source": {
294 "type": "git",
295 "url": "https://github.com/guzzle/psr7.git",
296 "reference": "239400de7a173fe9901b9ac7c06497751f00727a"
297 },
298 "dist": {
299 "type": "zip",
300 "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a",
301 "reference": "239400de7a173fe9901b9ac7c06497751f00727a",
302 "shasum": ""
303 },
304 "require": {
305 "php": ">=5.4.0",
306 "psr/http-message": "~1.0",
307 "ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
308 },
309 "provide": {
310 "psr/http-message-implementation": "1.0"
311 },
312 "require-dev": {
313 "ext-zlib": "*",
314 "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
315 },
316 "suggest": {
317 "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses"
318 },
319 "type": "library",
320 "extra": {
321 "branch-alias": {
322 "dev-master": "1.6-dev"
323 }
324 },
325 "autoload": {
326 "psr-4": {
327 "GuzzleHttp\\Psr7\\": "src/"
328 },
329 "files": [
330 "src/functions_include.php"
331 ]
332 },
333 "notification-url": "https://packagist.org/downloads/",
334 "license": [
335 "MIT"
336 ],
337 "authors": [
338 {
339 "name": "Michael Dowling",
340 "email": "mtdowling@gmail.com",
341 "homepage": "https://github.com/mtdowling"
342 },
343 {
344 "name": "Tobias Schultze",
345 "homepage": "https://github.com/Tobion"
346 }
347 ],
348 "description": "PSR-7 message implementation that also provides common utility methods",
349 "keywords": [
350 "http",
351 "message",
352 "psr-7",
353 "request",
354 "response",
355 "stream",
356 "uri",
357 "url"
358 ],
359 "time": "2019-07-01T23:21:34+00:00"
360 },
361 {
362 "name": "psr/http-message",
363 "version": "1.0.1",
364 "source": {
365 "type": "git",
366 "url": "https://github.com/php-fig/http-message.git",
367 "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
368 },
369 "dist": {
370 "type": "zip",
371 "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
372 "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
373 "shasum": ""
374 },
375 "require": {
376 "php": ">=5.3.0"
377 },
378 "type": "library",
379 "extra": {
380 "branch-alias": {
381 "dev-master": "1.0.x-dev"
382 }
383 },
384 "autoload": {
385 "psr-4": {
386 "Psr\\Http\\Message\\": "src/"
387 }
388 },
389 "notification-url": "https://packagist.org/downloads/",
390 "license": [
391 "MIT"
392 ],
393 "authors": [
394 {
395 "name": "PHP-FIG",
396 "homepage": "http://www.php-fig.org/"
397 }
398 ],
399 "description": "Common interface for HTTP messages",
400 "homepage": "https://github.com/php-fig/http-message",
401 "keywords": [
402 "http",
403 "http-message",
404 "psr",
405 "psr-7",
406 "request",
407 "response"
408 ],
409 "time": "2016-08-06T14:39:51+00:00"
410 },
411 {
412 "name": "ralouphie/getallheaders",
413 "version": "3.0.3",
414 "source": {
415 "type": "git",
416 "url": "https://github.com/ralouphie/getallheaders.git",
417 "reference": "120b605dfeb996808c31b6477290a714d356e822"
418 },
419 "dist": {
420 "type": "zip",
421 "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
422 "reference": "120b605dfeb996808c31b6477290a714d356e822",
423 "shasum": ""
424 },
425 "require": {
426 "php": ">=5.6"
427 },
428 "require-dev": {
429 "php-coveralls/php-coveralls": "^2.1",
430 "phpunit/phpunit": "^5 || ^6.5"
431 },
432 "type": "library",
433 "autoload": {
434 "files": [
435 "src/getallheaders.php"
436 ]
437 },
438 "notification-url": "https://packagist.org/downloads/",
439 "license": [
440 "MIT"
441 ],
442 "authors": [
443 {
444 "name": "Ralph Khattar",
445 "email": "ralph.khattar@gmail.com"
446 }
447 ],
448 "description": "A polyfill for getallheaders.",
449 "time": "2019-03-08T08:55:37+00:00"
450 }
451 ],
452 "packages-dev": [],
453 "aliases": [],
454 "minimum-stability": "stable",
455 "stability-flags": [],
456 "prefer-stable": false,
457 "prefer-lowest": false,
458 "platform": [],
459 "platform-dev": []
460 }
1 <?php
2
3 namespace fehrlich\ScoutAPI;
4
5 use function GuzzleHttp\json_decode;
6 use function GuzzleHttp\json_encode;
7 use fehrlich\ScoutAPI\exceptions\AuthException;
8 use fehrlich\ScoutAPI\exceptions\InvalidResponse;
9 use fehrlich\ScoutAPI\exceptions\InvalidTokenException;
10 use GuzzleHttp\Client;
11 use GuzzleHttp\Command\Guzzle\Description;
12 use GuzzleHttp\Command\Guzzle\GuzzleClient;
13 use GuzzleHttp\Exception\ClientException;
14 use GuzzleHttp\HandlerStack;
15 use GuzzleHttp\Subscriber\Oauth\Oauth1;
16 use InvalidArgumentException;
17 use Psr\Http\Message\ResponseInterface;
18
19 /**
20 * An example implementation for the immoscout24 rest api in php. This Client is not complete and only
21 * implements some methods for the export/import api, but it should be quite easy to extend. Just look
22 * at the later methods that show the interaction after the auth is done.
23 */
24 abstract class ImmoScoutAPI extends GuzzleClient
25 {
26 private $consumerKey;
27 private $consumerKeySecret;
28 private $client = null;
29 public $history = null;
30 private $user = 'me';
31 private $isSandbox = true;
32
33 /**
34 * should save the request token with tokenname + secret, the token can be temporarily saved within a session
35 * this token is only needed during the "request an access token" phase.
36 *
37 * @param string $token
38 * @param string $secret
39 *
40 * @return void
41 */
42 abstract public function saveRequestToken($token, $secret);
43
44 /**
45 * restores the temporarily saved request token.
46 *
47 * @return array with 2 elements: first element is the token name, second element is the secret
48 */
49 abstract public function restoreRequestToken();
50
51 /**
52 * saves the access token, the information should be stored persistently, for example in a database or file.
53 * the progress of getting an access token only needs to be done once per user.
54 *
55 * @param string $token
56 * @param string $secret
57 *
58 * @return void
59 */
60 abstract public function saveAccessToken($token, $secret);
61
62 /**
63 * restores the saved access token.
64 *
65 * @return array with 2 elements: first element is the token name, second element is the secret
66 */
67 abstract public static function restoreAccessToken();
68
69 /**
70 * checks for the right format of a token.
71 *
72 * @param array a token array [name, secret]
73 *
74 * @return void
75 */
76 private function validateToken($tokenArray)
77 {
78 if (!is_array($tokenArray)) {
79 throw new InvalidTokenException('restored Token is not an Array need to be of the form [token, token_secret]');
80 }
81 if (!isset($tokenArray[1])) {
82 throw new InvalidTokenException('restored Token array does not have a second element needs to be of the form [token, token_secret]');
83 }
84 }
85
86 private function getValidatedRequestToken()
87 {
88 $token = $this->restoreRequestToken();
89 $this->validateToken($token);
90
91 return $token;
92 }
93
94 private function getValidatedAccessToken()
95 {
96 $token = static::restoreAccessToken();
97 $this->validateToken($token);
98
99 return $token;
100 }
101
102 /**
103 * use the sandbox api.
104 *
105 * @return boolean
106 */
107 public function useSandbox()
108 {
109 $this->isSandbox = true;
110 }
111
112 /**
113 * use the production api.
114 *
115 * @return boolean
116 */
117 public function dontUseSandbox()
118 {
119 $this->isSandbox = false;
120 }
121
122 /**
123 * creates a new client.
124 *
125 * @param string $key consumer key
126 * @param string $secret consumer secret
127 * @param bool $authorized this should only be false for non 3-legged-authented requests
128 *
129 * @return static
130 */
131 public static function createClient($key, $secret, $authorized = true)
132 {
133 $token = '';
134 $token_secret = '';
135 if ($authorized) {
136 $tokenArray = static::restoreAccessToken();
137 $token = $tokenArray[0];
138 $token_secret = $tokenArray[1];
139 }
140
141 $stack = HandlerStack::create();
142 $oAuth = new Oauth1([
143 'consumer_key' => $key,
144 'consumer_secret' => $secret,
145 'token' => $token,
146 'token_secret' => $token_secret,
147 ]);
148 $stack->push($oAuth);
149 $client = new Client([
150 'headers' => [
151 'Content-Type' => 'application/json',
152 'Accept' => 'application/json',
153 ],
154 'handler' => $stack,
155 'auth' => 'oauth',
156 ]);
157
158 $newGuzzleClient = new static($client, new Description([]));
159 $newGuzzleClient->client = $client;
160 $newGuzzleClient->oAuth = $oAuth;
161 $newGuzzleClient->consumerKey = $key;
162 $newGuzzleClient->consumerKeySecret = $secret;
163
164 return $newGuzzleClient;
165 }
166
167 /**
168 * get base api url depending on the sandbox.
169 *
170 * @return string base url
171 */
172 private function getBaseUrl()
173 {
174 return $this->isSandbox ? 'https://rest.sandbox-immobilienscout24.de/restapi/' : 'https://rest.immobilienscout24.de/restapi/';
175 }
176
177 /**
178 * get the current uri, this can be used to return to the current site after authentification.
179 *
180 * @return string current url
181 */
182 private static function getSelfURI()
183 {
184 if (!isset($_SERVER) || !isset($_SERVER['HTTP_HOST']) || !isset($_SERVER['REQUEST_URI'])) {
185 throw new \Exception("coudn't detect request uri for callback (please specify one)");
186 }
187 $url = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http';
188 $url .= '://';
189 $url .= $_SERVER['HTTP_HOST'];
190 $url .= $_SERVER['REQUEST_URI'];
191
192 return $url;
193 }
194
195 /**
196 * sets the user for the api, this is only needed if you want to have multiple users,
197 * default authenticated user is "me".
198 *
199 * @param string $user
200 *
201 * @return void
202 */
203 public function setUser($user)
204 {
205 $this->user = $user;
206 }
207
208 /**
209 * this creates a default oAuth with a token and secret, this method is used for calls
210 * before the app is authenticated, so for requesting a request token and requesting an access token.
211 *
212 * @param string $uri
213 * @param array $oAuthData
214 *
215 * @return ResponseInterface
216 */
217 private function callOAuth($uri, $oAuthData)
218 {
219 $oAuthData['consumer_key'] = $this->consumerKey;
220 $oAuthData['consumer_secret'] = $this->consumerKeySecret;
221 $oAuthData['token'] = isset($oAuthData['token']) ? $oAuthData['token'] : '';
222 $oAuthData['token_secret'] = isset($oAuthData['token_secret']) ? $oAuthData['token_secret'] : '';
223
224 $stack = HandlerStack::create();
225 $oAuth = new Oauth1($oAuthData);
226 $stack->push($oAuth);
227
228 $client = new Client([
229 'base_uri' => 'https://rest.sandbox-immobilienscout24.de/restapi/',
230 'handler' => $stack,
231 'auth' => 'oauth',
232 ]);
233
234 return $client->get($uri);
235 }
236
237 /**
238 * get a request token.
239 *
240 * @param string $callback the callack url, where you get redirected after you authenticated the application on is24
241 *
242 * @return void
243 */
244 private function requestRequestToken($callback)
245 {
246 try {
247 $res = static::callOAuth('/restapi/security/oauth/request_token', [
248 'callback' => $callback,
249 ]);
250 } catch (ClientException $ex) {
251 throw new AuthException('Failed to get the request token');
252 }
253 parse_str((string) $res->getBody(), $body);
254 if (!isset($body['oauth_token']) || !isset($body['oauth_token_secret'])) {
255 throw new InvalidResponse('Could not parse the requested request token');
256 }
257 $this->saveRequestToken($body['oauth_token'], $body['oauth_token_secret']);
258 header('Location: '.$this->getBaseUrl().'security/oauth/confirm_access?oauth_token='.$body['oauth_token']);
259 exit();
260 }
261
262 /**
263 * request the access token.
264 *
265 * @param string $verifier
266 *
267 * @return void
268 */
269 private function requestAccessToken($verifier)
270 {
271 $requestToken = $this->getValidatedRequestToken();
272 try {
273 $res = $this->callOAuth('security/oauth/access_token', [
274 'token' => $requestToken[0],
275 'token_secret' => $requestToken[1],
276 'verifier' => $verifier,
277 ]);
278 } catch (ClientException $ex) {
279 throw new AuthException('Failed to get the access token');
280 }
281 parse_str((string) $res->getBody(), $body);
282 if (!isset($body['oauth_token']) || !isset($body['oauth_token_secret'])) {
283 throw InvalidResponse('Could not parse the requested access token');
284 }
285
286 $this->saveAccessToken($body['oauth_token'], $body['oauth_token_secret']);
287 }
288
289 /**
290 * this method will verify the application, this is needed if the application doesn't have an
291 * access token yet, this step only needs to be performed once, general the process looks like this:
292 * - getRequest token -> save Request token
293 * -> verify with is24 (receive a verifier) (external site that will redirect to the specified callBackUrl)
294 * -> get access token (with verifier)
295 * -> save access token.
296 *
297 * @param bool $callBackUrl the url where you want to be redirected after your verification on is24, this
298 * url needs to recall this method, to consume the oauth_verifier GET variable
299 *
300 * @return bool if auth is complete it returns true, on the verifiaction step returns false
301 */
302 public function verifyApplication($callBackUrl = false)
303 {
304 $key = $this->consumerKey;
305 $secret = $this->consumerKeySecret;
306 $callBackUrl = $callBackUrl ? $callBackUrl : static::getSelfURI();
307
308 $api = self::createClient($key, $secret, false);
309
310 if (isset($_GET['oauth_verifier']) && isset($_GET['oauth_token'])) {
311 $api->requestAccessToken($_GET['oauth_verifier']);
312
313 return true;
314 } else {
315 $api->requestRequestToken($callBackUrl);
316
317 return false;
318 }
319 }
320
321 /**
322 * returns if the app is verified. this is the case if it can restore the access token.
323 *
324 * @return boolean
325 */
326 public function isVerified()
327 {
328 $validToken = true;
329 try {
330 $this->getValidatedAccessToken();
331 } catch (InvalidTokenException $ex) {
332 $validToken = false;
333 }
334
335 return $validToken;
336 }
337
338 /**
339 * get the deep base url, that includes the parsed user.
340 *
341 * @param string $methodUri
342 *
343 * @return string url
344 */
345 private function getUrl($methodUri)
346 {
347 $url = $this->getBaseUrl();
348 $path = '/api/offer/v1.0/user/{user}/';
349 if (substr($methodUri, 0, 1) !== '/') {
350 $path .= $methodUri;
351 } else {
352 $path = $methodUri;
353 }
354 $path = str_replace('{user}', $this->user, $path);
355
356 return $url.$path;
357 }
358
359 /**
360 * parses the default message body of an response (includes errors and success messages).
361 *
362 * @param ResponseInterface $res
363 *
364 * @return array message array
365 */
366 private function parseMessages($res)
367 {
368 $return = [
369 'parsed' => [],
370 ];
371 if (!empty($res->getBody())) {
372 try {
373 $json = json_decode($res->getBody(), true);
374 } catch (InvalidArgumentException $ex) {
375 $json = [];
376 }
377
378 if (isset($json['common.messages']) && is_array($json['common.messages'])) {
379 foreach ($json['common.messages'] as $message) {
380 if (isset($message['message']) && isset($message['message']['messageCode'])) {
381 if (!isset($return[$message['message']['messageCode']])) {
382 $return[$message['message']['messageCode']] = [];
383 }
384 $return[$message['message']['messageCode']][] = $message['message']['message'];
385 }
386 }
387 }
388
389 $parsedErrors = [];
390 foreach ($return as $errorCode => $msgArr) {
391 foreach ($msgArr as $msg) {
392 $add = null;
393 switch ($errorCode) {
394 case 'ERROR_RESOURCE_VALIDATION':
395 preg_match('/MESSAGE: (.*?) :/', $msg, $matches);
396
397 if (isset($matches[1])) {
398 $add = $matches[1];
399 }
400 break;
401 case 'MESSAGE_RESOURCE_CREATED':
402 preg_match('/with id \[(.*?)\] /', $msg, $matches);
403 if (isset($matches[1])) {
404 $add = $matches[1];
405 }
406 break;
407 case 'MESSAGE_RESOURCE_UPDATED':
408 $add = '1';
409 break;
410 case 'MESSAGE_RESOURCE_DELETED':
411 $add = '1';
412 break;
413 case 'ERROR_RESOURCE_NOT_FOUND':
414 $add = '404';
415 break;
416 }
417 if ($add) {
418 if (!isset($parsedErrors[$errorCode])) {
419 $parsedErrors[$errorCode] = [];
420 }
421 $parsedErrors[$errorCode][] = $add;
422 }
423 }
424 }
425 $return['parsed'] = $parsedErrors;
426 }
427
428 return $return;
429 }
430
431 /**
432 * make an internal call.
433 *
434 * @param string $method like GET, POST, PUT, DELETE
435 * @param string $methodUri
436 * @param array $data
437 *
438 * @return void
439 */
440 private function call($method, $methodUri, $data = [])
441 {
442 $methodUri = $this->getUrl($methodUri);
443 try {
444 return $this->client->request($method, $methodUri, $data);
445 } catch (ClientException $ex) {
446 $res = $ex->getResponse();
447 $messages = $this->parseMessages($res);
448
449 return $res;
450 }
451 }
452
453 private function callMethod($methodUri, $method = 'GET', $jsonData = null, $useJson = true)
454 {
455 $data = [];
456 if ($jsonData && $useJson) {
457 $data = [
458 'json' => $jsonData,
459 ];
460 } elseif ($jsonData) {
461 $data = $jsonData;
462 }
463
464 return $this->call($method, $methodUri, $data);
465 }
466
467 /**
468 * check for a specific response, if it isn't present a InvalidResponse is thrown.
469 *
470 * @param ResponseInterface $res
471 * @param string $expectedResponse the expected response
472 *
473 * @return bool
474 */
475 private function checkForResponse($res, $expectedResponse = 'MESSAGE_RESOURCE_CREATED')
476 {
477 if (is_null($res)) {
478 throw new InvalidResponse('Got empty response ');
479 }
480 $msgs = $this->parseMessages($res);
481 if (!isset($msgs['parsed'][$expectedResponse])) {
482 $code = isset($msgs['parsed']['ERROR_RESOURCE_NOT_FOUND']) ? 404 : null;
483 throw new InvalidResponse('Did not get expected response: '.$res->getBody(), $code);
484 }
485 if ($expectedResponse == 'MESSAGE_RESOURCE_CREATED') {
486 return $msgs['parsed'][$expectedResponse][0];
487 }
488
489 return true;
490 }
491
492 private function checkForResponseCreated($res)
493 {
494 return $this->checkForResponse($res, 'MESSAGE_RESOURCE_CREATED');
495 }
496
497 private function checkForResponseUpdated($res)
498 {
499 return $this->checkForResponse($res, 'MESSAGE_RESOURCE_UPDATED');
500 }
501
502 private function checkForResponseDeleted($res)
503 {
504 return $this->checkForResponse($res, 'MESSAGE_RESOURCE_DELETED');
505 }
506
507 private function parseGetResponse($res)
508 {
509 return json_decode((string) $res->getBody(), true);
510 }
511
512 /**
513 * get a list of possible channels, to publish to.
514 *
515 * @return ResponseInterface
516 */
517 public function getPublishChannels()
518 {
519 return $this->callMethod('publishchannel');
520 }
521
522 /**
523 * create a realestate object.
524 *
525 * @param array $obj realestate object compatible with: https://api.immobilienscout24.de/our-apis/import-export/ftp-vs-api.html
526 *
527 * @return string id used by immoscout24, this should be saved in order to update the realestate later
528 */
529 public function createRealEstate($obj)
530 {
531 $res = $this->callMethod('realestate/?usenewenergysourceenev2014values=true', 'POST', $obj);
532
533 return $this->checkForResponseCreated($res);
534 }
535
536 /**
537 * update a realestate.
538 *
539 * @param string $id id used by immoscout
540 * @param array $obj realestate object compatible with: https://api.immobilienscout24.de/our-apis/import-export/ftp-vs-api.html
541 *
542 * @return bool
543 */
544 public function updateRealEstate($id, $obj)
545 {
546 $res = $this->callMethod('realestate/'.$id.'?usenewenergysourceenev2014values=true', 'PUT', $obj);
547
548 return $this->checkForResponseUpdated($res);
549 }
550
551 /**
552 * delete an attachment.
553 *
554 * @param string $reId realestate id used by is24
555 * @param string $attachmentId attachment id used by is24
556 *
557 * @return void
558 */
559 public function deleteAttachment($reId, $attachmentId)
560 {
561 $res = $this->callMethod('realestate/'.$reId.'/attachment/'.$attachmentId, 'DELETE');
562
563 return $this->checkForResponseDeleted($res);
564 }
565
566 /**
567 * create an attachment for an realestate.
568 *
569 *
570 * @return void
571 */
572
573 /**
574 * create an attachment for an realestate.
575 *
576 * @param string $objId realestate id used by is24
577 * @param array $attachmentData compatible with https://api.immobilienscout24.de/our-apis/import-export/attachments/post.html
578 * @param string $content file content
579 * @param string $mimeType file mime type
580 * @param string $fileName filename (the right extension is important)
581 *
582 * @return string returns the id that is used by is24, this should be saved
583 */
584 public function createAttachment($objId, $attachmentData, $content, $mimeType = 'image/jpeg', $fileName = 'image.jpg')
585 {
586 $res = $this->callMethod('realestate/'.$objId.'/attachment/', 'POST', [
587 'multipart' => [
588 [
589 'Content-type' => 'application/json',
590 'name' => 'metadata',
591 'filename' => 'metadata.json',
592 'contents' => json_encode($attachmentData),
593 ], [
594 'Content-type' => $mimeType,
595 'name' => 'attachment',
596 'filename' => $fileName,
597 'contents' => $content,
598 ],
599 ],
600 ], false);
601
602 return $this->checkForResponseCreated($res);
603 }
604
605 /**
606 * update realestate attachment.
607 *
608 * @param string $objId realestate id used by is24
609 * @param string $id id of the attachment used by is24
610 * @param array $attachmentData compatible with https://api.immobilienscout24.de/our-apis/import-export/attachments/post.html
611 *
612 * @return void
613 */
614 public function updateAttachment($objId, $id, $attachmentData)
615 {
616 $res = $this->callMethod('realestate/'.$objId.'/attachment/'.$id, 'PUT', $attachmentData);
617
618 return $this->checkForResponseUpdated($res);
619 }
620
621 /**
622 * Undocumented function.
623 *
624 * @param string $objId realestate id used by is24
625 * @param array $orderArray ordered array of the ids used by is24
626 *
627 * @return void
628 */
629 public function updateAttachmentOrder($objId, $orderArray)
630 {
631 $res = $this->callMethod('realestate/'.$objId.'/attachment/attachmentsorder', 'PUT', [
632 'attachmentsorder.attachmentsorder' => [
633 '@xmlns' => [
634 'attachmentsorder' => 'http://rest.immobilienscout24.de/schema/attachmentsorder/1.0',
635 ],
636 'attachmentId' => $orderArray,
637 ],
638 ]);
639
640 return $this->checkForResponseUpdated($res);
641 }
642
643 /**
644 * Get an array of all contacts that can be specified for a real estate.
645 *
646 * @return array array in the form of https://api.immobilienscout24.de/our-apis/import-export/contact/get-by-id.html
647 */
648 public function getContacts()
649 {
650 $res = $this->callMethod('contact');
651 $arr = $this->parseGetResponse($res);
652
653 if (!isset($arr['common.realtorContactDetailsList']) || !isset($arr['common.realtorContactDetailsList']['realtorContactDetails'])) {
654 throw new InvalidResponse('Response doesnt have the expected format');
655 }
656
657 return $arr['common.realtorContactDetailsList']['realtorContactDetails'];
658 }
659 }
1 <?php
2
3 namespace fehrlich\ScoutAPI\exceptions;
4
5 class AuthException extends Exception
6 {
7 }
1 <?php
2
3 namespace fehrlich\ScoutAPI\exceptions;
4
5 class InvalidResponse extends Exception
6 {
7 }
1 <?php
2
3 namespace fehrlich\ScoutAPI\exceptions;
4
5 class InvalidTokenException extends Exception
6 {
7 }
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!