<?php
# The MIT License
#
# Copyright (c) 2011 Nicolas Martin
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# http://joliclic.free.fr/php/oembed-proxy/en/
class OEmbedProxy {
private $_url = '';
private $_format = 'json';
private $_maxwidth = null;
private $_maxheight = null;
//TODO add wmode ? (Flash transparency)
private $_jsCallback = null;
private $_provider = null;
public function __construct
($aUrl = '', $aFormat = 'json', $aMaxWidth = null, $aMaxheight = null,
$aJSCallback = null)
{
$this->setUrl($aUrl);
$this->setFormat($aFormat);
if (!is_null($aMaxWidth))
$this->setMaxWidth($aMaxWidth);
if (!is_null($aMaxheight))
$this->setMaxHeight($aMaxheight);
if (!is_null($aJSCallback))
$this->setJSCallback($aJSCallback);
}
public function forward() {
if (is_null($this->_provider))
throw new Exception('Error: invalid url');
if (isset($this->_provider['endpoint-'.$this->_format]))
$url = $this->_provider['endpoint-'.$this->_format];
else
$url = $this->_provider['endpoint'];
$url .= '?url='.rawurlencode($this->_url).'&format='.$this->_format;
if (!is_null($this->_maxwidth))
$url .= '&maxwidth='.$this->_maxwidth;
if (!is_null($this->_maxheight))
$url .= '&maxheight='.$this->_maxheight;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
// don't display the http headers
curl_setopt($ch, CURLOPT_HEADER, false);
// don't display the result directly to the browser
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// 5 seconds timeout
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
// follow redirections
// doesn't work on free.fr :( (safe_mode)
//curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
if (isset($_SERVER['HTTP_USER_AGENT']))
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
$rv = curl_exec($ch);
if (!$rv) {
self::setHttpStatus(503);
exit;
}
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($http_code != 200) {
self::setHttpStatus($http_code);
exit;
}
header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
header('Pragma: no-cache');
if (!is_null($this->_jsCallback)) {
header('Content-type: text/javascript');
header('Content-disposition: filename="oembed-'.time().'.js"; ');
echo $this->_jsCallback.'('.$rv.')';
} else {
switch ($this->_format) {
case 'xml':
header('Content-type: text/xml');
header('Content-disposition: filename="oembed-'.time().'.xml"; ');
break;
case 'json':
default:
header('Content-type: application/json');
header('Content-disposition: filename="oembed-'.time().'.json"; ');
}
echo $rv;
}
exit;
}
public function setUrl($aStr) {
if (!$aStr) {
$this->_provider = null;
$this->_url = '';
return false;
}
foreach (self::$providers as $provider) {
if (preg_match('/' . $provider['url_regexp'] . '/', $aStr)) {
$this->_provider = $provider;
$this->_url = $aStr;
return true;
break;
}
}
$this->_provider = null;
$this->_url = '';
return false;
}
public function setFormat($aStr) {
switch ($aStr) {
case 'json':
$this->_format = 'json';
break;
case 'xml':
$this->_format = 'xml';
break;
default:
throw new Exception('Error: only json and xml formats are supported');
}
return $this->_format;
}
public function getFormat() {
return $this->_format;
}
public function setMaxWidth($aInt) {
$i = (int) $aInt;
if ($i > 0)
$this->_maxwidth = $i;
return $this->_maxwidth;
}
public function getMaxWidth() {
return $this->_maxwidth;
}
public function setMaxHeight($aInt) {
$i = (int) $aInt;
if ($i > 0)
$this->_maxheight = $i;
return $this->_maxheight;
}
public function getMaxHeight() {
return $this->_maxheight;
}
private $_reservedJSWords =
array('break', 'case', 'catch', 'continue', 'debugger', 'default',
'delete', 'do', 'else', 'finally', 'for', 'function', 'if', 'in',
'instanceof', 'new', 'return', 'switch', 'this', 'throw', 'try',
'typeof', 'var', 'void', 'while', 'with',
'null', 'true', 'false',
'class', 'enum', 'export', 'extends', 'import', 'super',
'implements', 'interface', 'let', 'package', 'private',
'protected', 'public', 'static', 'yield', 'const');
public function setJSCallback($aStr) {
if (is_null($aStr)) {
$this->_jsCallback = null;
return null;
}
if (!preg_match('/^[$A-Za-z_][0-9A-Za-z_]*$/', $aStr))
throw new Exception('Error: callback is not a valid JavaScript name');
if (in_array($aStr, $this->_reservedJSWords))
throw new Exception('Error: callback is not a valid JavaScript name');
$this->_jsCallback = $aStr;
return $aStr;
}
public static $http_status = array(
// [Informational 1xx]
100 => 'Continue',
101 => 'Switching Protocols',
// [Successful 2xx]
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
// [Redirection 3xx]
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
306 => '(Unused)',
307 => 'Temporary Redirect',
// [Client Error 4xx]
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
// [Server Error 5xx]
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported'
);
public static function setHttpStatus($aStatusCode) {
$msg = '-';
if (isset(self::$http_status[$aStatusCode]))
$msg = self::$http_status[$aStatusCode];
if (preg_match('/cgi/', PHP_SAPI))
header('Status: '.$aStatusCode.' '.$msg);
else
header($msg, true, $aStatusCode);
}
public static $providers = array(
// a name => array of properties
// properties:
// - endpoint: the real url to request
// - endpoint-json: the real url to request the json response
// - endpoint-xml: the real url to request the xml response
// - url_regexp: the regexp pattern that the data requested should
// match
// - title: a readable label of the real service
//
// Note that a provider must define a 'endpoint' or the 2
// 'endpoint-json' and 'endpoint-xml' properties (some services uses 2
// distinct endpoints)
'youtube' =>
array('endpoint' => 'http://www.youtube.com/oembed',
'url_regexp' => 'youtube\\.com\\/watch.+v=[\\w-]+&?',
// url example: "http://www.youtube.com/watch?v=vk1HvP7NO5w"
'title' => 'YouTube'),
'dailymotion' =>
array('endpoint' => 'http://www.dailymotion.com/api/oembed/',
'url_regexp' => 'dailymotion\\.com\\/.+',
// url example: "http://www.dailymotion.com/video/x5ioet_phoenix-mars-lander_tech"
'title' => 'Dailymotion'),
'vimeo' =>
array('endpoint-json' => 'http://www.vimeo.com/api/oembed.json',
'endpoint-xml' => 'http://www.vimeo.com/api/oembed.json',
'url_regexp' => 'vimeo\\.com\\/.*',
// url example: "http://www.vimeo.com/1211060"
'title' => 'Vimeo'),
);
}
?>