1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Embedly; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* |
7
|
|
|
* @author Embed.ly, Inc. |
8
|
|
|
* @author Sven Eisenschmidt <[email protected]> |
9
|
|
|
*/ |
10
|
|
|
class Embedly { |
11
|
|
|
|
12
|
|
|
/** |
13
|
|
|
* |
14
|
|
|
* @const |
15
|
|
|
*/ |
16
|
|
|
const VERSION = '0.1.0'; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* |
20
|
|
|
* @var string |
21
|
|
|
*/ |
22
|
|
|
protected $hostname = 'api.embed.ly'; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* |
26
|
|
|
* @var string |
27
|
|
|
*/ |
28
|
|
|
protected $key = null; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* |
32
|
|
|
* @var array |
33
|
|
|
*/ |
34
|
|
|
protected $api_version = array( |
35
|
|
|
'oembed' => 1, |
36
|
|
|
'objectify' => 2, |
37
|
|
|
'preview' => 1 |
38
|
|
|
); |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* |
42
|
|
|
* @var string |
43
|
|
|
*/ |
44
|
|
|
protected $user_agent = ""; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* |
48
|
|
|
* @var array|object |
49
|
|
|
*/ |
50
|
|
|
protected $services = null; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* |
54
|
|
|
* @param array $args |
55
|
|
|
*/ |
56
|
|
|
public function __construct(array $args = array()) |
57
|
|
|
{ |
58
|
|
|
$args = array_merge(array( |
59
|
|
|
'user_agent' => sprintf("Mozilla/5.0 (compatible; embedly-php/%s)", self::VERSION), |
60
|
|
|
'key' => null, |
61
|
|
|
'hostname' => null, |
62
|
|
|
'api_version' => null |
63
|
|
|
), $args); |
64
|
|
|
|
65
|
|
|
if ($args['user_agent']) { |
66
|
|
|
$this->user_agent = $args['user_agent']; |
67
|
|
|
} |
68
|
|
|
if ($args['key']) { |
69
|
|
|
$this->key = $args['key']; |
70
|
|
|
} |
71
|
|
|
if ($args['hostname']) { |
72
|
|
|
$this->hostname = $args['hostname']; |
73
|
|
|
} |
74
|
|
|
if ($args['api_version']) { |
75
|
|
|
$this->api_version = array_merge($this->api_version, $args['api_version']); |
76
|
|
|
} |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* |
81
|
|
|
* Flexibly parse host strings. |
82
|
|
|
* |
83
|
|
|
* Returns an array of |
84
|
|
|
* { protocol: |
85
|
|
|
* , host: |
86
|
|
|
* , port: |
87
|
|
|
* , url: |
88
|
|
|
* } |
89
|
|
|
* |
90
|
|
|
* @param string $host |
91
|
|
|
* @return array |
92
|
|
|
*/ |
93
|
|
|
protected function parse_host($host) |
94
|
|
|
{ |
95
|
|
|
$port = 80; |
96
|
|
|
$protocol = 'http'; |
97
|
|
|
|
98
|
|
|
preg_match('/^(https?:\/\/)?([^\/]+)(:\d+)?\/?$/', $host, $matches); |
99
|
|
|
|
100
|
|
|
if (!$matches) { |
|
|
|
|
101
|
|
|
throw new \Exception(sprintf('invalid host %s', host)); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
$hostname = $matches[2]; |
105
|
|
|
|
106
|
|
|
if ($matches[1] == 'https://') { |
107
|
|
|
$protocol = 'https'; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
if (array_key_exists(3, $matches) && $matches[3]) { |
111
|
|
|
$port = intval($matches[3]); |
112
|
|
|
} else if ($matches[1] == 'https://') { |
113
|
|
|
$port = 443; |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
$portpart = ""; |
117
|
|
|
if (array_key_exists(3, $matches) && $matches[3]) { |
118
|
|
|
$portpart = sprintf(":%s", $matches[3]); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
$url = sprintf("%s://%s%s/", $protocol, $hostname, $portpart); |
122
|
|
|
|
123
|
|
|
return array( |
124
|
|
|
'url' => $url, |
125
|
|
|
'scheme' => $protocol, |
126
|
|
|
'hostname' => $hostname, |
127
|
|
|
'port' => $port |
128
|
|
|
); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* |
133
|
|
|
* @return string|array |
134
|
|
|
*/ |
135
|
|
|
public function oembed($params) |
136
|
|
|
{ |
137
|
|
|
return $this->apicall($this->api_version['oembed'], 'oembed', $params); |
|
|
|
|
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* |
142
|
|
|
* @param string|array $params |
143
|
|
|
* @return object |
144
|
|
|
*/ |
145
|
|
|
public function preview($params) |
146
|
|
|
{ |
147
|
|
|
return $this->apicall($this->api_version['preview'], 'preview', $params); |
|
|
|
|
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* |
152
|
|
|
* @param array $params |
153
|
|
|
* @return object |
154
|
|
|
*/ |
155
|
|
|
public function objectify($params) |
156
|
|
|
{ |
157
|
|
|
return $this->apicall($this->api_version['objectify'], 'objectify', $params); |
|
|
|
|
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* |
162
|
|
|
* @return string |
163
|
|
|
*/ |
164
|
|
|
public function api_version() |
165
|
|
|
{ |
166
|
|
|
return $this->api_version; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* |
171
|
|
|
* @param string $version |
172
|
|
|
* @param array $action |
173
|
|
|
* @param array $params |
174
|
|
|
* @return object |
175
|
|
|
*/ |
176
|
|
|
public function apicall($version, $action, $params) |
177
|
|
|
{ |
178
|
|
|
$justone = is_string($params); |
179
|
|
|
$params = self::paramify($params); |
|
|
|
|
180
|
|
|
|
181
|
|
|
if (!array_key_exists('urls', $params)) { |
182
|
|
|
$params['urls'] = array(); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
if (!is_array($params['urls'])) { |
186
|
|
|
$urls = array($params['urls']); |
187
|
|
|
$params['urls'] = $urls; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
if (array_key_exists('url', $params) && $params['url']) { |
191
|
|
|
array_push($params['urls'], $params['url']); |
192
|
|
|
unset($params['url']); |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
$rejects = array(); |
196
|
|
|
if ($this->key) { |
197
|
|
|
$params['key'] = $this->key; |
198
|
|
|
} else { |
199
|
|
|
$regex = $this->services_regex(); |
200
|
|
|
foreach ($params['urls'] as $i => $url) { |
201
|
|
|
$match = preg_match($regex, $url); |
202
|
|
|
if (!$match) { |
203
|
|
|
//print("rejecting $url"); |
204
|
|
|
unset($params['urls'][$i]); |
205
|
|
|
$rejects[$i] = (object)array( |
206
|
|
|
'error_code' => '401', |
207
|
|
|
'error_message' => 'This service requires an Embedly key', |
208
|
|
|
'type' => 'error' |
209
|
|
|
); |
210
|
|
|
} |
211
|
|
|
}; |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
$result = array(); |
215
|
|
|
|
216
|
|
|
if (sizeof($rejects) < sizeof($params['urls'])) { |
217
|
|
|
if (count($params['urls']) > 20) { |
218
|
|
|
throw new \Exception( |
219
|
|
|
sprintf("Max of 20 urls can be queried at once, %s passed", |
220
|
|
|
count($params['urls']))); |
221
|
|
|
} |
222
|
|
|
$path = sprintf("%s/%s", $version, $action); |
223
|
|
|
$url_parts = $this->parse_host($this->hostname); |
224
|
|
|
$apiUrl = sprintf("%s%s?%s", $url_parts['url'], $path, $this->q($params)); |
225
|
|
|
|
226
|
|
|
$ch = curl_init($apiUrl); |
227
|
|
|
$this->setCurlOptions($ch, array( |
228
|
|
|
sprintf('Host: %s', $url_parts['hostname']), |
229
|
|
|
sprintf('User-Agent: %s', $this->user_agent) |
230
|
|
|
)); |
231
|
|
|
$res = $this->curlExec($ch); |
232
|
|
|
$result = json_decode($res) ?: array(); |
233
|
|
|
} |
234
|
|
|
$merged_result = array(); |
235
|
|
|
foreach ($result as $i => $v) { |
236
|
|
|
if (array_key_exists($i, $rejects)) { |
237
|
|
|
array_push($merged_result, array_shift($rejects)); |
238
|
|
|
} |
239
|
|
|
array_push($merged_result, $v); |
240
|
|
|
}; |
241
|
|
|
// grab any leftovers |
242
|
|
|
foreach ($rejects as $obj) { |
243
|
|
|
array_push($merged_result, $obj); |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
if($justone) { |
247
|
|
|
return array_shift($merged_result); |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
return $merged_result; |
|
|
|
|
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* |
255
|
|
|
* @return array |
256
|
|
|
*/ |
257
|
|
|
public function services() { |
258
|
|
|
if (!$this->services) { |
259
|
|
|
$url = $this->parse_host($this->hostname); |
260
|
|
|
$apiUrl = sprintf("%s1/services/php", $url['url']); |
261
|
|
|
$ch = curl_init($apiUrl); |
262
|
|
|
$this->setCurlOptions($ch, array( |
263
|
|
|
sprintf('Host: %s', $url['hostname']), |
264
|
|
|
sprintf('User-Agent: %s', $this->user_agent) |
265
|
|
|
)); |
266
|
|
|
$res = $this->curlExec($ch); |
267
|
|
|
$this->services = json_decode($res); |
268
|
|
|
} |
269
|
|
|
return $this->services; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* |
274
|
|
|
* @return string |
275
|
|
|
*/ |
276
|
|
|
public function services_regex() { |
277
|
|
|
$services = $this->services(); |
278
|
|
|
$regexes = array_map(array(__CLASS__, 'reg_imploder'), $services); |
279
|
|
|
return '#'.implode('|', $regexes).'#i'; |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* |
284
|
|
|
* @return string |
285
|
|
|
*/ |
286
|
|
|
protected function q($params) { |
287
|
|
|
$pairs = array_map(array(__CLASS__, 'url_encode'), array_keys($params), array_values($params)); |
288
|
|
|
return implode('&', $pairs); |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* |
293
|
|
|
* @param resource $ch |
294
|
|
|
* @param array $headers |
295
|
|
|
* @return void |
296
|
|
|
*/ |
297
|
|
|
protected function setCurlOptions(&$ch, $headers = array()) |
298
|
|
|
{ |
299
|
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); |
300
|
|
|
curl_setopt($ch, CURLOPT_HEADER, false); |
301
|
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); |
302
|
|
|
curl_setopt($ch, CURLOPT_BUFFERSIZE, 4096); |
303
|
|
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 25); |
304
|
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30); |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
/** |
308
|
|
|
* |
309
|
|
|
* @param resource $ch |
310
|
|
|
* @return string |
311
|
|
|
*/ |
312
|
|
|
protected function curlExec(&$ch) |
313
|
|
|
{ |
314
|
|
|
$res = curl_exec($ch); |
315
|
|
|
if (false === $res) { |
316
|
|
|
throw new \Exception(curl_error($ch), curl_errno($ch)); |
317
|
|
|
} |
318
|
|
|
return $res; |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
|
322
|
|
|
/** |
323
|
|
|
* |
324
|
|
|
* @param string $r |
325
|
|
|
* @return string |
326
|
|
|
*/ |
327
|
|
|
public static function reg_delim_stripper($r) |
328
|
|
|
{ |
329
|
|
|
# we need to strip off regex delimeters and options to make |
330
|
|
|
# one giant regex |
331
|
|
|
return substr($r, 1, -2); |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
/** |
335
|
|
|
* |
336
|
|
|
* @param stdClass $o |
337
|
|
|
* @return string |
338
|
|
|
*/ |
339
|
|
|
public static function reg_imploder(\stdClass $o) |
340
|
|
|
{ |
341
|
|
|
return implode('|', array_map(array(__CLASS__, 'reg_delim_stripper'), $o->regex)); |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* |
346
|
|
|
* @param string $key |
347
|
|
|
* @param string|array $value |
348
|
|
|
* @return string |
349
|
|
|
*/ |
350
|
|
|
public static function url_encode($key, $value) |
351
|
|
|
{ |
352
|
|
|
$key = urlencode($key); |
353
|
|
|
if (is_array($value)) { |
354
|
|
|
$value = implode(',', array_map('urlencode', $value)); |
355
|
|
|
} else { |
356
|
|
|
$value = urlencode($value); |
357
|
|
|
} |
358
|
|
|
return sprintf("%s=%s", $key, $value); |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
/** |
362
|
|
|
* |
363
|
|
|
* @param string $input |
364
|
|
|
* @return array |
365
|
|
|
*/ |
366
|
|
|
public static function paramify($input) |
367
|
|
|
{ |
368
|
|
|
if(is_string($input)) { |
369
|
|
|
return array('urls' => $input); |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
return $input; |
373
|
|
|
} |
374
|
|
|
} |
375
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.