Get image from metadata

This commit is contained in:
Knah Tsaeb 2018-09-26 09:20:09 +02:00
parent a787e3c648
commit 2f21a6f6b7
111 changed files with 20898 additions and 729 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@ cache/*
!cache/index.html
.project
.settings
composer.lock

5
composer.json Normal file
View file

@ -0,0 +1,5 @@
{
"require": {
"embed/embed": "^3.3"
}
}

View file

@ -1,9 +1,10 @@
<?php
use Embed\Embed;
$acceptParam['log'] = array(
'suspect',
'success',
'other',
'retry'
'retry',
);
if (isset($ui['pwd'])) {
checkAdmin($ui['pwd']);
@ -14,16 +15,18 @@ if (isset($ui['log']) && !in_array($ui['log'], $acceptParam['log']) || $_SESSION
if (empty($ui['log']) && empty($ui['ac'])) {
$ui['log'] = 'success';
}
function clearLog($log) {
function clearLog($log)
{
if (file_exists('cache/logs/' . $log . '.txt') && is_file('cache/logs/' . $log . '.txt')) {
file_put_contents('cache/logs/' . $log . '.txt', '');
}
}
function parseSuccessLog() {
function parseSuccessLog()
{
if (file_exists('cache/logs/success.txt')) {
$file = fopen('cache/logs/success.txt', "r");
$res = '';
$res = array();
if (!empty($file)) {
while (!feof($file)) {
$currentLine = trim(fgets($file));
@ -41,11 +44,12 @@ function parseSuccessLog() {
}
}
function parseFailLog($log) {
function parseFailLog($log)
{
if (empty($log) || ($log !== 'suspect' && $log !== 'retry' && $log !== 'other')) {
die('Are you sure about this.');
}
$res = '';
$res = array();
$list = glob('cache/logs/' . $log . '/*.log');
if (!empty($list)) {
foreach ($list as $value) {
@ -57,14 +61,14 @@ function parseFailLog($log) {
$line[1],
$line[2],
$line[4],
'Error '.$line[7].' - '
'Error ' . $line[7] . ' - ',
);
} else {
$res[] = array(
$line[1],
$line[2],
$line[4],
''
'',
);
}
}
@ -72,7 +76,8 @@ function parseFailLog($log) {
return $res;
}
function manualGen($hash, $log) {
function manualGen($hash, $log)
{
$line = file_get_contents('cache/logs/' . $log . '/' . $hash . '.log');
$line = trim($line);
$line = explode(' --- ', $line);
@ -92,7 +97,8 @@ function manualGen($hash, $log) {
return $res;
}
function delAndRegen($hash, $size) {
function delAndRegen($hash, $size)
{
$path = pathForFile($size, $hash);
if (file_exists($path . '.png')) {
unlink($path . '.png');
@ -105,7 +111,8 @@ function delAndRegen($hash, $size) {
}
}
function tryRootDomain($hash, $log) {
function tryRootDomain($hash, $log)
{
$line = file_get_contents('cache/logs/' . $log . '/' . $hash . '.log');
$line = trim($line);
$line = explode(' --- ', $line);
@ -121,14 +128,20 @@ function tryRootDomain($hash, $log) {
$res['filePath'] = pathForFile($line[4], $hash) . '.png';
$res['base64'] = 'data:image/png;base64,' . base64_encode(file_get_contents($res['filePath']));
return $res;
} else {
$res['error'] = 1;
$res['msg'] = 'Error with root domain';
return $res;
}
}
function keepThisThumb($hash, $log) {
function keepThisThumb($hash, $log)
{
unlink('cache/logs/' . $log . '/' . $hash . '.log');
}
function tryWithCurl($hash, $log) {
function tryWithCurl($hash, $log)
{
$line = file_get_contents('cache/logs/' . $log . '/' . $hash . '.log');
$line = trim($line);
$line = explode(' --- ', $line);
@ -143,12 +156,12 @@ function tryWithCurl($hash, $log) {
$searchStr = array(
'href="//',
'src="//',
'content="//'
'content="//',
);
$replaceStr = array(
'href="http://',
'src="http://',
'content="http://'
'content="http://',
);
$resultat = str_replace($searchStr, $replaceStr, $resultat);
file_put_contents('cache/tmp/' . $line[2] . '.html', $resultat);
@ -161,9 +174,45 @@ function tryWithCurl($hash, $log) {
$res['filePath'] = pathForFile($line[4], $hash) . '.png';
$res['base64'] = 'data:image/png;base64,' . base64_encode(file_get_contents($res['filePath']));
return $res;
} else {
$res['error'] = 1;
$res['msg'] = 'Error with curl'.n_print($result);
return $res;
}
}
function tryWithOpenGraph($hash, $log)
{
require_once 'vendor/autoload.php';
$line = file_get_contents('cache/logs/' . $log . '/' . $hash . '.log');
$line = trim($line);
$line = explode(' --- ', $line);
$url = $line[1];
$startPath = '';
$info = Embed::create($url);
$ext = testIfImg($info->image);
if ($ext !== false) {
$original = file_get_contents($info->image);
file_put_contents('cache/tmp/' . $hash . '.' . $ext, $original);
$res['success'] = 1;
$res['filePath'] = 'cache/tmp/' . $hash . '.' . $ext;
$res['base64'] = 'data:image/png;base64,' . base64_encode($original);
unlink('cache/tmp/' . $hash . '.' . $ext);
$startPath = 'cache/img/' . $line[4] . '/' . substr($hash, 0, 2) . '/' . substr($hash, 2, 2) . '/';
$width = explode("x", $line[3]);
$thumb = makeImgThumb($info->image, $ext, $hash, $width[0], $startPath);
return $res;
} else {
$res['error'] = 1;
$res['msg'] = 'Error with OpenGraph data';
return $res;
}
}
if (isset($ui['clear']) && (int) $ui['clear'] === 1) {
clearLog($ui['log']);
}
@ -200,7 +249,7 @@ echo '<div class="hMenu">
</div>
<div id="log">';
if (isset($ui['ac']) && $ui['ac'] === 'delete') {
if ($ui['delThumb']) {
if (!empty($ui['delThumb']) && $ui['delThumb']) {
delAndRegen($ui['delThumb'], $size);
}
if (isset($ui['deleteUrl']) && testValidUrl($ui['deleteUrl']) !== true) {
@ -272,6 +321,9 @@ if (isset($ui['log']) && ($ui['log'] === 'suspect' || $ui['log'] === 'retry' ||
if (isset($ui['tryCurl']) && (int) $ui['tryCurl'] === 1) {
$response = tryWithCurl($ui['hash'], $ui['log']);
}
if (isset($ui['openGraph']) && (int) $ui['openGraph'] === 1) {
$response = tryWithOpenGraph($ui['hash'], $ui['log']);
}
}
$logs = parseFailLog($ui['log']);
if (!empty($logs)) {
@ -289,23 +341,24 @@ if (!empty($response) && $response['success'] === 1) {
echo '<div id="result">';
echo '<img src="', $response['base64'], '" style="width:100%;"/>';
echo '<p><input type="text" value="http://', $_SERVER['SERVER_NAME'], '/', $response['filePath'], '" onclick="this.select()"/></p>';
echo '<a href="?p=admin&log=', $ui['log'], '&hash=', $value['1'], '&mGen=1">Manuel launch</a> / ';
echo '<a href="?p=admin&log=', $ui['log'], '&hash=', $ui['hash'], '&mGen=1">Manuel launch</a> / ';
if (extension_loaded("curl")) {
echo '<a href="?p=admin&log=', $ui['log'], '&hash=', $value['1'], '&tryCurl=1">Try with curl</a> / ';
echo '<a href="?p=admin&log=', $ui['log'], '&hash=', $ui['hash'], '&tryCurl=1">Try with curl</a> / ';
}
echo '<a href="?p=admin&log=', $ui['log'], '&hash=', $ui['hash'], '&root=1">Try with root domain</a> / ';
echo '<a href="?p=admin&log=', $ui['log'], '&hash=', $ui['hash'], '&openGraph=1">Try with OpenGraph</a> / ';
echo '<a href="?p=admin&log=', $ui['log'], '&hash=', $ui['hash'], '&keep=1">Keep this thumbshot</a>';
echo '</div>';
}
if (!empty($response) && $response['error'] === 1) {
if (!empty($response) && !empty($response['error']) && $response['error'] === 1) {
echo '<div id="result">';
echo '<p>', $response['msg'], '</p>';
echo '<a href="?p=admin&log=', $ui['log'], '&hash=', $value['1'], '&mGen=1">Manuel launch</a> / ';
echo '<a href="?p=admin&log=', $ui['log'], '&hash=', $ui['hash'], '&mGen=1">Manuel launch</a> / ';
if (extension_loaded("curl")) {
echo '<a href="?p=admin&log=', $ui['log'], '&hash=', $value['1'], '&tryCurl=1">Try with curl</a> / ';
echo '<a href="?p=admin&log=', $ui['log'], '&hash=', $ui['hash'], '&tryCurl=1">Try with curl</a> / ';
}
echo '<a href="?p=admin&log=', $ui['log'], '&hash=', $ui['hash'], '&root=1">Try with root domain</a> / ';
echo '<a href="?p=admin&log=', $ui['log'], '&hash=', $ui['hash'], '&openGraph=1">Try with OpenGraph</a> / ';
echo '<a href="?p=admin&log=', $ui['log'], '&hash=', $ui['hash'], '&keep=1">Keep this thumbshot</a>';
echo '</div>';
}
?>

View file

@ -23,7 +23,7 @@ $GLOBALS['config']['thumbSize'] = array(
'm' => '200x160',
'l' => '300x240',
'xl' => '400x320',
'xxl' => '500x400'
'xxl' => '500x400',
);
// create thumshot 1280x1024
$GLOBALS['config']['activeFullSize'] = false;
@ -39,7 +39,8 @@ if (file_exists('cache/config/options.php')) {
require 'cache/config/options.php';
}
if (get_magic_quotes_gpc()) {
function stripslashes_deep($value) {
function stripslashes_deep($value)
{
$value = is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value);
return $value;
}
@ -97,7 +98,8 @@ if ($GLOBALS['config']['NoWebPage'] === true && empty($_GET)) {
echo "The page that you have requested could not be found.";
exit();
}
function testExistImg($file) {
function testExistImg($file)
{
if ($GLOBALS['config']['activeFullSize'] && $GLOBALS['config']['activeComplete']) {
if (file_exists($file . '_thumb.png') && file_exists($file . '.png') && file_exists($file . '_complete.png')) {
return true;
@ -137,7 +139,8 @@ function testExistImg($file) {
* @param $waitForResult (bool)
* @return
*/
function launchScript($url, $hashUrl, $width, $size, $waitForResult = false) {
function launchScript($url, $hashUrl, $width, $size, $waitForResult = false)
{
$oriHash = $hashUrl;
$hashUrl = escapeshellarg($hashUrl);
$url = escapeshellarg($url);
@ -171,7 +174,8 @@ function launchScript($url, $hashUrl, $width, $size, $waitForResult = false) {
return $result;
}
function makeQueueFile($url, $hashUrl, $width, $size, $renderType) {
function makeQueueFile($url, $hashUrl, $width, $size, $renderType)
{
$url = str_replace('\'', '', $url);
$hashUrl = str_replace('\'', '', $hashUrl);
$width = str_replace('\'', '', $width);
@ -181,13 +185,14 @@ function makeQueueFile($url, $hashUrl, $width, $size, $renderType) {
}
}
function testValidUrl($url) {
function testValidUrl($url)
{
$url = trim($url);
if (filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED)) {
$url = parse_url($url);
if (!in_array($url['scheme'], array(
'http',
'https'
'https',
))) {
return array('msg' => 'Url must be start by http or https.');
}
@ -197,21 +202,24 @@ function testValidUrl($url) {
}
}
function genToken() {
function genToken()
{
$token = sha1(uniqid(rand(), true) . '_' . mt_rand());
$_SESSION['token'] = $token;
$_SESSION['tokenTime'] = time();
return $token;
}
function verifToken($token) {
function verifToken($token)
{
if ($token !== $_SESSION['token'] || $_SESSION['tokenTime'] <= time() - 24000) {
ban();
die('So Long, and Thanks for All the Fish.');
}
}
function checkIfBan() {
function checkIfBan()
{
require 'cache/logs/banUser.php';
$userIp = $_SERVER['REMOTE_ADDR'];
if (isset($banList[$userIp]) && $banList[$userIp]['nbBan'] >= $GLOBALS['config']['maxErrorBeforeBan'] && $banList[$userIp]['lastBan'] + $GLOBALS['config']['banTime'] > time()) {
@ -224,7 +232,8 @@ function checkIfBan() {
}
}
function ban() {
function ban()
{
require 'cache/logs/banUser.php';
$userIp = $_SERVER['REMOTE_ADDR'];
if (isset($banList[$userIp])) {
@ -237,14 +246,16 @@ function ban() {
file_put_contents('cache/logs/banUser.php', "<?php\n\$banList=" . var_export($banList, true) . ";\n?>");
}
function unBan() {
function unBan()
{
require 'cache/logs/banUser.php';
$userIp = $_SERVER['REMOTE_ADDR'];
unset($banList[$userIp]);
file_put_contents('cache/logs/banUser.php', "<?php\n\$banList=" . var_export($banList, true) . ";\n?>");
}
function install() {
function install()
{
if (!is_writable('cache')) {
die('Make dir "cache" writable');
}
@ -303,7 +314,8 @@ function install() {
return $serverKey;
}
function checkInstall() {
function checkInstall()
{
if (!is_file('.htaccess')) {
file_put_contents('.htaccess', "AddDefaultCharset UTF-8\nOptions -Indexes\nDirectoryIndex index.php index.html\nFileETag none\nSetOutputFilter DEFLATE\n");
}
@ -321,7 +333,8 @@ function checkInstall() {
}
}
function removeOlderThan($dir = 'cache/img/shortLive') {
function removeOlderThan($dir = 'cache/img/shortLive')
{
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
@ -339,7 +352,8 @@ function removeOlderThan($dir = 'cache/img/shortLive') {
}
}
function pathForFile($size, $hashUrl) {
function pathForFile($size, $hashUrl)
{
$startPath = substr($hashUrl, 0, 2) . '/' . substr($hashUrl, 2, 2) . '/';
if (empty($size)) {
$size = tryDetectSize($startPath . $hashUrl);
@ -348,7 +362,8 @@ function pathForFile($size, $hashUrl) {
return $file;
}
function tryDetectSize($file) {
function tryDetectSize($file)
{
if (is_file('cache/img/s/' . $file . '_thumb.png')) {
return 's';
}
@ -366,7 +381,8 @@ function tryDetectSize($file) {
}
}
function validHash($hash) {
function validHash($hash)
{
if (empty($hash)) {
die('You talking to me');
}
@ -376,7 +392,8 @@ function validHash($hash) {
return $hash;
}
function checkAdmin($pwd) {
function checkAdmin($pwd)
{
$pwd = sha1($GLOBALS['config']['salt'] . $pwd . $GLOBALS['config']['serverKey']);
if (validHash($pwd) !== $GLOBALS['config']['pwd']) {
ban();
@ -386,7 +403,8 @@ function checkAdmin($pwd) {
return true;
}
function savePass($passOne, $passTwo, $token) {
function savePass($passOne, $passTwo, $token)
{
verifToken($token);
if ($passOne !== $passTwo || empty($passOne) || empty($passTwo)) {
reloadInstall();
@ -408,7 +426,8 @@ function savePass($passOne, $passTwo, $token) {
header("Location:?");
}
function reloadInstall() {
function reloadInstall()
{
array_map('unlink', glob("cache/config/*"));
}
@ -422,7 +441,8 @@ function reloadInstall() {
* @return false affiche les clef valeur du tableau $data
* @example n_print($array, 'Tableau de valeur');
*/
function n_print($data, $name = '') {
function n_print($data, $name = '')
{
$aBackTrace = debug_backtrace();
echo '<h2>', $name, '</h2>';
echo '<fieldset style="border: 1px solid orange; padding: 5px;color: #333; background-color: #fff;">';
@ -436,7 +456,8 @@ function n_print($data, $name = '') {
';
}
function printThumbShot($file, $renderType) {
function printThumbShot($file, $renderType)
{
ob_end_clean();
header("Content-type: image/png");
header('Expires: ', gmdate('D, d M Y H:i:s', time()) . ' GMT');
@ -454,7 +475,8 @@ function printThumbShot($file, $renderType) {
exit();
}
function getPage($page) {
function getPage($page)
{
$page = htmlspecialchars($page);
switch ($page) {
case 'login':
@ -472,16 +494,18 @@ function getPage($page) {
}
}
function testIfImg($url) {
function testIfImg($url)
{
$ext = strtolower(pathinfo($url, PATHINFO_EXTENSION));
if ($ext === 'jpg' || $ext === 'jpeg' || $ext === 'png' || $ext === 'gif') {
if ($ext === 'jpg' || $ext === 'jpeg' || $ext === 'png' || $ext === 'gif' || $ext === 'ico') {
return $ext;
} else {
return false;
}
}
function makeImgThumb($url, $ext, $hashUrl, $width, $path, $renderType) {
function makeImgThumb($url, $ext, $hashUrl, $width, $path, $renderType=null)
{
if (!function_exists('imagecreatefromjpeg')) {
return false;
}
@ -526,16 +550,17 @@ function makeImgThumb($url, $ext, $hashUrl, $width, $path, $renderType) {
}
}
function checkHmac($receiveHmac, $url){
function checkHmac($receiveHmac, $url)
{
if ($receiveHmac === makeHmac($url, $GLOBALS['config']['apikey'])) {
return true;
}
else {
} else {
return false;
}
}
function makeHmac($url){
function makeHmac($url)
{
return hash_hmac('sha1', $url, $GLOBALS['config']['apikey']);
}
@ -585,7 +610,7 @@ if (!empty($ui['request']) && $ui['p'] !== 'install' && $ui['p'] !== 'login' &&
$success = array(
'normal' => $file . '.png',
'thumb' => $file . '_thumb.png',
'complete' => $file.'_complete.png'
'complete' => $file . '_complete.png',
);
}
} else {
@ -610,7 +635,7 @@ if (!empty($ui['request']) && $ui['p'] !== 'install' && $ui['p'] !== 'login' &&
$success = array(
'normal' => 'bin/error.png',
'thumb' => 'bin/error_thumb.png',
'complete' => 'bin/error.png'
'complete' => 'bin/error.png',
);
}
} else {
@ -630,7 +655,7 @@ if (!empty($ui['request']) && $ui['p'] !== 'install' && $ui['p'] !== 'login' &&
$success = array(
'normal' => $file . '.png',
'thumb' => $file . '_thumb.png',
'complete' => $file.'_complete.png'
'complete' => $file . '_complete.png',
);
}
}
@ -652,7 +677,7 @@ if (!empty($ui['request']) && $ui['p'] !== 'install' && $ui['p'] !== 'login' &&
$success = array(
'normal' => $file . '.png',
'thumb' => $file . '_thumb.png',
'complete' => $file.'_complete.png'
'complete' => $file . '_complete.png',
);
}
}

7
vendor/autoload.php vendored Normal file
View file

@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit0e506de7fccd419bf18972ed94446862::getLoader();

445
vendor/composer/ClassLoader.php vendored Normal file
View file

@ -0,0 +1,445 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

21
vendor/composer/LICENSE vendored Normal file
View file

@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
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.

9
vendor/composer/autoload_classmap.php vendored Normal file
View file

@ -0,0 +1,9 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View file

@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

11
vendor/composer/autoload_psr4.php vendored Normal file
View file

@ -0,0 +1,11 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Embed\\' => array($vendorDir . '/embed/embed/src'),
'Composer\\CaBundle\\' => array($vendorDir . '/composer/ca-bundle/src'),
);

52
vendor/composer/autoload_real.php vendored Normal file
View file

@ -0,0 +1,52 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit0e506de7fccd419bf18972ed94446862
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit0e506de7fccd419bf18972ed94446862', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit0e506de7fccd419bf18972ed94446862', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit0e506de7fccd419bf18972ed94446862::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
return $loader;
}
}

39
vendor/composer/autoload_static.php vendored Normal file
View file

@ -0,0 +1,39 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit0e506de7fccd419bf18972ed94446862
{
public static $prefixLengthsPsr4 = array (
'E' =>
array (
'Embed\\' => 6,
),
'C' =>
array (
'Composer\\CaBundle\\' => 18,
),
);
public static $prefixDirsPsr4 = array (
'Embed\\' =>
array (
0 => __DIR__ . '/..' . '/embed/embed/src',
),
'Composer\\CaBundle\\' =>
array (
0 => __DIR__ . '/..' . '/composer/ca-bundle/src',
),
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit0e506de7fccd419bf18972ed94446862::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit0e506de7fccd419bf18972ed94446862::$prefixDirsPsr4;
}, null, ClassLoader::class);
}
}

19
vendor/composer/ca-bundle/LICENSE vendored Normal file
View file

@ -0,0 +1,19 @@
Copyright (C) 2016 Composer
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.

85
vendor/composer/ca-bundle/README.md vendored Normal file
View file

@ -0,0 +1,85 @@
composer/ca-bundle
==================
Small utility library that lets you find a path to the system CA bundle,
and includes a fallback to the Mozilla CA bundle.
Originally written as part of [composer/composer](https://github.com/composer/composer),
now extracted and made available as a stand-alone library.
Installation
------------
Install the latest version with:
```bash
$ composer require composer/ca-bundle
```
Requirements
------------
* PHP 5.3.2 is required but using the latest version of PHP is highly recommended.
Basic usage
-----------
# `Composer\CaBundle\CaBundle`
- `CaBundle::getSystemCaRootBundlePath()`: Returns the system CA bundle path, or a path to the bundled one as fallback
- `CaBundle::getBundledCaBundlePath()`: Returns the path to the bundled CA file
- `CaBundle::validateCaFile($filename)`: Validates a CA file using opensl_x509_parse only if it is safe to use
- `CaBundle::isOpensslParseSafe()`: Test if it is safe to use the PHP function openssl_x509_parse()
- `CaBundle::reset()`: Resets the static caches
## To use with curl
```php
$curl = curl_init("https://example.org/");
$caPathOrFile = \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath();
if (is_dir($caPathOrFile) || (is_link($caPathOrFile) && is_dir(readlink($caPathOrFile)))) {
curl_setopt($curl, CURLOPT_CAPATH, $caPathOrFile);
} else {
curl_setopt($curl, CURLOPT_CAINFO, $caPathOrFile);
}
$result = curl_exec($curl);
```
## To use with php streams
```php
$opts = array(
'http' => array(
'method' => "GET"
)
);
$caPathOrFile = \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath();
if (is_dir($caPathOrFile) || (is_link($caPathOrFile) && is_dir(readlink($caPathOrFile)))) {
$opts['ssl']['capath'] = $caPathOrFile;
} else {
$opts['ssl']['cafile'] = $caPathOrFile;
}
$context = stream_context_create($opts);
$result = file_get_contents('https://example.com', false, $context);
```
## To use with Guzzle
```php
$client = new \GuzzleHttp\Client([
\GuzzleHttp\RequestOptions::VERIFY => \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath()
]);
```
License
-------
composer/ca-bundle is licensed under the MIT License, see the LICENSE file for details.

54
vendor/composer/ca-bundle/composer.json vendored Normal file
View file

@ -0,0 +1,54 @@
{
"name": "composer/ca-bundle",
"description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
"type": "library",
"license": "MIT",
"keywords": [
"cabundle",
"cacert",
"certificate",
"ssl",
"tls"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"support": {
"irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/ca-bundle/issues"
},
"require": {
"ext-openssl": "*",
"ext-pcre": "*",
"php": "^5.3.2 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5",
"psr/log": "^1.0",
"symfony/process": "^2.5 || ^3.0 || ^4.0"
},
"autoload": {
"psr-4": {
"Composer\\CaBundle\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Composer\\CaBundle\\": "tests"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"config": {
"platform": {
"php": "5.3.9"
}
}
}

3314
vendor/composer/ca-bundle/res/cacert.pem vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,308 @@
<?php
/*
* This file is part of composer/ca-bundle.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Composer\CaBundle;
use Psr\Log\LoggerInterface;
use Symfony\Component\Process\PhpProcess;
/**
* @author Chris Smith <chris@cs278.org>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class CaBundle
{
private static $caPath;
private static $caFileValidity = array();
private static $useOpensslParse;
/**
* Returns the system CA bundle path, or a path to the bundled one
*
* This method was adapted from Sslurp.
* https://github.com/EvanDotPro/Sslurp
*
* (c) Evan Coury <me@evancoury.com>
*
* For the full copyright and license information, please see below:
*
* Copyright (c) 2013, Evan Coury
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @param LoggerInterface $logger optional logger for information about which CA files were loaded
* @return string path to a CA bundle file or directory
*/
public static function getSystemCaRootBundlePath(LoggerInterface $logger = null)
{
if (self::$caPath !== null) {
return self::$caPath;
}
// If SSL_CERT_FILE env variable points to a valid certificate/bundle, use that.
// This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
$envCertFile = getenv('SSL_CERT_FILE');
if ($envCertFile && is_readable($envCertFile) && static::validateCaFile($envCertFile, $logger)) {
return self::$caPath = $envCertFile;
}
// If SSL_CERT_DIR env variable points to a valid certificate/bundle, use that.
// This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
$envCertDir = getenv('SSL_CERT_DIR');
if ($envCertDir && is_dir($envCertDir) && is_readable($envCertDir)) {
return self::$caPath = $envCertDir;
}
$configured = ini_get('openssl.cafile');
if ($configured && strlen($configured) > 0 && is_readable($configured) && static::validateCaFile($configured, $logger)) {
return self::$caPath = $configured;
}
$configured = ini_get('openssl.capath');
if ($configured && is_dir($configured) && is_readable($configured)) {
return self::$caPath = $configured;
}
$caBundlePaths = array(
'/etc/pki/tls/certs/ca-bundle.crt', // Fedora, RHEL, CentOS (ca-certificates package)
'/etc/ssl/certs/ca-certificates.crt', // Debian, Ubuntu, Gentoo, Arch Linux (ca-certificates package)
'/etc/ssl/ca-bundle.pem', // SUSE, openSUSE (ca-certificates package)
'/usr/local/share/certs/ca-root-nss.crt', // FreeBSD (ca_root_nss_package)
'/usr/ssl/certs/ca-bundle.crt', // Cygwin
'/opt/local/share/curl/curl-ca-bundle.crt', // OS X macports, curl-ca-bundle package
'/usr/local/share/curl/curl-ca-bundle.crt', // Default cURL CA bunde path (without --with-ca-bundle option)
'/usr/share/ssl/certs/ca-bundle.crt', // Really old RedHat?
'/etc/ssl/cert.pem', // OpenBSD
'/usr/local/etc/ssl/cert.pem', // FreeBSD 10.x
'/usr/local/etc/openssl/cert.pem', // OS X homebrew, openssl package
);
foreach ($caBundlePaths as $caBundle) {
if (@is_readable($caBundle) && static::validateCaFile($caBundle, $logger)) {
return self::$caPath = $caBundle;
}
}
foreach ($caBundlePaths as $caBundle) {
$caBundle = dirname($caBundle);
if (@is_dir($caBundle) && glob($caBundle.'/*')) {
return self::$caPath = $caBundle;
}
}
return self::$caPath = static::getBundledCaBundlePath(); // Bundled CA file, last resort
}
/**
* Returns the path to the bundled CA file
*
* In case you don't want to trust the user or the system, you can use this directly
*
* @return string path to a CA bundle file
*/
public static function getBundledCaBundlePath()
{
$caBundleFile = __DIR__.'/../res/cacert.pem';
// cURL does not understand 'phar://' paths
// see https://github.com/composer/ca-bundle/issues/10
if (0 === strpos($caBundleFile, 'phar://')) {
file_put_contents(
$tempCaBundleFile = tempnam(sys_get_temp_dir(), 'openssl-ca-bundle-'),
file_get_contents($caBundleFile)
);
register_shutdown_function(function() use ($tempCaBundleFile) {
@unlink($tempCaBundleFile);
});
$caBundleFile = $tempCaBundleFile;
}
return $caBundleFile;
}
/**
* Validates a CA file using opensl_x509_parse only if it is safe to use
*
* @param string $filename
* @param LoggerInterface $logger optional logger for information about which CA files were loaded
*
* @return bool
*/
public static function validateCaFile($filename, LoggerInterface $logger = null)
{
static $warned = false;
if (isset(self::$caFileValidity[$filename])) {
return self::$caFileValidity[$filename];
}
$contents = file_get_contents($filename);
// assume the CA is valid if php is vulnerable to
// https://www.sektioneins.de/advisories/advisory-012013-php-openssl_x509_parse-memory-corruption-vulnerability.html
if (!static::isOpensslParseSafe()) {
if (!$warned && $logger) {
$logger->warning(sprintf(
'Your version of PHP, %s, is affected by CVE-2013-6420 and cannot safely perform certificate validation, we strongly suggest you upgrade.',
PHP_VERSION
));
$warned = true;
}
$isValid = !empty($contents);
} else {
$isValid = (bool) openssl_x509_parse($contents);
}
if ($logger) {
$logger->debug('Checked CA file '.realpath($filename).': '.($isValid ? 'valid' : 'invalid'));
}
return self::$caFileValidity[$filename] = $isValid;
}
/**
* Test if it is safe to use the PHP function openssl_x509_parse().
*
* This checks if OpenSSL extensions is vulnerable to remote code execution
* via the exploit documented as CVE-2013-6420.
*
* @return bool
*/
public static function isOpensslParseSafe()
{
if (null !== self::$useOpensslParse) {
return self::$useOpensslParse;
}
if (PHP_VERSION_ID >= 50600) {
return self::$useOpensslParse = true;
}
// Vulnerable:
// PHP 5.3.0 - PHP 5.3.27
// PHP 5.4.0 - PHP 5.4.22
// PHP 5.5.0 - PHP 5.5.6
if (
(PHP_VERSION_ID < 50400 && PHP_VERSION_ID >= 50328)
|| (PHP_VERSION_ID < 50500 && PHP_VERSION_ID >= 50423)
|| (PHP_VERSION_ID < 50600 && PHP_VERSION_ID >= 50507)
) {
// This version of PHP has the fix for CVE-2013-6420 applied.
return self::$useOpensslParse = true;
}
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
// Windows is probably insecure in this case.
return self::$useOpensslParse = false;
}
$compareDistroVersionPrefix = function ($prefix, $fixedVersion) {
$regex = '{^'.preg_quote($prefix).'([0-9]+)$}';
if (preg_match($regex, PHP_VERSION, $m)) {
return ((int) $m[1]) >= $fixedVersion;
}
return false;
};
// Hard coded list of PHP distributions with the fix backported.
if (
$compareDistroVersionPrefix('5.3.3-7+squeeze', 18) // Debian 6 (Squeeze)
|| $compareDistroVersionPrefix('5.4.4-14+deb7u', 7) // Debian 7 (Wheezy)
|| $compareDistroVersionPrefix('5.3.10-1ubuntu3.', 9) // Ubuntu 12.04 (Precise)
) {
return self::$useOpensslParse = true;
}
// Symfony Process component is missing so we assume it is unsafe at this point
if (!class_exists('Symfony\Component\Process\PhpProcess')) {
return self::$useOpensslParse = false;
}
// This is where things get crazy, because distros backport security
// fixes the chances are on NIX systems the fix has been applied but
// it's not possible to verify that from the PHP version.
//
// To verify exec a new PHP process and run the issue testcase with
// known safe input that replicates the bug.
// Based on testcase in https://github.com/php/php-src/commit/c1224573c773b6845e83505f717fbf820fc18415
// changes in https://github.com/php/php-src/commit/76a7fd893b7d6101300cc656058704a73254d593
$cert = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVwRENDQTR5Z0F3SUJBZ0lKQUp6dThyNnU2ZUJjTUEwR0NTcUdTSWIzRFFFQkJRVUFNSUhETVFzd0NRWUQKVlFRR0V3SkVSVEVjTUJvR0ExVUVDQXdUVG05eVpISm9aV2x1TFZkbGMzUm1ZV3hsYmpFUU1BNEdBMVVFQnd3SApTOE9Ed3Jac2JqRVVNQklHQTFVRUNnd0xVMlZyZEdsdmJrVnBibk14SHpBZEJnTlZCQXNNRmsxaGJHbGphVzkxCmN5QkRaWEowSUZObFkzUnBiMjR4SVRBZkJnTlZCQU1NR0cxaGJHbGphVzkxY3k1elpXdDBhVzl1WldsdWN5NWsKWlRFcU1DZ0dDU3FHU0liM0RRRUpBUlliYzNSbFptRnVMbVZ6YzJWeVFITmxhM1JwYjI1bGFXNXpMbVJsTUhVWQpaREU1TnpBd01UQXhNREF3TURBd1dnQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBCkFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFBQUFBQVhEVEUwTVRFeU9ERXhNemt6TlZvd2djTXhDekFKQmdOVkJBWVRBa1JGTVJ3d0dnWURWUVFJREJOTwpiM0prY21obGFXNHRWMlZ6ZEdaaGJHVnVNUkF3RGdZRFZRUUhEQWRMdzRQQ3RteHVNUlF3RWdZRFZRUUtEQXRUClpXdDBhVzl1UldsdWN6RWZNQjBHQTFVRUN3d1dUV0ZzYVdOcGIzVnpJRU5sY25RZ1UyVmpkR2x2YmpFaE1COEcKQTFVRUF3d1liV0ZzYVdOcGIzVnpMbk5sYTNScGIyNWxhVzV6TG1SbE1Tb3dLQVlKS29aSWh2Y05BUWtCRmh0egpkR1ZtWVc0dVpYTnpaWEpBYzJWcmRHbHZibVZwYm5NdVpHVXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCCkR3QXdnZ0VLQW9JQkFRRERBZjNobDdKWTBYY0ZuaXlFSnBTU0RxbjBPcUJyNlFQNjV1c0pQUnQvOFBhRG9xQnUKd0VZVC9OYSs2ZnNnUGpDMHVLOURaZ1dnMnRIV1dvYW5TYmxBTW96NVBINlorUzRTSFJaN2UyZERJalBqZGhqaAowbUxnMlVNTzV5cDBWNzk3R2dzOWxOdDZKUmZIODFNTjJvYlhXczROdHp0TE11RDZlZ3FwcjhkRGJyMzRhT3M4CnBrZHVpNVVhd1Raa3N5NXBMUEhxNWNNaEZHbTA2djY1Q0xvMFYyUGQ5K0tBb2tQclBjTjVLTEtlYno3bUxwazYKU01lRVhPS1A0aWRFcXh5UTdPN2ZCdUhNZWRzUWh1K3ByWTNzaTNCVXlLZlF0UDVDWm5YMmJwMHdLSHhYMTJEWAoxbmZGSXQ5RGJHdkhUY3lPdU4rblpMUEJtM3ZXeG50eUlJdlZBZ01CQUFHalFqQkFNQWtHQTFVZEV3UUNNQUF3CkVRWUpZSVpJQVliNFFnRUJCQVFEQWdlQU1Bc0dBMVVkRHdRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQU5CZ2txaGtpRzl3MEJBUVVGQUFPQ0FRRUFHMGZaWVlDVGJkajFYWWMrMVNub2FQUit2SThDOENhRAo4KzBVWWhkbnlVNGdnYTBCQWNEclk5ZTk0ZUVBdTZacXljRjZGakxxWFhkQWJvcHBXb2NyNlQ2R0QxeDMzQ2tsClZBcnpHL0t4UW9oR0QySmVxa2hJTWxEb214SE83a2EzOStPYThpMnZXTFZ5alU4QVp2V01BcnVIYTRFRU55RzcKbFcyQWFnYUZLRkNyOVRuWFRmcmR4R1ZFYnY3S1ZRNmJkaGc1cDVTanBXSDErTXEwM3VSM1pYUEJZZHlWODMxOQpvMGxWajFLRkkyRENML2xpV2lzSlJvb2YrMWNSMzVDdGQwd1lCY3BCNlRac2xNY09QbDc2ZHdLd0pnZUpvMlFnClpzZm1jMnZDMS9xT2xOdU5xLzBUenprVkd2OEVUVDNDZ2FVK1VYZTRYT1Z2a2NjZWJKbjJkZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K';
$script = <<<'EOT'
error_reporting(-1);
$info = openssl_x509_parse(base64_decode('%s'));
var_dump(PHP_VERSION, $info['issuer']['emailAddress'], $info['validFrom_time_t']);
EOT;
$script = '<'."?php\n".sprintf($script, $cert);
try {
$process = new PhpProcess($script);
$process->mustRun();
} catch (\Exception $e) {
// In the case of any exceptions just accept it is not possible to
// determine the safety of openssl_x509_parse and bail out.
return self::$useOpensslParse = false;
}
$output = preg_split('{\r?\n}', trim($process->getOutput()));
$errorOutput = trim($process->getErrorOutput());
if (
count($output) === 3
&& $output[0] === sprintf('string(%d) "%s"', strlen(PHP_VERSION), PHP_VERSION)
&& $output[1] === 'string(27) "stefan.esser@sektioneins.de"'
&& $output[2] === 'int(-1)'
&& preg_match('{openssl_x509_parse\(\): illegal (?:ASN1 data type for|length in) timestamp in - on line \d+}', $errorOutput)
) {
// This PHP has the fix backported probably by a distro security team.
return self::$useOpensslParse = true;
}
return self::$useOpensslParse = false;
}
/**
* Resets the static caches
*/
public static function reset()
{
self::$caFileValidity = array();
self::$caPath = null;
self::$useOpensslParse = null;
}
}

115
vendor/composer/installed.json vendored Normal file
View file

@ -0,0 +1,115 @@
[
{
"name": "composer/ca-bundle",
"version": "1.1.2",
"version_normalized": "1.1.2.0",
"source": {
"type": "git",
"url": "https://github.com/composer/ca-bundle.git",
"reference": "46afded9720f40b9dc63542af4e3e43a1177acb0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/46afded9720f40b9dc63542af4e3e43a1177acb0",
"reference": "46afded9720f40b9dc63542af4e3e43a1177acb0",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"ext-pcre": "*",
"php": "^5.3.2 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5",
"psr/log": "^1.0",
"symfony/process": "^2.5 || ^3.0 || ^4.0"
},
"time": "2018-08-08T08:57:40+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Composer\\CaBundle\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
"keywords": [
"cabundle",
"cacert",
"certificate",
"ssl",
"tls"
]
},
{
"name": "embed/embed",
"version": "v3.3.6",
"version_normalized": "3.3.6.0",
"source": {
"type": "git",
"url": "https://github.com/oscarotero/Embed.git",
"reference": "4b4ab5f5a02563ba4a0707baad00eef2e376108a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/oscarotero/Embed/zipball/4b4ab5f5a02563ba4a0707baad00eef2e376108a",
"reference": "4b4ab5f5a02563ba4a0707baad00eef2e376108a",
"shasum": ""
},
"require": {
"composer/ca-bundle": "^1.0",
"ext-curl": "*",
"ext-mbstring": "*",
"php": "^5.5|^7.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.0",
"phpunit/phpunit": "^4.8|^5.7"
},
"time": "2018-09-13T19:10:15+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Embed\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Oscar Otero",
"email": "oom@oscarotero.com",
"homepage": "http://oscarotero.com",
"role": "Developer"
}
],
"description": "PHP library to retrieve page info using oembed, opengraph, etc",
"homepage": "https://github.com/oscarotero/Embed",
"keywords": [
"embed",
"embedly",
"oembed",
"opengraph",
"twitter cards"
]
}
]

21
vendor/embed/embed/LICENSE vendored Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017 Oscar Otero Marzoa
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.

302
vendor/embed/embed/README.md vendored Normal file
View file

@ -0,0 +1,302 @@
# Embed
[![Build Status][ico-travis]][link-travis]
[![Quality Score][ico-scrutinizer]][link-scrutinizer]
[![Reference Status][ico-references]][link-references]
[![Latest Version on Packagist][ico-version]][link-packagist]
[![Total Downloads][ico-downloads]][link-packagist]
[![Monthly Downloads][ico-m-downloads]][link-packagist]
[![Software License][ico-license]](LICENSE)
[![SensioLabs Insight][ico-sensiolabs]][link-sensiolabs]
PHP library to get information from any web page (using oembed, opengraph, twitter-cards, scrapping the html, etc). It's compatible with any web service (youtube, vimeo, flickr, instagram, etc) and has adapters to some sites like (archive.org, github, facebook, etc).
Requirements:
* PHP 5.5+
* Curl library installed
>
* If you need PHP 5.3 support, use the 1.x version
* If you need PHP 5.4 support, use the 2.x version
## Online demo
http://oscarotero.com/embed3/demo
## Installation
This package is installable and autoloadable via Composer as [embed/embed](https://packagist.org/packages/embed/embed).
```
$ composer require embed/embed
```
If you cannot (or don't want to) use composer, just include the [PSR-4 autoload.php file](src/autoloader.php) in your code.
## Usage
```php
use Embed\Embed;
//Load any url:
$info = Embed::create('https://www.youtube.com/watch?v=PP1xn5wHtxE');
//Get content info
$info->title; //The page title
$info->description; //The page description
$info->url; //The canonical url
$info->type; //The page type (link, video, image, rich)
$info->tags; //The page keywords (tags)
$info->images; //List of all images found in the page
$info->image; //The image choosen as main image
$info->imageWidth; //The width of the main image
$info->imageHeight; //The height of the main image
$info->code; //The code to embed the image, video, etc
$info->width; //The width of the embed code
$info->height; //The height of the embed code
$info->aspectRatio; //The aspect ratio (width/height)
$info->authorName; //The resource author
$info->authorUrl; //The author url
$info->providerName; //The provider name of the page (Youtube, Twitter, Instagram, etc)
$info->providerUrl; //The provider url
$info->providerIcons; //All provider icons found in the page
$info->providerIcon; //The icon choosen as main icon
$info->publishedDate; //The published date of the resource
$info->license; //The license url of the resource
$info->linkedData; //The linked-data info (http://json-ld.org/)
$info->feeds; //The RSS/Atom feeds
```
## The adapter
The adapter is the class that get all information of the page from the providers and choose the best result for each value. For example, a page can provide multiple titles from opengraph, twitter cards, oembed, the `<title>` html element, etc, so the adapter get all this titles and choose the best one.
Embed has an generic adapter called "Webpage" to use with any web but has also some specific adapters for sites like archive.org, facebook, google, github, spotify, etc, that provides information using their own apis, or need to fix any specific issue.
The available options for the adapters are:
Name | Type | Description
-----|------|------------
`min_image_width` | `int` | Minimal image width used to choose the main image
`min_image_height` | `int` | Minimal image height used to choose the main image
`choose_bigger_image` | `bool` | Choose the bigger image as the main image (instead the first found, that usually is the most relevant).
`images_blacklist` | `string`&#124;`array` | Images that you don't want to be used. Could be plain text or [Uri](https://github.com/oscarotero/Embed/blob/master/src/Http/Url.php) match pattern.
`url_blacklist` | `string`&#124;`array` | URLs that you don't want to be used. Could be plain text or [Uri](https://github.com/oscarotero/Embed/blob/master/src/Http/Url.php) match pattern.
`follow_canonical` | `bool` | Whether to redirect to the canonical URL or not.
`custom_adapters_namespace` | `string`&#124;`array` | A namespace used to load custom adapters. This allows to override the behaviour of existing adapters or add support for more sites.
## The providers
The providers get the data from different sources. Each source has it's own provider. For example, there is a provider for opengraph, other for twitter cards, for oembed, html, etc. Some providers can be configured and are the following:
### oembed
Used to get data from oembed api if it's available:
Name | Type | Description
-----|------|------------
`parameters` | `array` | Extra query parameters to send with the oembed request
`embedly_key` | `string` | If it's defined, use embed.ly api as fallback oembed provider.
`iframely_key` | `string` | If it's defined, use iframe.ly api as fallback oembed provider.
### html
Used to get data directly from the html code of the page:
Name | Type | Description
-----|------|------------
`max_images` | `int` | Max number of images fetched from the html code (searching for the `<img>` elements). By default is -1 (no limit). Use 0 to no get images.
`external_images` | `bool`&#124;`array` | By default is `false`, this means that images located in other domains are not allowed. You can set `true` (allow all) or provide an array of url patterns.
### google
Used only for google maps to generate the embed code.
Name | Type | Description
-----|------|------------
`key` | `string` | The key used [for the embed api](https://developers.google.com/maps/documentation/embed/)
### soundcloud
Used only for soundcloud pages, to get information using its api.
Name | Type | Description
-----|------|------------
`key` | `string` | The key used to get info from soundcloud API.
### facebook
Used only for facebook events (not needed for posts, images, etc), to get information using its api.
Name | Type | Description
-----|------|------------
`key` | `string` | The access token used to get info from facebook graph API.
`events_fields` | `string` | Comma-separated list of fields to query for a facebook event. Please refer to [Facebook documentation](https://developers.facebook.com/docs/graph-api/reference/event) for the full list of available fields.
`videos_fields` | `string` | Comma-separated list of fields to query for a facebook video. Please refer to [Facebook documentation](https://developers.facebook.com/docs/graph-api/reference/event) for the full list of available fields.
## Example with all options:
The options are passed as the second argument as you can see in the following example:
```php
$info = Embed::create($url, [
'min_image_width' => 100,
'min_image_height' => 100,
'choose_bigger_image' => true,
'images_blacklist' => 'example.com/*',
'url_blacklist' => 'example.com/*',
'follow_canonical' => true,
'html' => [
'max_images' => 10,
'external_images' => true
],
'oembed' => [
'parameters' => [],
'embedly_key' => 'YOUR_KEY',
'iframely_key' => 'YOUR_KEY',
],
'google' => [
'key' => 'YOUR_KEY',
],
'soundcloud' => [
'key' => 'YOUR_KEY',
],
'facebook' => [
'key' => 'YOUR_KEY',
'fields' => 'field1,field2,field3' // default : cover,description,end_time,id,name,owner,place,start_time,timezone
],
]);
```
## The dispatcher
To dispatch the http request, Embed includes the interface `Embed\Http\DispatcherInterface`. By default the curl library is used but you can create your own dispatcher to use any other library like [guzzle](https://github.com/guzzle/guzzle):
```php
use Embed\Embed;
use Embed\Http\DispatcherInteface;
use Embed\Http\Url;
use Embed\Http\Response;
use Embed\Http\ImageResponse;
class MyDispatcher implements DispatcherInterface
{
public function dispatch(Url $url)
{
$result = function_to_execute_request($url);
return new Response($url, $result['url'], $result['status'], $result['type'], $result['content'], $result['headers']);
}
public function dispatchImages(array $urls)
{
$responses = [];
foreach ($urls as $url) {
$result = function_to_get_image_size($url);
if ($result) {
$responses[] = new ImageResponse($url, $result['url'], $result['status'], $result['type'], $result['size'], $result['headers']);
}
}
return $responses;
}
}
//Use the dispatcher passing as third argument
$info = Embed::create('http://example.com', null, new MyDispatcher());
```
The default curl dispatcher accepts the same options that the [curl_setopt PHP function](http://php.net/manual/en/function.curl-setopt.php). You can edit the default values:
```php
use Embed\Embed;
use Embed\Http\CurlDispatcher;
$dispatcher = new CurlDispatcher([
CURLOPT_MAXREDIRS => 20,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 10,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_ENCODING => '',
CURLOPT_AUTOREFERER => true,
CURLOPT_USERAGENT => 'Embed PHP Library',
CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,
]);
$info = Embed::create('http://example.com', null, $dispatcher);
```
## Accessing to advanced data
The adapter get the data from all providers and choose the best values. But you can get all available values accessing directly to each provider. There's also the possibility to inspect all http requests executed for debug purposes:
```php
use Embed\Embed;
//Get the info
$info = Embed::create('https://www.youtube.com/watch?v=PP1xn5wHtxE');
//Get all providers
$providers = $info->getProviders();
//Get the oembed provider
$oembed = $providers['oembed'];
//Get the oembed title value:
echo $oembed->getTitle();
//Get any value returned by the oembed api
echo $oembed->bag->get('author_name');
//Get the main response object
$response = $info->getResponse();
//Get any http response header
$lastModified = $response->getHeader('Last-Modifier');
//Get the html body as DOMDocument
$html = $response->getHtmlContent();
//Get all http request executed (oembed endpoints, images, apis, etc...)
$dispatcher = $adapter->getDispatcher();
foreach ($dispatcher->getAllResponses() as $response) {
echo 'The request to '.$response->getStartingUrl();
echo ' is resolved to '.$response->getUrl();
}
```
---
If this library is useful for you, say thanks [buying me a beer :beer:](https://www.paypal.me/oscarotero)!
[ico-version]: https://poser.pugx.org/embed/embed/v/stable
[ico-travis]: https://travis-ci.org/oscarotero/Embed.svg?branch=master
[ico-license]: https://poser.pugx.org/embed/embed/license
[ico-scrutinizer]: https://scrutinizer-ci.com/g/oscarotero/Embed/badges/quality-score.png?s=79e37032db280b9795388124c030dcf4309343d1
[ico-sensiolabs]: https://insight.sensiolabs.com/projects/f0beab9f-fe41-47db-8806-373f80c50f9e/big.png
[ico-downloads]: https://poser.pugx.org/embed/embed/downloads
[ico-m-downloads]: https://poser.pugx.org/embed/embed/d/monthly
[ico-references]: https://www.versioneye.com/php/embed:embed/reference_badge.svg?style=flat
[link-packagist]: https://packagist.org/packages/embed/embed
[link-travis]: https://travis-ci.org/oscarotero/Embed
[link-scrutinizer]: https://scrutinizer-ci.com/g/oscarotero/Embed/
[link-sensiolabs]: https://insight.sensiolabs.com/projects/f0beab9f-fe41-47db-8806-373f80c50f9e
[link-references]: https://www.versioneye.com/php/embed:embed/references

51
vendor/embed/embed/composer.json vendored Normal file
View file

@ -0,0 +1,51 @@
{
"name": "embed/embed",
"type": "library",
"description": "PHP library to retrieve page info using oembed, opengraph, etc",
"keywords": [
"oembed",
"opengraph",
"twitter cards",
"embed",
"embedly"
],
"homepage": "https://github.com/oscarotero/Embed",
"license": "MIT",
"authors": [
{
"name": "Oscar Otero",
"email": "oom@oscarotero.com",
"homepage": "http://oscarotero.com",
"role": "Developer"
}
],
"support": {
"email": "oom@oscarotero.com",
"issues": "https://github.com/oscarotero/Embed/issues"
},
"require": {
"php": "^5.5|^7.0",
"ext-curl": "*",
"ext-mbstring": "*",
"composer/ca-bundle": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8|^5.7",
"friendsofphp/php-cs-fixer": "^2.0"
},
"autoload": {
"psr-4": {
"Embed\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Embed\\Tests\\": "tests/"
}
},
"scripts": {
"demo": "php -S localhost:8888 demo/index.php",
"test": "phpunit",
"cs-fix": "php-cs-fixer fix ."
}
}

22
vendor/embed/embed/phpunit.xml.dist vendored Normal file
View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
verbose="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="All tests">
<directory>tests</directory>
</testsuite>
</testsuites>
<groups>
<exclude>
<group>ignore</group>
</exclude>
</groups>
</phpunit>

View file

@ -0,0 +1,687 @@
<?php
namespace Embed\Adapters;
use Embed\Bag;
use Embed\DataInterface;
use Embed\Http\DispatcherInterface;
use Embed\Http\ImageResponse;
use Embed\Http\Response;
use Embed\Http\Url;
use Embed\Providers\Provider;
use Embed\Utils;
/**
* Base class extended by all adapters.
*
* @property null|string $title
* @property null|string $description
* @property null|string $url
* @property null|string $type
* @property array $tags
* @property array $feeds
* @property array $images
* @property array $imagesUrls
* @property null|string $image
* @property null|int $imageWidth
* @property null|int $imageHeight
* @property null|string $code
* @property null|int $width
* @property null|int $height
* @property null|float $aspectRatio
* @property null|string $authorName
* @property null|string $authorUrl
* @property array $providerIcons
* @property array $providerIconsUrls
* @property null|string $providerIcon
* @property null|string $providerName
* @property null|string $providerUrl
* @property null|string $publishedTime
*/
abstract class Adapter implements DataInterface
{
protected $config;
protected $dispatcher;
protected $response;
protected $providers = [];
/**
* Checks whether the response is valid to this Adapter.
*
* @param Response $response
*
* @return bool
*/
public static function check(Response $response)
{
return $response->isValid();
}
/**
* Constructor.
*
* @param Response $response
* @param array $config
* @param DispatcherInterface $dispatcher
*/
public function __construct(Response $response, array $config, DispatcherInterface $dispatcher)
{
$this->response = $response;
$this->config = new Bag($config);
$this->dispatcher = $dispatcher;
$this->init();
}
/**
* Initialize the providers
*/
abstract protected function init();
/**
* Magic method to execute methods to return paramaters
* For example, $source->sourceUrl executes $source->getSourceUrl().
*
* @param string $name The property name
*
* @return mixed
*/
public function __get($name)
{
$method = 'get'.$name;
if (method_exists($this, $method)) {
return $this->$name = $this->$method();
}
return null;
}
/**
* Magic method to execute methods to check if parameters are empty
*
* @param string $name The property name
*
* @return bool
*/
public function __isset($name)
{
$value = $this->$name;
return !empty($value);
}
/**
* Returns the dispatcher used.
*
* @return DispatcherInterface
*/
public function getDispatcher()
{
return $this->dispatcher;
}
/**
* Returns the main response instance.
*
* @return Response
*/
public function getResponse()
{
return $this->response;
}
/**
* Get a config value.
*
* @param string $name
* @param mixed $default
*
* @return string|null
*/
public function getConfig($name, $default = null)
{
$value = $this->config->get($name);
return $value === null ? $default : $value;
}
/**
* Get all providers.
*
* @return Provider[]
*/
public function getProviders()
{
return $this->providers;
}
/**
* {@inheritdoc}
*/
public function getTitle()
{
$default = $this->url;
return $this->getFirstFromProviders(function (Provider $provider) {
return $provider->getTitle();
}, $default);
}
/**
* {@inheritdoc}
*/
public function getDescription()
{
return $this->getFirstFromProviders(function (Provider $provider) {
return $provider->getDescription();
});
}
/**
* {@inheritdoc}
*/
public function getType()
{
$code = $this->code;
if (empty($code)) {
return 'link';
}
if (strpos($code, '</video>')) {
return 'video';
}
//Get the most repeated type
$types = [];
foreach ($this->providers as $provider) {
$type = $provider->getType();
if ($type !== null) {
if (!isset($types[$type])) {
$types[$type] = 0;
}
++$types[$type];
}
}
//If it has code, it's not a link
unset($types['link']);
return static::getBigger($types) ?: 'rich';
}
/**
* {@inheritdoc}
*/
public function getTags()
{
return $this->getAllFromProviders(function (Provider $provider) {
return $provider->getTags();
});
}
/**
* {@inheritdoc}
*/
public function getFeeds()
{
return $this->getAllFromProviders(function (Provider $provider) {
return $provider->getFeeds();
});
}
/**
* {@inheritdoc}
*/
public function getCode()
{
//Width and height deppends to the code
$this->width = null;
$this->height = null;
$codes = [];
foreach ($this->providers as $provider) {
$code = $provider->getCode();
if (!empty($code)) {
$codes[] = [
'code' => $code,
'width' => $provider->getWidth(),
'height' => $provider->getHeight(),
];
}
}
if (empty($codes)) {
return null;
}
//Use only html5 codes
$html5 = array_filter($codes, function (array $code) {
return strpos($code['code'], '</object>') === false && strpos($code['code'], '</embed>') === false;
});
//If it's not html5 codes, returns flash
if (empty($html5)) {
$code = current($codes);
} else {
$code = current($html5);
}
$this->width = is_numeric($code['width']) ? (int) $code['width'] : $code['width'];
$this->height = is_numeric($code['height']) ? (int) $code['height'] : $code['height'];
return $code['code'];
}
/**
* Returns the code as DOMNodeList elements
*
* @return \DOMNodeList|null
*/
public function getHtml()
{
$code = $this->code;
if (empty($code)) {
return;
}
$errors = libxml_use_internal_errors(true);
$entities = libxml_disable_entity_loader(true);
$dom = new \DOMDocument();
$dom->loadHTML($code);
libxml_use_internal_errors($errors);
libxml_disable_entity_loader($entities);
return Utils::xpathQuery($dom, 'descendant-or-self::body/*', false);
}
/**
* {@inheritdoc}
*/
public function getUrl()
{
$default = (string) $this->getResponse()->getUrl();
$blacklist = $this->getConfig('url_blacklist');
//some sites returns the homepage as canonical
$homeUrl = $this->getResponse()->getUrl()->getAbsolute('/');
return $this->getFirstFromProviders(function (Provider $provider) use ($blacklist, $homeUrl) {
$url = $provider->getUrl();
if ($homeUrl === $url || (!empty($blacklist) && Url::create($url)->match($blacklist))) {
return false;
}
return $url;
}, $default);
}
/**
* {@inheritdoc}
*/
public function getAuthorName()
{
return $this->getFirstFromProviders(function (Provider $provider) {
return $provider->getAuthorName();
});
}
/**
* {@inheritdoc}
*/
public function getAuthorUrl()
{
return $this->getFirstFromProviders(function (Provider $provider) {
return $provider->getAuthorUrl();
});
}
/**
* {@inheritdoc}
*/
public function getProviderIconsUrls()
{
$urls = [
$this->getResponse()->getUrl()->getAbsolute('/favicon.ico'),
$this->getResponse()->getUrl()->getAbsolute('/favicon.png'),
];
foreach ($this->providers as $provider) {
foreach ($provider->getProviderIconsUrls() as $url) {
if (!in_array($url, $urls, true)) {
$urls[] = $url;
}
}
}
return $urls;
}
/**
* Gets all icon provider urls found
* It returns also the width, height and mime-type.
*
* @return array
*/
public function getProviderIcons()
{
return $this->dispatchImagesInfo($this->providerIconsUrls);
}
/**
* Gets the best icon provider
*
* @return string|null
*/
public function getProviderIcon()
{
$icons = $this->providerIcons;
if (empty($icons)) {
return null;
}
$sizes = [];
foreach ($icons as $icon) {
$sizes[$icon['url']] = $icon['size'];
}
return static::getBigger($sizes);
}
/**
* {@inheritdoc}
*/
public function getProviderName()
{
$default = $this->getResponse()->getUrl()->getDomain();
return $this->getFirstFromProviders(function (Provider $provider) {
return $provider->getProviderName();
}, $default);
}
/**
* {@inheritdoc}
*/
public function getProviderUrl()
{
$url = $this->getResponse()->getUrl();
$default = $url->getScheme().'://'.$url->getDomain(true);
return $this->getFirstFromProviders(function (Provider $provider) {
return $provider->getProviderUrl();
}, $default);
}
/**
* {@inheritdoc}
*/
public function getImagesUrls()
{
$urls = $this->getAllFromProviders(function (Provider $provider) {
return $provider->getImagesUrls();
});
$blacklist = $this->getConfig('images_blacklist');
if (!empty($blacklist)) {
$urls = array_filter($urls, function ($url) use ($blacklist) {
return !Url::create($url)->match($blacklist);
});
}
return array_values($urls);
}
/**
* Gets all images found in the webpage
* It returns also the width, height and mime-type.
*
* @return array
*/
public function getImages()
{
return $this->dispatchImagesInfo($this->imagesUrls);
}
/**
* Gets the best image
*
* @return string|null
*/
public function getImage()
{
$this->imageWidth = null;
$this->imageHeight = null;
$images = $this->images;
$bigger = (bool) $this->getConfig('choose_bigger_image');
$minWidth = $this->getConfig('min_image_width', 1);
$minHeight = $this->getConfig('min_image_height', 1);
$images = array_filter($images, function ($image) use ($minWidth, $minHeight) {
return $image['width'] >= $minWidth && $image['height'] >= $minHeight;
});
if (empty($images)) {
return null;
}
reset($images);
$image = current($images);
if ($bigger) {
$sizes = [];
foreach ($images as $img) {
$sizes[$img['url']] = $img['size'];
}
$biggest = static::getBigger($sizes);
foreach ($images as $img) {
if ($biggest == $img['url']) {
$image = $img;
break;
}
}
}
$this->imageWidth = $image['width'];
$this->imageHeight = $image['height'];
return $image['url'];
}
/**
* Gets the image width.
*
* @return int|null
*/
public function getImageWidth()
{
$this->image = $this->getImage();
return $this->imageWidth;
}
/**
* Gets the image height.
*
* @return int|null
*/
public function getImageHeight()
{
$this->image = $this->getImage();
return $this->imageHeight;
}
/**
* {@inheritdoc}
*/
public function getWidth()
{
$this->code = $this->getCode();
return $this->width;
}
/**
* {@inheritdoc}
*/
public function getHeight()
{
$this->code = $this->getCode();
return $this->height;
}
/**
* Gets the aspect ratio of the embedded widget
* (useful to make it responsive).
*
* @return float|null
*/
public function getAspectRatio()
{
if ($this->width !== null
&& (strpos($this->width, '%') === false)
&& $this->height !== null && (strpos($this->height, '%') === false)) {
return round(($this->height / $this->width) * 100, 3);
}
return null;
}
/**
* {@inheritdoc}
*/
public function getPublishedTime()
{
return $this->getFirstFromProviders(function (Provider $provider) {
return $provider->getPublishedTime();
});
}
/**
* {@inheritdoc}
*/
public function getLicense()
{
return $this->getFirstFromProviders(function (Provider $provider) {
return $provider->getLicense();
});
}
/**
* {@inheritdoc}
*/
public function getLinkedData()
{
return $this->getAllFromProviders(function (Provider $provider) {
return $provider->getLinkedData();
});
}
/**
* Get images info.
*
* @param array $urls
*
* @return array
*/
private function dispatchImagesInfo($urls)
{
if (empty($urls)) {
return [];
}
$requests = [];
foreach ($urls as $url) {
$requests[] = Url::create($url);
}
return array_map(
function (ImageResponse $response) {
return [
'url' => (string) $response->getUrl(),
'width' => $response->getWidth(),
'height' => $response->getHeight(),
'size' => $response->getWidth() * $response->getHeight(),
'mime' => $response->getContentType(),
];
},
$this->getDispatcher()->dispatchImages($requests)
);
}
/**
* Returns the key of the bigger value in an array
*
* @param array $values
*
* @return string|null
*/
protected static function getBigger(array $values)
{
$bigger = null;
foreach ($values as $value => $repeat) {
if ($bigger === null || $repeat > $values[$bigger]) {
$bigger = $value;
}
}
return $bigger;
}
/**
* Returns the first value of the providers
*
* @param callable $callable
* @param string|null $default
*
* @return string|null
*/
protected function getFirstFromProviders(callable $callable, $default = null)
{
$values = array_filter(array_map($callable, $this->providers));
return empty($values) ? $default : current($values);
}
/**
* Returns the all values from the providers
*
* @param callable $callable
*
* @return array
*/
protected function getAllFromProviders(callable $callable)
{
$values = array_filter(array_map($callable, $this->providers));
$all = [];
foreach ($values as $value) {
foreach ($value as $v) {
if (!in_array($v, $all, true)) {
$all[] = $v;
}
}
}
return $all;
}
}

View file

@ -0,0 +1,57 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
use Embed\Utils;
use Embed\Providers\Api;
/**
* Adapter to provide information from archive.org API.
*/
class Archive extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'archive.org/details/*',
]);
}
/**
* {@inheritdoc}
*/
protected function init()
{
parent::init();
$this->providers = ['archive' => new Api\Archive($this)] + $this->providers;
}
/**
* {@inheritdoc}
*/
public function getCode()
{
return Utils::iframe(str_replace('/details/', '/embed/', $this->url), $this->width, $this->height);
}
/**
* {@inheritdoc}
*/
public function getWidth()
{
return 640;
}
/**
* {@inheritdoc}
*/
public function getHeight()
{
return 480;
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace Embed\Adapters;
use Embed\Utils;
use Embed\Http\Response;
/**
* Adapter to get the embed code from play.cadenaser.com.
*/
class Cadenaser extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'play.cadenaser.com/audio/*',
]);
}
/**
* {@inheritdoc}
*/
public function getCode()
{
$url = $this->getResponse()->getUrl();
return Utils::iframe($url->withPath('/widget/'.$url->getPath()), $this->width, $this->height);
}
/**
* {@inheritdoc}
*/
public function getWidth()
{
return 620;
}
/**
* {@inheritdoc}
*/
public function getHeight()
{
return 100;
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
use Embed\Utils;
/**
* Adapter to get the embed code from cartodb.
*/
class Carto extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'*.carto.com/viz/*/public_map',
]);
}
/**
* {@inheritdoc}
*/
public function getCode()
{
$this->width = null;
$this->height = 520;
$url = $this->getResponse()->getUrl()->withDirectoryPosition(2, 'embed_map');
return Utils::iframe($url, '100%', $this->height);
}
}

41
vendor/embed/embed/src/Adapters/Cnn.php vendored Normal file
View file

@ -0,0 +1,41 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
use Embed\Http\Url;
use Embed\Utils;
/**
* Adapter provider more information from cnn.com.
*/
class Cnn extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'*.cnn.com/videos/*',
]);
}
/**
* {@inheritdoc}
*/
public function getCode()
{
$url = Url::create('https://fave.api.cnn.io/v1/fav/')
->withQueryParameters([
'video' => implode('/', $this->getResponse()->getUrl()->getSlicePath(1)),
'customer' => 'cnn',
'env' => 'prod'
]);
$this->width = 416;
$this->height = 234;
return Utils::iframe($url, $this->width, $this->height);
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
use Embed\Utils;
use Embed\Providers\Api;
/**
* Adapter provider more information from some facebook pages.
*/
class Facebook extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'www.facebook.com/*',
]);
}
/**
* {@inheritdoc}
*/
protected function init()
{
parent::init();
$this->providers = ['facebook' => new Api\Facebook($this)] + $this->providers;
}
}

View file

@ -0,0 +1,98 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
use Embed\Utils;
use Embed\Providers;
/**
* Adapter to provide information from raw files.
*/
class File extends Adapter
{
private static $contentTypes = [
'video/ogg' => ['video', 'videoHtml'],
'application/ogg' => ['video', 'videoHtml'],
'video/ogv' => ['video', 'videoHtml'],
'video/webm' => ['video', 'videoHtml'],
'video/mp4' => ['video', 'videoHtml'],
'audio/ogg' => ['audio', 'audioHtml'],
'audio/mp3' => ['audio', 'audioHtml'],
'audio/mpeg' => ['audio', 'audioHtml'],
'audio/webm' => ['audio', 'audioHtml'],
'image/jpeg' => ['photo', 'imageHtml'],
'image/jpg' => ['photo', 'imageHtml'],
'image/gif' => ['photo', 'imageHtml'],
'image/png' => ['photo', 'imageHtml'],
'image/bmp' => ['photo', 'imageHtml'],
'image/ico' => ['photo', 'imageHtml'],
'text/rtf' => ['rich', 'google'],
'application/pdf' => ['rich', 'google'],
'application/msword' => ['rich', 'google'],
'application/vnd.ms-powerpoint' => ['rich', 'google'],
'application/vnd.ms-excel' => ['rich', 'google'],
'application/zip' => ['rich', 'google'],
'application/postscript' => ['rich', 'google'],
'application/octet-stream' => ['rich', 'google'],
];
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && isset(self::$contentTypes[$response->getContentType()]);
}
/**
* {@inheritdoc}
*/
protected function init()
{
$this->providers = [
'oembed' => new Providers\OEmbed($this),
];
}
/**
* {@inheritdoc}
*/
public function getType()
{
return self::$contentTypes[$this->getResponse()->getContentType()][0];
}
/**
* {@inheritdoc}
*/
public function getCode()
{
if (($code = parent::getcode())) {
return $code;
}
switch (self::$contentTypes[$this->getResponse()->getContentType()][1]) {
case 'videoHtml':
return Utils::videoHtml($this->image, $this->url, $this->imageWidth, $this->imageHeight);
case 'audioHtml':
return Utils::audioHtml($this->url);
case 'google':
return Utils::google($this->url);
}
}
/**
* {@inheritdoc}
*/
public function getImagesUrls()
{
if ($this->type === 'photo') {
return [$this->url];
}
return [];
}
}

View file

@ -0,0 +1,47 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
use Embed\Utils;
/**
* Adapter provider more information from flickr.
*/
class Flickr extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'www.flickr.com/photos/*',
]);
}
/**
* {@inheritdoc}
*/
public function getCode()
{
$code = parent::getCode();
if (empty($code)) {
$this->width = 640;
$this->height = 425;
$code = Utils::iframe($this->getResponse()->getUrl()->withAddedPath('player'), $this->width, $this->height);
}
return $code;
}
/**
* {@inheritdoc}
*/
public function getProviderName()
{
return 'Flickr';
}
}

View file

@ -0,0 +1,74 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
use Embed\Providers\Api;
/**
* Adapter to get the embed code from gist.github.com.
*/
class Github extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'gist.github.com/*/*',
'github.com/*',
]);
}
/**
* {@inheritdoc}
*/
protected function init()
{
parent::init();
if ($this->getResponse()->getUrl()->getHost() === 'gist.github.com') {
$this->providers = ['gist' => new Api\Gist($this)] + $this->providers;
}
}
/**
* {@inheritdoc}
*/
public function getUrl()
{
//Open graph returns as canonical url the main repo url, instead the file
return (string) $this->getResponse()->getUrl();
}
/**
* {@inheritdoc}
*/
public function getCode()
{
$this->width = null;
$this->height = null;
$url = $this->getResponse()->getUrl();
if ($url->match('github.com/*/*/blob/*')) {
$username = $url->getDirectoryPosition(0);
$repo = $url->getDirectoryPosition(1);
$ref = $url->getDirectoryPosition(3);
$path_to_file = implode('/', $url->getSlicePath(4));
switch ($url->getExtension()) {
case 'geojson':
//https://help.github.com/articles/mapping-geojson-files-on-github/#embedding-your-map-elsewhere
return "<script src=\"https://embed.githubusercontent.com/view/geojson/{$username}/{$repo}/{$ref}/{$path_to_file}\"></script>";
case 'stl':
//https://help.github.com/articles/3d-file-viewer/#embedding-your-model-elsewhere
return "<script src=\"https://embed.githubusercontent.com/view/3d/{$username}/{$repo}/{$ref}/{$path_to_file}\"></script>";
}
}
return parent::getCode();
}
}

View file

@ -0,0 +1,91 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
use Embed\Utils;
use Embed\Providers\Api;
/**
* Adapter provider more information from google maps and google drive.
*/
class Google extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'maps.google.*',
'www.google.*/maps*',
'calendar.google.com/calendar/*',
'drive.google.com/file/*/view',
'plus.google.com/*/posts/*',
]);
}
/**
* {@inheritdoc}
*/
protected function init()
{
parent::init();
if ($this->getResponse()->getUrl()->match('*/maps/*')) {
$this->providers = ['google' => new Api\GoogleMaps($this)] + $this->providers;
}
}
/**
* {@inheritdoc}
*/
public function getCode()
{
$this->width = null;
$this->height = null;
$url = $this->getResponse()->getUrl();
if ($url->getHost() === 'plus.google.com') {
return '<script src="https://apis.google.com/js/plusone.js" type="text/javascript"></script>'
.'<div class="g-post" data-href="'.$url.'"></div>';
}
if ($url->getHost() === 'calendar.google.com') {
return Utils::iframe($url);
}
if (isset($this->providers['google'])) {
return $this->providers['google']->getCode();
}
return Utils::iframe($url
->withDirectoryPosition(3, 'preview')
->withQueryParameters([]));
}
/**
* {@inheritdoc}
*/
public function getImagesUrls()
{
if ($this->getResponse()->getUrl()->getHost() === 'plus.google.com') {
return parent::getImagesUrls();
}
return [];
}
/**
* {@inheritdoc}
*/
public function getProviderName()
{
if ($this->getResponse()->getUrl()->getHost() === 'plus.google.com') {
return 'Google Plus';
}
return parent::getProviderName();
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
use Embed\Utils;
/**
* Adapter to get the embed code from howcast.com.
*/
class Howcast extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'www.howcast.com/videos/*',
]);
}
/**
* {@inheritdoc}
*/
public function getCode()
{
$this->width = null;
$this->height = null;
if (!($dom = $this->getResponse()->getHtmlContent())) {
return;
}
// #embedModal textarea
$textarea = Utils::xpathQuery($dom, "descendant-or-self::*[@id = 'embedModal']/descendant-or-self::*/textarea");
if ($textarea) {
return $textarea->nodeValue;
}
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
use Embed\Utils;
/**
* Adapter to generate embed code from ideone.com.
*/
class Ideone extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'ideone.com/*',
]);
}
/**
* {@inheritdoc}
*/
public function getCode()
{
$this->width = null;
$this->height = null;
$url = $this->getResponse()->getUrl();
$path = '/e.js'.$url->getPath();
return Utils::script($url->getAbsolute($path));
}
}

View file

@ -0,0 +1,32 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
use Embed\Providers\Api;
/**
* Adapter to provide information from imageshack.
*/
class Imageshack extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'imageshack.com/i/*',
]);
}
/**
* {@inheritdoc}
*/
protected function init()
{
parent::init();
$this->providers = ['imageshack' => new Api\Imageshack($this)] + $this->providers;
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace Embed\Adapters;
use Embed\Utils;
/**
* Adapter to fix some issues from jsfiddle.
*/
class Jsfiddle extends Webpage
{
/**
* {@inheritdoc}
*/
public function getCode()
{
$this->width = null;
$this->height = null;
$url = $this->url;
$embed_url = $url.((substr($url, -1) === '/') ? 'embedded/' : '/embedded/');
return Utils::iframe($embed_url);
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
/**
* Adapter to provide all information from lavozdegalicia.es that needs a special query parameter to generate a session cookie.
*/
class Lavozdegalicia extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'www.lavozdegalicia.es/*',
]);
}
/**
* {@inheritdoc}
*/
protected function init()
{
parent::init();
$url = $this->getResponse()->getStartingUrl();
$this->response = $this->getDispatcher()->dispatch($url->withQueryParameter('piano_d', '1'));
}
}

View file

@ -0,0 +1,49 @@
<?php
namespace Embed\Adapters;
use Embed\Utils;
use Embed\Http\Response;
/**
* Adapter to get the embed code from line.do.
*/
class Line extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'line.do/*',
]);
}
/**
* {@inheritdoc}
*/
public function getCode()
{
$url = $this->getResponse()->getUrl();
$id = $url->getDirectoryPosition(2);
return Utils::iframe($url->withPath("embed/{$id}/vertical"), $this->width, $this->height, 'border:1px solid #e7e7e7;');
}
/**
* {@inheritdoc}
*/
public function getWidth()
{
return 640;
}
/**
* {@inheritdoc}
*/
public function getHeight()
{
return 640;
}
}

View file

@ -0,0 +1,54 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
use Embed\Utils;
/**
* Adapter get embed code from 500px.com.
*/
class N500px extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'500px.com/photo/*',
]);
}
/**
* {@inheritdoc}
*/
public function getCode()
{
$url = $this->getResponse()->getUrl();
if (is_numeric($url->getDirectoryPosition(1))) {
return Utils::iframe($url->withDirectoryPosition(2, 'embed.html'), $this->width, $this->height);
}
}
/**
* {@inheritdoc}
*/
public function getWidth()
{
if (is_numeric($this->getResponse()->getUrl()->getDirectoryPosition(1))) {
return $this->imageWidth;
}
}
/**
* {@inheritdoc}
*/
public function getHeight()
{
if (is_numeric($this->getResponse()->getUrl()->getDirectoryPosition(1))) {
return $this->imageHeight;
}
}
}

View file

@ -0,0 +1,55 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
/**
* Adapter to get more info from parleys.com.
*/
class Parleys extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'www.parleys.com/play/*',
]);
}
/**
* {@inheritdoc}
*/
public function getCode()
{
$id = $this->getResponse()->getUrl()->getDirectoryPosition(1);
return '<div data-parleys-presentation="'.$id.'" style="width:'.$this->width.';height:'.$this->height.'px"><script type = "text/javascript" src="//parleys.com/js/parleys-share.js"></script></div>';
}
/**
* {@inheritdoc}
*/
public function getWidth()
{
return '100%';
}
/**
* {@inheritdoc}
*/
public function getHeight()
{
return 300;
}
/**
* {@inheritdoc}
*/
public function getType()
{
return 'video';
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
use Embed\Utils;
/**
* Adapter to generate embed code from pastebin.
*/
class Pastebin extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'pastebin.com/*',
]);
}
/**
* {@inheritdoc}
*/
public function getCode()
{
$this->width = null;
$this->height = null;
$url = $this->getResponse()->getUrl();
$path = '/embed_js' . $url->getPath();
return Utils::script($this->getResponse()->getUrl()->getAbsolute($path));
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
use Embed\Utils;
/**
* Adapter to generate embed code from pastie.org.
*/
class Pastie extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'pastie.org/pastes/*',
]);
}
/**
* {@inheritdoc}
*/
public function getCode()
{
$this->width = null;
$this->height = null;
$path = '/'.$this->getResponse()->getUrl()->getDirectoryPosition(1).'.js';
return Utils::script($this->getResponse()->getUrl()->getAbsolute($path));
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
/**
* Adapter to get embed code from pinterest.com.
*/
class Pinterest extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'www.pinterest.com/pin/*',
]);
}
/**
* {@inheritdoc}
*/
public function getCode()
{
return '<a data-pin-do="embedPin" data-pin-lang="es" href="'
.$this->getResponse()->getUrl()
.'"></a>'
.'<script async defer src="//assets.pinterest.com/js/pinit.js"></script>';
}
/**
* {@inheritdoc}
*/
public function getWidth()
{
return 236;
}
/**
* {@inheritdoc}
*/
public function getHeight()
{
return 442;
}
}

View file

@ -0,0 +1,37 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
/**
* Adapter to generate embed code from SassMeister.
*/
class Sassmeister extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'sassmeister.com/gist/*',
'www.sassmeister.com/gist/*',
]);
}
/**
* {@inheritdoc}
*/
public function getCode()
{
$this->width = null;
$this->height = 480;
$id = $this->getResponse()->getUrl()->getDirectoryPosition(1);
return "<p class=\"sassmeister\" data-gist-id=\"{$id}\" data-height=\"480\" data-theme=\"tomorrow\">".
"<a href=\"http://sassmeister.com/gist/{$id}\">Play with this gist on SassMeister.</a>".
'</p>'.
'<script src="http://cdn.sassmeister.com/js/embed.js" async></script>';
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace Embed\Adapters;
use Embed\Utils;
use Embed\Http\Response;
/**
* Adapter to get the embed code from slides.com.
*/
class Slides extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'slides.com/*',
]);
}
/**
* {@inheritdoc}
*/
public function getCode()
{
return Utils::iframe($this->getResponse()->getUrl()->withAddedPath('embed'), $this->width, $this->height);
}
/**
* {@inheritdoc}
*/
public function getWidth()
{
return 576;
}
/**
* {@inheritdoc}
*/
public function getHeight()
{
return 420;
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
/**
* Adapter to generate embed code from snipplr.com.
*/
class Snipplr extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'snipplr.com/view/*',
]);
}
/**
* {@inheritdoc}
*/
public function getCode()
{
$this->width = null;
$this->height = null;
$id = $this->getResponse()->getUrl()->getDirectoryPosition(1);
return <<<CODE
<div id="snipplr_embed_{$id}" class="snipplr_embed"><a target_"blank" href="https://snipplr.com/view/{$id}">View this snippet</a> on Snipplr</div><script type="text/javascript" src="https://snipplr.com/js/embed.js"></script><script type="text/javascript" src="https://snipplr.com/json/{$id}"></script>
CODE;
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace Embed\Adapters;
use Embed\Providers;
/**
* Adapter to provide all information from any webpage.
*/
class Webpage extends Adapter
{
/**
* {@inheritdoc}
*/
protected function init()
{
$this->providers = [
'oembed' => new Providers\OEmbed($this),
'opengraph' => new Providers\OpenGraph($this),
'linkpulse' => new Providers\Linkpulse($this),
'twittercards' => new Providers\TwitterCards($this),
'dcterms' => new Providers\Dcterms($this),
'sailthru' => new Providers\Sailthru($this),
'html' => new Providers\Html($this),
];
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
use Embed\Providers\Api;
/**
* Adapter to provide information from wikipedia.
*/
class Wikipedia extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid() && $response->getUrl()->match([
'*.wikipedia.org/wiki/*',
]);
}
/**
* {@inheritdoc}
*/
protected function init()
{
parent::init();
$this->providers = ['wikipedia' => new Api\Wikipedia($this)] + $this->providers;
}
/**
* {@inheritdoc}
*/
public function getProviderName()
{
return 'Wikipedia';
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace Embed\Adapters;
use Embed\Http\Response;
/**
* Adapter to provide information from youtube.
* Required when youtube returns a 429 status code.
*/
class Youtube extends Webpage
{
/**
* {@inheritdoc}
*/
public static function check(Response $response)
{
return $response->isValid([200, 429]) && $response->getUrl()->match([
'*.youtube.*',
]);
}
}

196
vendor/embed/embed/src/Bag.php vendored Normal file
View file

@ -0,0 +1,196 @@
<?php
namespace Embed;
/**
* Class to store and access to data.
*/
class Bag
{
private $parameters = [];
/**
* Set the initial parameters.
*
* @param array $parameters
*/
public function __construct(array $parameters = [])
{
$this->set($parameters);
}
/**
* Save a value.
*
* @param string|array $name
* @param mixed $value
*/
public function set($name, $value = null)
{
if (!is_array($name)) {
$name = [$name => $value];
}
foreach ($name as $name => $value) {
$name = self::normalizeName($name);
$value = self::normalizeValue($value);
$this->parameters[$name] = $value;
}
}
/**
* Adds a subvalue.
*
* @param string $name
* @param mixed $value
*/
public function add($name, $value = null)
{
$name = self::normalizeName($name);
$value = self::normalizeValue($value);
if (!isset($this->parameters[$name])) {
$this->parameters[$name] = [];
} elseif (!is_array($this->parameters[$name])) {
$this->parameters[$name] = (array) $this->parameters[$name];
}
$this->parameters[$name][] = $value;
}
/**
* Get a value.
*
* @param string $name
* @param bool $isHtml
*
* @return string|null
*/
public function get($name, $isHtml = false)
{
$name = self::normalizeName($name);
if (strpos($name, '[') !== false) {
$names = explode('[', str_replace(']', '', $name));
$key = array_shift($names);
$item = isset($this->parameters[$key]) ? $this->parameters[$key] : [];
foreach ($names as $key) {
if (!isset($item[$key])) {
return;
}
$item = $item[$key];
}
return $isHtml ? $item : self::toPlainValue($item);
}
if (isset($this->parameters[$name])) {
return $isHtml ? $this->parameters[$name] : self::toPlainValue($this->parameters[$name]);
}
}
/**
* Return all stored values keys.
*
* @return array
*/
public function getKeys()
{
return array_keys($this->parameters);
}
/**
* Return the raw stored values.
*
* @return array
*/
public function getAll()
{
return $this->parameters;
}
/**
* Check if a value exists and is not empty.
*
* @param string $name
*
* @return bool
*/
public function has($name)
{
$name = self::normalizeName($name);
if (strpos($name, '[') !== false) {
$names = explode('[', str_replace(']', '', $name));
$key = array_shift($names);
$item = isset($this->parameters[$key]) ? $this->parameters[$key] : [];
foreach ($names as $key) {
if (!isset($item[$key])) {
return false;
}
$item = $item[$key];
}
return !empty($item);
}
return !empty($this->parameters[$name]);
}
/**
* Normalize a variable name.
*
* @param string $name
*
* @return string
*/
private static function normalizeName($name)
{
return strtolower(trim($name));
}
/**
* Normalize a value.
* If it's a string, removes spaces and normalize some utf-8 chars.
*
* @param mixed $value
*
* @return mixed
*/
private static function normalizeValue($value)
{
if (is_string($value)) {
$value = str_ireplace(['&nbsp;', '&#160;'], ' ', $value);
$value = trim($value);
return ($value === '') ? null : $value;
}
return $value;
}
/**
* Remove the html code and entities in a value
*
* @param mixed $value
*
* @return mixed
*/
private static function toPlainValue($value)
{
if (is_string($value)) {
$value = strip_tags($value);
$value = html_entity_decode($value, ENT_QUOTES | ENT_XML1, 'UTF-8');
$value = trim($value);
return ($value === '') ? null : $value;
}
return $value;
}
}

139
vendor/embed/embed/src/DataInterface.php vendored Normal file
View file

@ -0,0 +1,139 @@
<?php
namespace Embed;
/**
* Interface used by all adapters and providers.
*/
interface DataInterface
{
/**
* Gets the title.
*
* @return string|null
*/
public function getTitle();
/**
* Gets the description.
*
* @return string|null
*/
public function getDescription();
/**
* Gets the type of the url
* The types are the same than the oEmbed types:
* video, photo, link, rich.
*
* @return string|null
*/
public function getType();
/**
* Gets the tags of the url.
*
* @return array
*/
public function getTags();
/**
* Gets the feeds urls.
*
* @return array
*/
public function getFeeds();
/**
* Gets the embed code.
*
* @return string|null
*/
public function getCode();
/**
* Gets the canonical url.
*
* @return string|null
*/
public function getUrl();
/**
* Gets the author name.
*
* @return string|null
*/
public function getAuthorName();
/**
* Gets the author url.
*
* @return string|null
*/
public function getAuthorUrl();
/**
* Gets the urls of all icons of the provider
* Note: it doesn't check whether the image exists or not.
*
* @return array
*/
public function getProviderIconsUrls();
/**
* Gets the provider name.
*
* @return string|null
*/
public function getProviderName();
/**
* Gets the provider url (usually the home url of the link).
*
* @return string|null
*/
public function getProviderUrl();
/**
* Gets the urls of all images found in the webpage
* Note: it doesn't check whether the image exists or not.
*
* @return array
*/
public function getImagesUrls();
/**
* Gets the width of the embedded widget.
*
* @return int|null
*/
public function getWidth();
/**
* Gets the height of the embedded widget.
*
* @return int|null
*/
public function getHeight();
/**
* Gets the published time, if the webpage is an article.
*
* @return string|null
*/
public function getPublishedTime();
/**
* Gets the license info.
*
* @return string|null
*/
public function getLicense();
/**
* Returns all linked data found.
*
* @return array
*/
public function getLinkedData();
}

176
vendor/embed/embed/src/Embed.php vendored Normal file
View file

@ -0,0 +1,176 @@
<?php
namespace Embed;
use Embed\Adapters\Adapter;
use Embed\Http\CurlDispatcher;
use Embed\Http\DispatcherInterface;
use Embed\Http\Url;
abstract class Embed
{
/**
* @var array
*/
public static $default_config = [
'custom_adapters_namespace' => null,
'min_image_width' => 1,
'min_image_height' => 1,
'choose_bigger_image' => false,
'images_blacklist' => [],
'url_blacklist' => [
'?&ns_campaign=*',
'?&ns_source=*',
'?&utm_campaign=*',
'?&utm_medium=*',
'?&utm_source=*',
],
'follow_canonical' => true,
'html' => [
'max_images' => -1,
'external_images' => false
],
'oembed' => [
'parameters' => [],
'embedly_key' => null,
'iframely_key' => null,
],
'google' => [
'key' => null,
],
'soundcloud' => [
'key' => null,
],
'facebook' => [
'key' => null,
'events_fields' => 'id,cover,description,end_time,name,owner,place,start_time,timezone',
'videos_fields' => 'id,description,embed_html',
]
];
/**
* Gets the info from an url.
*
* @param Url|string $url
* @param array|null $config
* @param DispatcherInterface|null $dispatcher
*
* @return Adapter
*/
public static function create($url, array $config = null, DispatcherInterface $dispatcher = null)
{
if (!($url instanceof Url)) {
$url = Url::create($url);
}
if ($config === null) {
$config = self::$default_config;
} else {
$config += self::$default_config;
}
if ($dispatcher === null) {
$dispatcher = new CurlDispatcher();
}
$info = self::process($url, $config, $dispatcher);
if ($info->getConfig('follow_canonical') === false) {
return $info;
}
// Repeat the process if:
// - The canonical url is different
// - No embed code has found
$from = preg_replace('|^(\w+://)|', '', rtrim((string)$info->getResponse()->getUrl(), '/'));
$to = preg_replace('|^(\w+://)|', '', rtrim($info->url, '/'));
if ($from !== $to && empty($info->code)) {
//accept new result if valid
try {
return self::process(Url::create($info->url), $config, $dispatcher);
} catch (\Exception $e) {
return $info;
}
}
return $info;
}
/**
* Process the url.
*
* @param Url $url
* @param array $config
* @param DispatcherInterface $dispatcher
*
* @throws Exceptions\InvalidUrlException If the urls is not valid
*
* @return Adapter
*/
private static function process(Url $url, array $config, DispatcherInterface $dispatcher)
{
$response = $dispatcher->dispatch($url);
//If is a file use File Adapter
$adapter = self::getClass('File', $config);
if ($adapter::check($response)) {
return new $adapter($response, $config, $dispatcher);
}
//Search the adapter using the domain
$adapter = self::getClass($response->getUrl()->getClassNameForDomain(), $config);
if (class_exists($adapter) && $adapter::check($response)) {
return new $adapter($response, $config, $dispatcher);
}
//Use the default webpage adapter
$adapter = self::getClass('Webpage', $config);
if ($adapter::check($response)) {
return new $adapter($response, $config, $dispatcher);
}
if ($response->getError() === null) {
$exception = new Exceptions\InvalidUrlException(sprintf("Invalid url '%s' (Status code %s)", (string) $url, $response->getStatusCode()));
} else {
$exception = new Exceptions\InvalidUrlException($response->getError());
}
$exception->setResponse($response);
throw $exception;
}
/**
* Returns a class name using the custom_adapters_namespace
*
* @param string $name
* @param array $config
*
* @return string
*/
private static function getClass($name, array $config)
{
if (!empty($config['custom_adapters_namespace'])) {
$namespaces = (array) $config['custom_adapters_namespace'];
foreach ($namespaces as $namespace) {
$class = $namespace.$name;
if (class_exists($class)) {
return $class;
}
}
}
return 'Embed\\Adapters\\'.$name;
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Embed\Exceptions;
use Exception;
class EmbedException extends Exception
{
}

View file

@ -0,0 +1,33 @@
<?php
namespace Embed\Exceptions;
use Embed\Http\Response;
class InvalidUrlException extends EmbedException
{
/**
* @var Response|null
*/
private $response;
/**
* Set the response related with this error
*
* @param Response
*/
public function setResponse(Response $response)
{
$this->response = $response;
}
/**
* Get the response related with this error
*
* @return Response|null
*/
public function getResponse()
{
return $this->response;
}
}

View file

@ -0,0 +1,126 @@
<?php
namespace Embed\Http;
/**
* Class to consume http responses.
*/
abstract class AbstractResponse
{
protected $startingUrl;
protected $url;
protected $statusCode;
protected $contentType;
protected $headers;
protected $info;
public function __construct(Url $startingUrl, Url $url, $statusCode, $contentType, array $headers, array $info)
{
$this->startingUrl = $startingUrl;
$this->url = $url;
$this->statusCode = (int)$statusCode;
$this->contentType = $contentType;
$this->headers = $headers;
$this->info = $info;
}
/**
* Get the starting url.
*
* @return Url
*/
public function getStartingUrl()
{
return $this->startingUrl;
}
/**
* Get the http code of the response, for example: 200.
*
* @return int
*/
public function getStatusCode()
{
return $this->statusCode;
}
/**
* Get the content-type of the response, for example: text/html.
*
* @return string|null
*/
public function getContentType()
{
return $this->contentType;
}
/**
* Returns the final url.
*
* @return Url
*/
public function getUrl()
{
return $this->url;
}
/**
* Returns the http headers.
*
* @return array
*/
public function getHeaders()
{
return $this->headers;
}
/**
* Returns extra http info.
*
* @return array
*/
public function getInfo()
{
return $this->info;
}
/**
* Returns dispatch error.
*
* @return string|null
*/
public function getError()
{
return empty($this->info['error']) ? null : $this->info['error'];
}
/**
* Get a header.
*
* @param string $name
*
* @return string|null
*/
public function getHeader($name)
{
$name = strtolower($name);
return isset($this->headers[$name]) ? implode(',', $this->headers[$name]) : null;
}
/**
* Check if the response is valid or not.
*
* @param array $codes
*
* @return bool
*/
public function isValid(array $codes = null)
{
if ($codes === null) {
return $this->statusCode === 200;
}
return in_array($this->statusCode, $codes);
}
}

View file

@ -0,0 +1,259 @@
<?php
namespace Embed\Http;
use Embed\Exceptions\EmbedException;
use Composer\CaBundle\CaBundle;
use stdClass;
/**
* Curl dispatcher.
*/
class CurlDispatcher implements DispatcherInterface
{
private $responses = [];
private static $acceptHeaders = [
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'gif' => 'image/gif',
'png' => 'image/png',
'mp3' => 'audio/mpeg',
'mp4' => 'video/mp4',
'ogg' => 'audio/ogg',
'ogv' => 'video/ogg',
'webm' => 'video/webm',
];
private $config = [
CURLOPT_MAXREDIRS => 10,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 10,
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_ENCODING => '',
CURLOPT_AUTOREFERER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_USERAGENT => 'Embed PHP library',
CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,
];
/**
* Constructor.
*
* @param array $config
*/
public function __construct(array $config = [])
{
$this->config = $config + $this->config;
if (!isset($this->config[CURLOPT_COOKIEJAR])) {
$cookies = str_replace('//', '/', sys_get_temp_dir().'/embed-cookies.'.uniqid());
if (is_file($cookies)) {
if (!is_writable($cookies)) {
throw new EmbedException(sprintf('The temporary cookies file "%s" is not writable', $cookies));
}
} elseif (!is_writable(dirname($cookies))) {
throw new EmbedException(sprintf('The temporary folder "%s" is not writable', dirname($cookies)));
}
$this->config[CURLOPT_COOKIEJAR] = $cookies;
$this->config[CURLOPT_COOKIEFILE] = $cookies;
}
}
/**
* Return all responses for debug purposes
*
* @return AbstractResponse[]
*/
public function getAllResponses()
{
return $this->responses;
}
/**
* Remove the cookies file on destruct the instance.
*/
public function __destruct()
{
$cookies = $this->config[CURLOPT_COOKIEJAR];
if (is_file($cookies)) {
unlink($cookies);
}
}
/**
* {@inheritdoc}
*/
public function dispatch(Url $url)
{
$options = $this->config;
$extension = $url->getExtension();
if (!empty($extension) && isset(self::$acceptHeaders[$extension])) {
$options[CURLOPT_HTTPHEADER] = ['Accept: '.self::$acceptHeaders[$extension]];
} else {
$options[CURLOPT_HTTPHEADER] = ['Accept: */*'];
}
$response = $this->exec($url, $options);
//Some sites returns 403 with the default user-agent
if ($response->getStatusCode() === 403) {
$options[CURLOPT_USERAGENT] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36';
return $this->exec($url, $options);
}
//Other sites needs a certificate
if (
class_exists('Composer\\CaBundle\\CaBundle')
&& $response->getStatusCode() === 0
&& strpos($response->getError(), 'SSL') !== false
) {
$options[CURLOPT_SSL_VERIFYHOST] = 2;
$options[CURLOPT_SSL_VERIFYPEER] = true;
$options[CURLOPT_CAINFO] = CaBundle::getSystemCaRootBundlePath();
return $this->exec($url, $options);
}
return $response;
}
/**
* Execute a curl request
*
* @param Url $url
* @param array $options
*
* @return Response
*/
protected function exec(Url $url, array $options)
{
$connection = curl_init((string) $url);
curl_setopt_array($connection, $options);
$curl = new CurlResult($connection);
//Get only text responses
$curl->onHeader(function ($name, $value, $data) {
if ($name === 'content-type') {
$data->isBinary = !preg_match('/(text|html|json)/', strtolower($value));
}
});
$curl->onBody(function ($string, stdClass $data) {
return empty($data->isBinary);
});
curl_exec($connection);
$result = $curl->getResult();
curl_close($connection);
return $this->responses[] = new Response(
$url,
Url::create($result['url']),
$result['statusCode'],
$result['contentType'],
$result['content'],
$result['headers'],
$result['info']
);
}
/**
* {@inheritdoc}
*/
public function dispatchImages(array $urls)
{
if (empty($urls)) {
return [];
}
$curl_multi = curl_multi_init();
$responses = [];
$connections = [];
foreach ($urls as $k => $url) {
if ($url->getScheme() === 'data') {
$response = ImageResponse::createFromBase64($url);
if ($response) {
$responses[$k] = $response;
}
continue;
}
$connection = curl_init((string) $url);
$options = $this->config;
$options[CURLOPT_HTTPHEADER] = ['Accept: image/*'];
curl_setopt_array($connection, $options);
curl_multi_add_handle($curl_multi, $connection);
$curl = new CurlResult($connection);
$curl->onBody(function ($body, stdClass $data) {
if (($info = @getimagesizefromstring($body))) {
$data->width = $info[0];
$data->height = $info[1];
$data->mime = $info['mime'];
return false;
}
});
$connections[$k] = $curl;
}
if (!empty($connections)) {
do {
$return = curl_multi_exec($curl_multi, $active);
} while ($return === CURLM_CALL_MULTI_PERFORM);
while ($active && $return === CURLM_OK) {
if (curl_multi_select($curl_multi) === -1) {
usleep(100);
}
do {
$return = curl_multi_exec($curl_multi, $active);
} while ($return === CURLM_CALL_MULTI_PERFORM);
}
foreach ($connections as $k => $connection) {
$resource = $connection->getResource();
curl_multi_remove_handle($curl_multi, $resource);
$result = $connection->getResult();
if (!empty($result['data']->mime)) {
$responses[$k] = $this->responses[] = new ImageResponse(
$urls[$k],
Url::create($result['url']),
$result['statusCode'],
$result['contentType'],
[$result['data']->width, $result['data']->height],
$result['headers'],
$result['info']
);
}
}
}
curl_multi_close($curl_multi);
ksort($responses, SORT_NUMERIC);
return array_values($responses);
}
}

View file

@ -0,0 +1,135 @@
<?php
namespace Embed\Http;
/**
* Class to dispatch urls using curl and get the result.
*/
class CurlResult
{
protected $resource;
protected $body;
protected $headers = [];
protected $onHeader;
protected $onBody;
protected $data;
/**
* Creates a new response.
*
* @param resource $resource The curl resource
*/
public function __construct($resource)
{
$this->resource = $resource;
$this->data = (object) [];
curl_setopt($this->resource, CURLOPT_HEADERFUNCTION, [$this, 'headerCallback']);
curl_setopt($this->resource, CURLOPT_WRITEFUNCTION, [$this, 'writeCallback']);
}
/**
* Returns the response result.
*/
public function getResult()
{
$result = curl_getinfo($this->resource);
if (curl_errno($this->resource)) {
$result['error'] = curl_error($this->resource);
}
return [
'url' => isset($result['url']) ? $result['url'] : null,
'statusCode' => isset($result['http_code']) ? $result['http_code'] : null,
'contentType' => isset($result['content_type']) ? $result['content_type'] : null,
'info' => $result,
'content' => $this->body,
'headers' => $this->headers,
'data' => $this->data,
];
}
/**
* Returns the resource.
*
* @return resource
*/
public function getResource()
{
return $this->resource;
}
/**
* Callback used on receive a header.
*
* @param callable $callback
*/
public function onHeader(callable $callback)
{
$this->onHeader = $callback;
}
/**
* Callback used on receive a body string portion.
*
* @param callable $callback
*/
public function onBody(callable $callback)
{
$this->onBody = $callback;
}
/**
* Callback used to collect the headers.
*
* @param resource $resource
* @param string $string
*
* @return int
*/
public function headerCallback($resource, $string)
{
if (!strpos($string, ':')) {
return strlen($string);
}
list($name, $value) = array_map('trim', explode(':', $string, 2));
$name = strtolower($name);
if (!isset($this->headers[$name])) {
$this->headers[$name] = [];
}
$this->headers[$name][] = $value;
if ($this->onHeader === null || call_user_func($this->onHeader, $name, $value, $this->data) !== false) {
return strlen($string);
}
return -1;
}
/**
* Callback used to get the body content.
*
* @param resource $resource
* @param string $string
*
* @return int
*/
public function writeCallback($resource, $string)
{
$this->body .= $string;
if ($this->onBody === null || call_user_func($this->onBody, $this->body, $this->data) !== false) {
return strlen($string);
}
//Cancel
$this->body = null;
return -1;
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace Embed\Http;
/**
* Interface used to create http responses.
*/
interface DispatcherInterface
{
/**
* Dispatch an url.
*
* @param Url $url
*
* @return Response
*/
public function dispatch(Url $url);
/**
* Resolve multiple image urls at once.
*
* @param Url[] $urls
*
* @return ImageResponse[]
*/
public function dispatchImages(array $urls);
}

View file

@ -0,0 +1,65 @@
<?php
namespace Embed\Http;
/**
* Class to consume http responses.
*/
class ImageResponse extends AbstractResponse
{
protected $size;
/**
* Create a ImageResponse using a bas64 url.
*
* @param Url $url
*
* @return static|null
*/
public static function createFromBase64(Url $url)
{
$pieces = explode(';', $url->getContent(), 2);
if ((count($pieces) !== 2) || (strpos($pieces[0], 'image/') === false) || (strpos($pieces[1], 'base64,') !== 0)) {
return false;
}
if (($info = @getimagesizefromstring(base64_decode(substr($pieces[1], 7)))) !== false) {
return new self(
$url,
$url,
200,
$info['mime'],
[$info[0], $info[1]],
[],
[]
);
}
}
public function __construct(Url $startingUrl, Url $url, $statusCode, $contentType, $size, array $headers, array $info)
{
parent::__construct($startingUrl, $url, $statusCode, $contentType, $headers, $info);
$this->size = $size;
}
/**
* Get the image width.
*
* @return int
*/
public function getWidth()
{
return (int) $this->size[0];
}
/**
* Get the image height.
*
* @return int
*/
public function getHeight()
{
return (int) $this->size[1];
}
}

View file

@ -0,0 +1,111 @@
<?php
namespace Embed\Http;
/**
* Class to resolve some specific redirections.
*/
abstract class Redirects
{
private static $patterns = [
'google' => 'www.google.com/url*',
'googleTranslator' => 'translate.google.com/translate',
'hashBang' => '*#!*',
'spotify' => 'play.spotify.com/*',
'tumblr' => 't.umblr.com/redirect',
];
/**
* Resolve the url redirection.
*
* @param Url $url
*
* @return Url
*/
public static function resolve(Url $url)
{
foreach (self::$patterns as $method => $pattern) {
if ($url->match($pattern)) {
return self::$method($url);
}
}
return $url;
}
/**
* Resolve a google redirection url.
*
* @param Url $url
*
* @return Url
*/
public static function google(Url $url)
{
if (($value = $url->getQueryParameter('url'))) {
return Url::create($value);
}
return $url;
}
/**
* Resolve a google translation url.
*
* @param Url $url
*
* @return Url
*/
public static function googleTranslator(Url $url)
{
if (($value = $url->getQueryParameter('u'))) {
return Url::create($value);
}
return $url;
}
/**
* Resolve an url with hashbang.
*
* @param Url $url
*
* @return Url
*/
public static function hashBang(Url $url)
{
if (($path = preg_replace('|^(/?!)|', '', $url->getFragment()))) {
return $url->withPath($url->getPath().$path);
}
return $url;
}
/**
* Redirect the player of spotify.
*
* @param Url $url
*
* @return Url
*/
public static function spotify(Url $url)
{
return $url->withHost('open.spotify.com');
}
/**
* Redirect the tumblr url
*
* @param Url $url
*
* @return Url
*/
public static function tumblr(Url $url)
{
if (($value = $url->getQueryParameter('z'))) {
return Url::create($value);
}
return $url;
}
}

160
vendor/embed/embed/src/Http/Response.php vendored Normal file
View file

@ -0,0 +1,160 @@
<?php
namespace Embed\Http;
use Embed\Utils;
use Exception;
use DOMDocument;
use SimpleXMLElement;
/**
* Class to consume http responses.
*/
class Response extends AbstractResponse
{
protected $content;
protected $xmlContent;
protected $jsonContent;
protected $htmlContent;
/**
* Response constructor.
*
* @param Url $startingUrl
* @param Url $url
* @param int $statusCode
* @param string|null $contentType
* @param string $content
* @param array $headers
* @param array $info
*/
public function __construct(Url $startingUrl, Url $url, $statusCode, $contentType, $content, array $headers, array $info = [])
{
parent::__construct($startingUrl, $url, $statusCode, $contentType, $headers, $info);
$this->setContent($content);
}
/**
* Get the content.
*
* @return string|null
*/
public function getContent()
{
return $this->content;
}
/**
* Get the content as HTML.
*
* @return DOMDocument|false
*/
public function getHtmlContent()
{
if ($this->htmlContent === null) {
try {
if (empty($content = $this->content)) {
return $this->htmlContent = false;
}
$errors = libxml_use_internal_errors(true);
$entities = libxml_disable_entity_loader(true);
$this->htmlContent = new DOMDocument();
if (mb_detect_encoding($content, 'UTF-8', true) === 'UTF-8') {
$content = mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8');
$content = preg_replace(
'/<head[^>]*>/',
'<head><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">',
$content
);
} elseif (mb_detect_encoding($content, 'SJIS', true) === 'SJIS') {
$content = mb_convert_encoding($content, 'HTML-ENTITIES', 'SJIS');
$content = preg_replace('/<head[^>]*>/', '<head><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=shift_jis">', $content);
} elseif (mb_detect_encoding($content, 'ISO-8859-1', true) === 'ISO-8859-1') {
$content = mb_convert_encoding($content, 'HTML-ENTITIES', 'ISO-8859-1');
$content = preg_replace('/<head[^>]*>/', '<head><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">', $content);
}
$this->htmlContent->loadHTML(trim($content));
libxml_use_internal_errors($errors);
libxml_disable_entity_loader($entities);
} catch (Exception $exception) {
return $this->htmlContent = false;
}
}
return $this->htmlContent;
}
/**
* Get the content as an array from JSON.
*
* @return array|false
*/
public function getJsonContent()
{
if ($this->jsonContent === null) {
try {
if (($content = $this->content) === '') {
return $this->jsonContent = false;
}
$this->jsonContent = json_decode($content, true);
} catch (\Exception $exception) {
return $this->jsonContent = false;
}
}
return $this->jsonContent;
}
/**
* Get the content as XML.
*
* @return SimpleXMLElement|false
*/
public function getXMLContent()
{
if ($this->xmlContent === null) {
try {
if (($content = $this->content) === '') {
return $this->xmlContent = false;
}
$errors = libxml_use_internal_errors(true);
$this->xmlContent = new SimpleXMLElement($content);
libxml_use_internal_errors($errors);
} catch (Exception $exception) {
return $this->xmlContent = false;
}
}
return $this->xmlContent;
}
/**
* Set the response content.
*
* @param string $content
*/
private function setContent($content)
{
$this->content = $content;
if (empty($this->contentType)) {
return;
}
//Remove charset from Content-Type
if (strpos($this->contentType, ';') !== false) {
list($mime, $charset) = array_map('trim', explode(';', $this->contentType));
$this->contentType = $mime;
$charset = str_replace(['"', "'"], '', substr(strstr($charset, '='), 1));
$this->content = Utils::toUtf8($content, trim($charset));
}
}
}

679
vendor/embed/embed/src/Http/Url.php vendored Normal file
View file

@ -0,0 +1,679 @@
<?php
namespace Embed\Http;
/**
* Class to split and manipulate urls.
*/
class Url
{
private $info;
private $url;
private static $public_suffix_list;
/**
* Create a new Url instance.
*
* @param string $url
*
* @return Url
*/
public static function create($url)
{
return Redirects::resolve(new static($url));
}
/**
* Constructor. Sets the url.
*
* @param string $url
*/
private function __construct($url)
{
$this->parseUrl($url);
}
/**
* Returns the url.
*
* @return string
*/
public function __toString()
{
if ($this->url === null) {
return $this->url = $this->buildUrl();
}
return $this->url;
}
/**
* Remove the built url on clone.
*/
public function __clone()
{
$this->url = null;
}
/**
* Check if the url match with a specific pattern. The patterns only accepts * and ?
*
* @param string|array $patterns The pattern or an array with various patterns
*
* @return bool
*/
public function match($patterns)
{
if (!is_array($patterns)) {
$patterns = [$patterns];
}
//Remove scheme and query
$long_url = preg_replace('|^(\w+://)|', '', (string) $this);
$short_url = preg_replace('|(\?.*)?$|', '', $long_url);
foreach ($patterns as $pattern) {
if ($pattern[0] === '?') { // ?hello=world => *?hello=world
$pattern = '*' . $pattern;
}
$pattern = str_replace('\\*', '.*', preg_quote($pattern, '|'));
if (strpos($pattern, '?') === false) {
$url = $short_url;
} else {
$pattern = str_replace('\\?&', '[\\?&]', $pattern);
$url = $long_url;
}
if (preg_match('|^'.$pattern.'$|i', $url)) {
return true;
}
}
return false;
}
/**
* Return the content of the url (for embedded images).
*
* @return string The content or null
*/
public function getContent()
{
return isset($this->info['content']) ? $this->info['content'] : null;
}
/**
* Return the extension of the url (html, php, jpg, etc).
*
* @return string The scheme or null
*/
public function getExtension()
{
return isset($this->info['extension']) ? $this->info['extension'] : null;
}
/**
* Returns a new instance with other relative url.
*
* @param string $url
*
* @return Url
*/
public function createAbsolute($url)
{
return self::create($this->getAbsolute($url));
}
/**
* Returns a clone with other extension.
*
* @param string $extension
*
* @return Url
*/
public function withExtension($extension)
{
$clone = clone $this;
$clone->info['extension'] = $extension;
if (empty($clone->info['file'])) {
$clone->info['file'] = array_pop($clone->info['path']);
}
return $clone;
}
/**
* Return the scheme of the url (for example http, https, ftp, etc).
*
* @return string The scheme or null
*/
public function getScheme()
{
return isset($this->info['scheme']) ? $this->info['scheme'] : null;
}
/**
* Returns a clone with other scheme.
*
* @param string $scheme
*
* @return Url
*/
public function withScheme($scheme)
{
$clone = clone $this;
$clone->info['scheme'] = $scheme;
return $clone;
}
/**
* Return the host of the url (for example: google.com).
*
* @return string The host or null
*/
public function getHost()
{
return isset($this->info['host']) ? $this->info['host'] : null;
}
/**
* Returns a clone with other host.
*
* @param string $host
*
* @return Url
*/
public function withHost($host)
{
$clone = clone $this;
$clone->info['host'] = $host;
return $clone;
}
/**
* Return the domain of the url (for example: google).
*
* @param bool $first_level True to return the first level domain (.com, .es, etc)
*
* @return string
*/
public function getDomain($first_level = false)
{
$host = $this->getHost();
if (empty($host)) {
return '';
}
$host = array_reverse(explode('.', $host));
switch (count($host)) {
case 1:
return $host[0];
case 2:
return $first_level ? ($host[1].'.'.$host[0]) : $host[1];
default:
$tld = $host[1].'.'.$host[0];
$suffixes = self::getSuffixes();
if (in_array($tld, $suffixes, true)) {
return $first_level ? $host[2].'.'.$tld : $host[2];
}
return $first_level ? $host[1].'.'.$host[0] : $host[1];
}
}
/**
* Return a specific directory position in the path of the url.
*
* @param int $position The position of the directory (0 based index)
*
* @return string|null
*/
public function getDirectoryPosition($position)
{
if ($position === count($this->info['path'])) {
return $this->info['file'];
}
return isset($this->info['path'][$position]) ? $this->info['path'][$position] : null;
}
/**
* Returns a clone with other directory in a specific position.
*
* @param int|null $key The position of the subdirectory (0 based index)
* @param string $value The new value
*
* @return Url
*/
public function withDirectoryPosition($key, $value)
{
$clone = clone $this;
$pos = $clone->info['path'];
$hasFile = isset($clone->info['file']);
if ($hasFile) {
$pos[] = $clone->info['file'];
}
$pos[$key] = $value;
if ($hasFile) {
$clone->info['file'] = array_pop($pos);
}
$clone->info['path'] = $pos;
return $clone;
}
/**
* Return all directories.
*
* @return string
*/
public function getDirectories()
{
return !empty($this->info['path']) ? '/'.implode('/', $this->info['path']).'/' : '/';
}
/**
* Slice path.
*
* @param int $offset
* @param int|null $length
*
* @return array
*/
public function getSlicePath($offset, $length = null)
{
$array = explode('/', $this->getPath());
if (empty($array[0])) {
array_shift($array);
}
return array_slice($array, $offset, $length);
}
/**
* Return the url path.
*
* @return string
*/
public function getPath()
{
$path = !empty($this->info['path']) ? '/'.implode('/', array_map('self::urlEncode', $this->info['path'])).'/' : '/';
if (isset($this->info['file'])) {
$path .= self::urlEncode($this->info['file']);
if (isset($this->info['extension'])) {
$path .= '.'.$this->info['extension'];
}
}
return $path;
}
/**
* Returns a clone with other path.
*
* @param string $path
*
* @return Url
*/
public function withPath($path)
{
$clone = clone $this;
$clone->setPath($path);
return $clone;
}
/**
* Returns a clone with path appended.
*
* @param string $path
*
* @return Url
*/
public function withAddedPath($path)
{
$path = $this->getPath().'/'.$path;
return $this->withPath($path);
}
/**
* Check if the url has a query parameter.
*
* @param string $name
*
* @return bool
*/
public function hasQueryParameter($name)
{
return isset($this->info['query'][$name]);
}
/**
* Returns all query parameters.
*
* @return array
*/
public function getQueryParameters()
{
return $this->info['query'];
}
/**
* Returns a query parameter value.
*
* @param string $name
*
* @return string|null
*/
public function getQueryParameter($name)
{
return isset($this->info['query'][$name]) ? $this->info['query'][$name] : null;
}
/**
* Returns a clone with a new query parameter.
*
* @param string $name The parameter name
* @param string $value The parameter value
*
* @return Url
*/
public function withQueryParameter($name, $value)
{
$clone = clone $this;
$clone->info['query'][$name] = $value;
return $clone;
}
/**
* Returns a clone with new query parameters merged.
*
* @param array $parameters
*
* @return Url
*/
public function withAddedQueryParameters(array $parameters)
{
$clone = clone $this;
$clone->info['query'] = empty($clone->info['query']) ? $parameters : array_replace($clone->info['query'], $parameters);
return $clone;
}
/**
* Returns a clone with new query parameters.
*
* @param array $parameters
*
* @return Url
*/
public function withQueryParameters(array $parameters)
{
$clone = clone $this;
$clone->info['query'] = $parameters;
return $clone;
}
/**
* Return the url fragment.
*
* @return string
*/
public function getFragment()
{
return isset($this->info['fragment']) ? $this->info['fragment'] : null;
}
/**
* Return ClassName for domain.
*
* Domains started with numbers will get N prepended to their class name.
*
* @return string
*/
public function getClassNameForDomain()
{
$className = str_replace(array('-', ' '), '', ucwords(strtolower($this->getDomain())));
if (is_numeric(mb_substr($className, 0, 1))) {
$className = 'N'.$className;
}
return $className;
}
/**
* Build the url using the splitted data.
*/
protected function buildUrl()
{
$url = '';
if (isset($this->info['content'])) {
return 'data:'.$this->info['content'];
}
if (isset($this->info['scheme'])) {
$url .= $this->info['scheme'].'://';
}
$user = isset($this->info['user']) ? $this->info['user'] : '';
$pass = isset($this->info['pass']) ? ':'.$this->info['pass'] : '';
if ($user || $pass) {
$url .= $user.$pass.'@';
}
if (isset($this->info['host'])) {
$url .= $this->info['host'];
}
if (isset($this->info['port'])) {
$url .= ':'.$this->info['port'];
}
$url .= $this->getPath();
if (!empty($this->info['query'])) {
$url .= '?'.rtrim(http_build_query($this->info['query']), '=');
}
if (isset($this->info['fragment'])) {
$url .= '#'.$this->info['fragment'];
}
return $url;
}
/**
* Parse an url and split into different pieces.
*
* @param string $url The url to parse
*/
protected function parseUrl($url)
{
$this->info = self::parse($url);
if (isset($this->info['path'])) {
$this->setPath($this->info['path']);
}
if (empty($this->info['query'])) {
$this->info['query'] = [];
return;
}
// Fix dots and other characters used in query's variables names
// https://github.com/oscarotero/Embed/issues/101
$queryString = preg_replace_callback('/(^|(?<=&))[^=[&]+/', function ($key) {
return bin2hex(urldecode($key[0]));
}, $this->info['query']);
parse_str($queryString, $query);
$this->info['query'] = [];
foreach ((array) $query as $key => $value) {
$this->info['query'][hex2bin($key)] = $value;
}
array_walk_recursive($this->info['query'], function (&$value) {
$value = urldecode($value);
});
}
/**
* Return an absolute url based in a relative.
*
* @return string
*/
public function getAbsolute($url)
{
$url = trim($url);
if (empty($url)) {
return '';
}
if (preg_match('|^\w+:[^/]|', $url)) {
return $url;
}
if (preg_match('|^\w+://|', $url)) {
return self::validUrlOrEmpty($url);
}
if (strpos($url, '://') === 0) {
return self::validUrlOrEmpty($this->getScheme().$url);
}
if (strpos($url, '//') === 0) {
return self::validUrlOrEmpty($this->getScheme().":$url");
}
if ($url[0] === '/') {
return self::validUrlOrEmpty($this->getScheme().'://'.$this->getHost().$url);
}
return self::validUrlOrEmpty($this->getScheme().'://'.$this->getHost().$this->getDirectories().$url);
}
/**
* Parses and adds path and file value.
*
* @param string $path
*/
private function setPath($path)
{
$this->info['path'] = $this->info['file'] = $this->info['extension'] = $this->info['content'] = null;
if ($this->getScheme() === 'data') {
$this->info['content'] = $path;
return;
}
$file = substr(strrchr($path, '/'), 1);
if (strlen($file)) {
$path = substr($path, 0, -strlen($file));
if (preg_match('/(.*)\.([\w]+)$/', $file, $match)) {
$this->info['file'] = urldecode($match[1]);
$this->info['extension'] = $match[2];
} else {
$this->info['file'] = urldecode($file);
}
}
$this->info['path'] = [];
foreach (explode('/', $path) as $dir) {
$dir = urldecode($dir);
if ($dir !== '') {
$this->info['path'][] = $dir;
}
}
}
private static function getSuffixes()
{
if (self::$public_suffix_list === null) {
self::$public_suffix_list = include __DIR__.'/../resources/public_suffix_list.php';
}
return self::$public_suffix_list;
}
/**
* UTF-8 compatible parse_url
* http://php.net/manual/en/function.parse-url.php#114817
*
* @param string $url
*
* @return string
*/
private static function parse($url)
{
$enc_url = preg_replace_callback(
'%[^:/@?&=#]+%usD',
function ($matches) {
return urlencode($matches[0]);
},
$url
);
$parts = parse_url($enc_url);
if ($parts === false) {
throw new \InvalidArgumentException('Malformed URL: ' . $url);
}
if (!empty($parts['scheme']) && !in_array($parts['scheme'], ['http', 'https', 'data'])) {
throw new \InvalidArgumentException(sprintf('Invalid URL scheme: "%s"', $parts['scheme']));
}
foreach ($parts as $name => $value) {
$parts[$name] = urldecode($value);
}
return $parts;
}
private static function urlEncode($path)
{
// : - used for files
// @ and , - used for GoogleMaps adapter url (in view and streetview modes)
return str_replace(['%3A','%40','%2C'], [':','@',','], urlencode($path));
}
private static function validUrlOrEmpty($url)
{
return parse_url($url) === false ? '' : $url;
}
}

View file

@ -0,0 +1,98 @@
<?php
namespace Embed\Providers\Api;
use Embed\Adapters\Adapter;
use Embed\Providers\Provider;
/**
* Provider to use the API of archive.org.
*/
class Archive extends Provider
{
/**
* {@inheritdoc}
*/
public function __construct(Adapter $adapter)
{
parent::__construct($adapter);
$endPoint = $adapter->getResponse()->getUrl()->withQueryParameter('output', 'json');
$response = $adapter->getDispatcher()->dispatch($endPoint);
if (($json = $response->getJsonContent())) {
$this->bag->set($json);
}
}
/**
* {@inheritdoc}
*/
public function getTitle()
{
return $this->bag->get('metadata[title][0]');
}
/**
* {@inheritdoc}
*/
public function getDescription()
{
return $this->bag->get('metadata[description][0]');
}
/**
* {@inheritdoc}
*/
public function getType()
{
switch ($this->bag->get('metadata[mediatype][0]')) {
case 'movies':
return 'video';
case 'audio':
return 'audio';
case 'texts':
return 'rich';
}
}
/**
* {@inheritdoc}
*/
public function getProviderName()
{
return 'Internet Archive';
}
/**
* {@inheritdoc}
*/
public function getUrl()
{
return $this->normalizeUrl($this->bag->get('url'));
}
/**
* {@inheritdoc}
*/
public function getAuthorName()
{
$this->bag->get('metadata[creator][0]');
}
/**
* {@inheritdoc}
*/
public function getImagesUrls()
{
$images = (array) $this->bag->get('misc[image]');
foreach (array_keys((array) $this->bag->get('files')) as $url) {
$images[] = $url;
}
return $this->normalizeUrls($images);
}
}

View file

@ -0,0 +1,128 @@
<?php
namespace Embed\Providers\Api;
use Embed\Adapters\Adapter;
use Embed\Providers\Provider;
use Embed\Http\Url;
/**
* Provider to use the open graph API of facebook.
*/
class Facebook extends Provider
{
/**
* {@inheritdoc}
*/
public function __construct(Adapter $adapter)
{
parent::__construct($adapter);
if (($endPoint = $this->getEndPoint())) {
$response = $adapter->getDispatcher()->dispatch($endPoint);
if ($json = $response->getJsonContent()) {
$this->bag->set($json);
}
}
}
/**
* {@inheritdoc}
*/
public function getTitle()
{
return $this->bag->get('name');
}
/**
* {@inheritdoc}
*/
public function getDescription()
{
return $this->bag->get('description') ?: $this->bag->get('about');
}
/**
* {@inheritdoc}
*/
public function getProviderName()
{
return 'Facebook';
}
/**
* {@inheritdoc}
*/
public function getUrl()
{
return $this->normalizeUrl($this->bag->get('url'));
}
/**
* {@inheritdoc}
*/
public function getAuthorName()
{
return $this->bag->get('username') ?: $this->bag->get('owner[name]');
}
/**
* {@inheritdoc}
*/
public function getImagesUrls()
{
$images = (array) $this->bag->get('cover[source]');
if (($id = $this->bag->get('id'))) {
$images[] = 'https://graph.facebook.com/'.$id.'/picture';
}
return $this->normalizeUrls($images);
}
/**
* {@inheritdoc}
*/
public function getPublishedTime()
{
return $this->bag->get('start_time');
}
/**
* {@inheritdoc}
*/
public function getCode()
{
return $this->bag->get('embed_html', true);
}
/**
* Returns the Graph API Endpoint
*
* @return Url|null
*/
private function getEndPoint()
{
$url = $this->adapter->getResponse()->getUrl();
$key = $this->adapter->getConfig('facebook[key]');
if (empty($key)) {
return;
}
if ($url->getDirectoryPosition(0) === 'events') {
return Url::create('https://graph.facebook.com/')
->withAddedPath($url->getDirectoryPosition(1))
->withQueryParameter('access_token', $key)
->withQueryParameter('fields', $this->adapter->getConfig('facebook[events_fields]'));
}
if ($url->getDirectoryPosition(1) === 'videos') {
return Url::create('https://graph.facebook.com/')
->withAddedPath($url->getDirectoryPosition(2))
->withQueryParameter('access_token', $key)
->withQueryParameter('fields', $this->adapter->getConfig('facebook[videos_fields]'));
}
}
}

View file

@ -0,0 +1,71 @@
<?php
namespace Embed\Providers\Api;
use Embed\Adapters\Adapter;
use Embed\Providers\Provider;
/**
* Provider to use the API of gist.github.com.
*/
class Gist extends Provider
{
/**
* {@inheritdoc}
*/
public function __construct(Adapter $adapter)
{
parent::__construct($adapter);
$endPoint = $adapter->getResponse()->getUrl()->withExtension('json');
$response = $adapter->getDispatcher()->dispatch($endPoint);
if (($json = $response->getJsonContent())) {
$this->bag->set($json);
}
}
/**
* {@inheritdoc}
*/
public function getDescription()
{
return $this->bag->get('description');
}
/**
* {@inheritdoc}
*/
public function getType()
{
if ($this->getCode() !== null) {
return 'rich';
}
}
/**
* {@inheritdoc}
*/
public function getAuthorName()
{
return $this->bag->get('owner');
}
/**
* {@inheritdoc}
*/
public function getPublishedTime()
{
return $this->bag->get('created_at');
}
/**
* {@inheritdoc}
*/
public function getCode()
{
if (($code = $this->bag->get('div', true)) && ($stylesheet = $this->normalizeUrl($this->bag->get('stylesheet')))) {
return '<link href="'.$stylesheet.'" rel="stylesheet">'.$code;
}
}
}

View file

@ -0,0 +1,167 @@
<?php
namespace Embed\Providers\Api;
use Embed\Adapters\Adapter;
use Embed\Providers\Provider;
use Embed\Http\Url;
use Embed\Utils;
/**
* Provider to use the API of google.com
*/
class GoogleMaps extends Provider
{
protected $mode;
protected $config = [
'key' => null,
];
/**
* {@inheritdoc}
*/
public function __construct(Adapter $adapter)
{
parent::__construct($adapter);
$mode = $adapter->getResponse()->getUrl()->getDirectoryPosition(1);
// Default is view (if mode is not mentioned in the url)
$this->mode = 'view';
switch ($mode) {
case 'place':
case 'dir':
case 'search':
$this->mode = $mode;
break;
}
// check streetview mode
// simple check,- starts with @, ends with t
if ((substr($mode, 0, 1) === '@') && (substr($mode, -1) === 't')) {
$this->mode = 'streetview';
}
}
/**
* {@inheritdoc}
*/
public function getTitle()
{
$url = $this->adapter->getResponse()->getUrl();
if ($this->mode === 'place') {
return $url->getDirectoryPosition(2);
}
if ($this->mode === 'dir') {
return $url->getDirectoryPosition(2).' / '.$url->getDirectoryPosition(3);
}
}
/**
* {@inheritdoc}
*/
public function getProviderName()
{
return 'Google Maps';
}
/**
* {@inheritdoc}
*/
public function getCode()
{
$url = $this->adapter->getResponse()->getUrl();
$key = $this->adapter->getConfig('google[key]');
if (empty($key)) {
return;
}
switch ($this->mode) {
case 'view':
$pos = self::getPosition($this->mode, $url);
return Utils::iframe($url
->withPath('maps/embed/v1/'.$this->mode)
->withQueryParameters([
'center' => $pos['coordinates'],
'zoom' => $pos['zoom'],
'key' => $key,
]));
case 'streetview':
$pos = self::getPosition($this->mode, $url);
return Utils::iframe($url
->withPath('maps/embed/v1/'.$this->mode)
->withQueryParameters([
'location' => $pos['coordinates'],
'heading' => $pos['heading'],
'pitch' => $pos['pitch'],
'fov' => $pos['fov'],
'key' => $key,
]));
case 'place':
case 'search':
return Utils::iframe($url
->withPath('maps/embed/v1/'.$this->mode)
->withQueryParameters([
'q' => $url->getDirectoryPosition(2),
'key' => $key,
]));
case 'dir':
return Utils::iframe($url
->withPath('maps/embed/v1/directions')
->withQueryParameters([
'origin' => $url->getDirectoryPosition(2),
'destination' => $url->getDirectoryPosition(3),
'key' => $key,
]));
}
}
/**
* Returns parsed position data from url.
*
* @param string $mode The url mode
* @param Url $url
*
* @return array
*/
private static function getPosition($mode, Url $url)
{
// Set defaults
$position = [
'coordinates' => '',
'zoom' => '4',
'heading' => '0',
'pitch' => '0',
'fov' => '90'
];
if ($mode === 'view') {
$pos = explode(",", $url->getDirectoryPosition(1));
$position['coordinates'] = str_replace('@', '', $pos[0]).','.$pos[1];
$position['zoom'] = str_replace('z', "", $pos[2]);
}
if ($mode === 'streetview') {
$pos = explode(",", $url->getDirectoryPosition(1));
$position['coordinates'] = str_replace('@', '', $pos[0]).','.$pos[1];
$position['zoom'] = str_replace('a', '', $pos[2]); // seems not used by google (emulated by other params)
$position['heading'] = str_replace('h', '', $pos[4]);
$position['fov'] = str_replace('y', '', $pos[3]);
$pitch = str_replace('t', '', $pos[5]); // t is pitch but in 180% format
if (is_numeric($pitch)) {
$position['pitch'] = floatval($pitch) - 90;
}
}
return $position;
}
}

View file

@ -0,0 +1,105 @@
<?php
namespace Embed\Providers\Api;
use Embed\Http\Url;
use Embed\Adapters\Adapter;
use Embed\Providers\Provider;
/**
* Provider to use the API of imageshack.com.
*/
class Imageshack extends Provider
{
/**
* {@inheritdoc}
*/
public function __construct(Adapter $adapter)
{
parent::__construct($adapter);
$id = $adapter->getResponse()->getUrl()->getDirectoryPosition(1);
$endPoint = Url::create('https://api.imageshack.com/v2/images/'.$id);
$response = $adapter->getDispatcher()->dispatch($endPoint);
if (($json = $response->getJsonContent()) && !empty($json['result'])) {
$this->bag->set($json['result']);
}
}
/**
* {@inheritdoc}
*/
public function getTitle()
{
return $this->bag->get('title');
}
/**
* {@inheritdoc}
*/
public function getDescription()
{
return $this->bag->get('description');
}
/**
* {@inheritdoc}
*/
public function getType()
{
return 'photo';
}
/**
* {@inheritdoc}
*/
public function getPublishedTime()
{
return $this->bag->get('creation_date');
}
/**
* {@inheritdoc}
*/
public function getWidth()
{
return $this->bag->get('width');
}
/**
* {@inheritdoc}
*/
public function getHeight()
{
return $this->bag->get('height');
}
/**
* {@inheritdoc}
*/
public function getAuthorName()
{
return $this->bag->get('owner[username]');
}
/**
* {@inheritdoc}
*/
public function getAuthorUrl()
{
$username = $this->getAuthorName();
if (!empty($username)) {
return 'http://imageshack.com/user/'.$username;
}
}
/**
* {@inheritdoc}
*/
public function getImagesUrls()
{
return $this->normalizeUrls($this->bag->get('direct_link'));
}
}

View file

@ -0,0 +1,91 @@
<?php
namespace Embed\Providers\Api;
use Embed\Http\Url;
use Embed\Adapters\Adapter;
use Embed\Providers\Provider;
/**
* Provider to use the API of soundcloud.
*/
class Soundcloud extends Provider
{
/**
* {@inheritdoc}
*/
public function __construct(Adapter $adapter)
{
parent::__construct($adapter);
$key = $adapter->getConfig('soundcloud[key]');
if (!empty($key)) {
$endPoint = Url::create('http://api.soundcloud.com/resolve.json')
->withQueryParameters([
'client_id' => $key,
'url' => (string) $adapter->getResponse()->getUrl(),
]);
$response = $adapter->getDispatcher()->dispatch($endPoint);
if ($json = $response->getJsonContent()) {
$this->bag->set($json);
}
}
}
/**
* {@inheritdoc}
*/
public function getTitle()
{
return $this->bag->get('title');
}
/**
* {@inheritdoc}
*/
public function getDescription()
{
return $this->bag->get('description');
}
/**
* {@inheritdoc}
*/
public function getUrl()
{
return $this->normalizeUrl($this->bag->get('permalink_url'));
}
/**
* {@inheritdoc}
*/
public function getImagesUrls()
{
$images = [];
if (empty($this->bag->get('artwork_url')) && ($img = $this->bag->get('user[avatar_url]'))) {
$images[] = str_replace('-large.jpg', '-t500x500.jpg', $img);
}
return $this->normalizeUrls($images);
}
/**
* {@inheritdoc}
*/
public function getAuthorName()
{
return $this->bag->get('user[username]');
}
/**
* {@inheritdoc}
*/
public function getAuthorUrl()
{
return $this->normalizeUrl($this->bag->get('user[permalink_url]'));
}
}

View file

@ -0,0 +1,134 @@
<?php
namespace Embed\Providers\Api;
use Embed\Adapters\Adapter;
use Embed\Providers\Provider;
/**
* Provider to use the API of wikipedia.
*/
class Wikipedia extends Provider
{
/**
* {@inheritdoc}
*/
public function __construct(Adapter $adapter)
{
parent::__construct($adapter);
$titles = $adapter->getResponse()->getUrl()->getDirectoryPosition(1);
if (!empty($titles)) {
//extract images
$endPoint = $adapter->getResponse()->getUrl()
->withPath('/w/api.php')
->withQueryParameters([
'action' => 'query',
'format' => 'json',
'continue' => '',
'titles' => $titles,
'prop' => 'images',
]);
$response = $adapter->getDispatcher()->dispatch($endPoint);
if (($json = $response->getJsonContent())) {
$this->bag->set('images', $json);
}
//extract content
$endPoint = $endPoint
->withQueryParameter('prop', 'extracts')
->withQueryParameter('exchars', 1500);
$response = $adapter->getDispatcher()->dispatch($endPoint);
if (($json = $response->getJsonContent())) {
$this->bag->set('extracts', $json);
}
}
}
/**
* {@inheritdoc}
*/
public function getTitle()
{
$pages = $this->bag->get('extracts[query][pages]');
if (!empty($pages)) {
$page = current($pages);
return strip_tags($page['title']);
}
}
/**
* {@inheritdoc}
*/
public function getDescription()
{
$pages = $this->bag->get('extracts[query][pages]');
if (!empty($pages)) {
return $this->bag->get('extracts[query][pages]['.key($pages).'][extract]');
}
}
/**
* {@inheritdoc}
*/
public function getImagesUrls()
{
$images = [];
$pages = $this->bag->get('images[query][pages]');
if (!empty($pages)) {
$page = current($pages);
$imgs = [];
if (isset($page['images'])) {
foreach ($page['images'] as $image) {
switch (strrchr($image['title'], '.')) {
case '.png':
case '.jpg':
case '.gif':
case '.jpeg':
$imgs[] = $image['title'];
break;
}
}
}
//Get image urls
if (!empty($imgs)) {
$endPoint = $this->adapter->getResponse()->getUrl()
->withPath('/w/api.php')
->withQueryParameters([
'action' => 'query',
'prop' => 'imageinfo',
'iiprop' => 'url',
'format' => 'json',
'continue' => '',
'titles' => implode('|', $imgs),
]);
$response = $this->adapter->getDispatcher()->dispatch($endPoint);
$json = $response->getJsonContent();
if (isset($json['query']['pages'])) {
foreach ($json['query']['pages'] as $page) {
if (isset($page['imageinfo'][0]['url'])) {
$images[] = $page['imageinfo'][0]['url'];
}
}
}
}
}
return $this->normalizeUrls($images);
}
}

View file

@ -0,0 +1,75 @@
<?php
namespace Embed\Providers;
use Embed\Adapters\Adapter;
/**
* Provider to get the data from the Dublin Core data elements in the HTML
*/
class Dcterms extends Provider
{
/**
* {@inheritdoc}
*/
public function __construct(Adapter $adapter)
{
parent::__construct($adapter);
if (!($html = $adapter->getResponse()->getHtmlContent())) {
return;
}
foreach ($html->getElementsByTagName('meta') as $meta) {
$name = trim(strtolower($meta->getAttribute('name')));
$value = $meta->getAttribute('content');
if (empty($name) || empty($value)) {
continue;
}
foreach (['dc.', 'dc:', 'dcterms:'] as $prefix) {
if (stripos($name, $prefix) === 0) {
$name = substr($name, strlen($prefix));
$this->bag->set($name, $value);
}
}
}
}
/**
* {@inheritdoc}
*/
public function getTitle()
{
return $this->bag->get('title');
}
/**
* {@inheritdoc}
*/
public function getDescription()
{
return $this->bag->get('description');
}
/**
* {@inheritdoc}
*/
public function getAuthorName()
{
return $this->bag->get('creator') ?: $this->bag->get('author');
}
/**
* {@inheritdoc}
*/
public function getPublishedTime()
{
foreach (['date', 'date.created', 'date.issued'] as $key) {
if ($found = $this->bag->get($key)) {
return $found;
}
}
}
}

View file

@ -0,0 +1,441 @@
<?php
namespace Embed\Providers;
use Embed\Utils;
use Embed\Adapters\Adapter;
use Embed\Http\Url;
use DOMDocument;
use Exception;
/**
* Provider to get the data from the HTML code
*/
class Html extends Provider
{
/**
* {@inheritdoc}
*/
public function __construct(Adapter $adapter)
{
parent::__construct($adapter);
if (!($html = $adapter->getResponse()->getHtmlContent())) {
return;
}
$this->extractLinks($html);
$this->extractMetas($html);
$this->extractImages($html);
//Title
$title = $html->getElementsByTagName('title');
if ($title->length) {
$this->bag->set('title', $title->item(0)->nodeValue);
}
}
/**
* {@inheritdoc}
*/
public function getTitle()
{
return $this->bag->get('title');
}
/**
* {@inheritdoc}
*/
public function getDescription()
{
return $this->bag->get('description');
}
/**
* {@inheritdoc}
*/
public function getType()
{
return $this->bag->has('video_src') ? 'video' : null;
}
/**
* {@inheritdoc}
*/
public function getTags()
{
$keywords = $this->bag->get('keywords').','.$this->bag->get('news_keywords');
return array_filter(
array_map('trim', explode(',', $keywords)),
function ($value) {
return !empty($value) && substr($value, -3) !== '...';
}
);
}
/**
* {@inheritdoc}
*/
public function getFeeds()
{
return $this->normalizeUrls($this->bag->get('feeds'));
}
/**
* {@inheritdoc}
*/
public function getCode()
{
$src = $this->normalizeUrl($this->bag->get('video_src'));
if ($src !== null) {
switch ($this->bag->get('video_type')) {
case 'application/x-shockwave-flash':
return Utils::flash($src, $this->getWidth(), $this->getHeight());
}
}
}
/**
* {@inheritdoc}
*/
public function getUrl()
{
return $this->normalizeUrl($this->bag->get('canonical'));
}
/**
* {@inheritdoc}
*/
public function getAuthorName()
{
return $this->bag->get('author') ?: $this->bag->get('article:author') ?: $this->bag->get('contributors');
}
/**
* {@inheritdoc}
*/
public function getProviderIconsUrls()
{
return $this->normalizeUrls($this->bag->get('icons'));
}
/**
* {@inheritdoc}
*/
public function getImagesUrls()
{
$images = $this->normalizeUrls($this->bag->get('images'));
if (!empty($images)) {
$maxImages = $this->adapter->getConfig('html[max_images]', -1);
if ($maxImages > -1) {
return array_slice($images, 0, $maxImages);
}
}
return $images;
}
/**
* {@inheritdoc}
*/
public function getWidth()
{
return ((int) $this->bag->get('video_width')) ?: null;
}
/**
* {@inheritdoc}
*/
public function getHeight()
{
return ((int) $this->bag->get('video_height')) ?: null;
}
/**
* {@inheritdoc}
*/
public function getPublishedTime()
{
$keys = [
'article:published_time',
'created',
'date',
'datepublished',
'datePublished',
'newsrepublic:publish_date',
'pagerender',
'pub_date',
'publication-date',
'publish-date',
'rc.datecreation',
'timestamp',
'article:modified_time',
'eomportal-lastupdate',
'shareaholic:article_published_time',
];
foreach ($keys as $key) {
if ($found = $this->bag->get($key)) {
return $found;
}
}
}
/**
* {@inheritdoc}
*/
public function getLicense()
{
return $this->bag->get('copyright');
}
/**
* {@inheritdoc}
*/
public function getLinkedData()
{
$data = [];
if (!($html = $this->adapter->getResponse()->getHtmlContent())) {
return $data;
}
foreach ($html->getElementsByTagName('script') as $script) {
if ($script->hasAttribute('type') && strtolower($script->getAttribute('type')) === 'application/ld+json') {
$value = trim($script->nodeValue);
if (empty($value)) {
continue;
}
try {
$data[] = json_decode($value);
} catch (Exception $exception) {
continue;
}
}
}
return $data;
}
/**
* Extract information from the <link> elements.
*
* @param DOMDocument $html
*/
private function extractLinks(DOMDocument $html)
{
foreach ($html->getElementsByTagName('link') as $link) {
if ($link->hasAttribute('rel') && $link->hasAttribute('href')) {
$rel = trim(strtolower($link->getAttribute('rel')));
$href = $link->getAttribute('href');
if (empty($href)) {
continue;
}
switch ($rel) {
case 'favicon':
case 'favico':
case 'icon':
case 'shortcut icon':
case 'apple-touch-icon-precomposed':
case 'apple-touch-icon':
$this->bag->add('icons', $href);
break;
case 'image_src':
$this->bag->add('images', $href);
break;
case 'alternate':
switch ($link->getAttribute('type')) {
case 'application/atom+xml':
case 'application/json':
case 'application/rdf+xml':
case 'application/rss+xml':
case 'application/xml':
case 'text/xml':
$this->bag->add('feeds', $href);
break;
}
break;
default:
$this->bag->set($rel, $href);
}
}
}
}
/**
* Extract information from the <meta> elements.
*
* @param DOMDocument $html
*/
private function extractMetas(DOMDocument $html)
{
foreach ($html->getElementsByTagName('meta') as $meta) {
$value = $meta->getAttribute('content');
if (empty($value)) {
continue;
}
if ($meta->hasAttribute('name')) {
$name = trim(strtolower($meta->getAttribute('name')));
switch ($name) {
case 'msapplication-tileimage':
$this->bag->add('icons', $value);
continue 2;
default:
$this->bag->set($name, $value);
continue 2;
}
}
if ($meta->hasAttribute('itemprop')) {
$this->bag->set($meta->getAttribute('itemprop'), $value);
}
if ($meta->hasAttribute('http-equiv')) {
$this->bag->set($meta->getAttribute('http-equiv'), $value);
}
if ($meta->hasAttribute('property')) {
$this->bag->set($meta->getAttribute('property'), $value);
}
}
}
/**
* Extract <img> elements.
*
* @param DOMDocument $html
*/
private function extractImages(DOMDocument $html)
{
if ($this->adapter->getConfig('html[max_images]') === 0) {
return;
}
//Extract only from the main element
$main = self::getMainElement($html);
if (!$main) {
return;
}
$url = $this->adapter->getResponse()->getUrl();
$externalImages = $this->adapter->getConfig('html[external_images]');
foreach ($main->getElementsByTagName('img') as $img) {
if (!$img->hasAttribute('src')) {
continue;
}
try {
$src = $url->createAbsolute($img->getAttribute('src'));
} catch (Exception $exception) {
continue;
}
//Avoid external images
if (!self::imageIsValid($src, $url, $externalImages)) {
continue;
}
$parent = $img->parentNode;
//The image is in a link
while ($parent && isset($parent->tagName)) {
if ($parent->tagName === 'a') {
//The link is external
if ($parent->hasAttribute('href')) {
try {
$href = $url->createAbsolute($parent->getAttribute('href'));
} catch (Exception $exception) {
continue 2;
}
if (!self::imageIsValid($href, $url, $externalImages)) {
continue 2;
}
}
//The link has rel=nofollow
if ($parent->hasAttribute('rel') && (string) $parent->getAttribute('rel') === 'nofollow') {
continue 2;
}
break;
}
$parent = $parent->parentNode;
}
$this->bag->add('images', (string) $src);
}
}
/**
* Check whether a image url is valid or not.
*
* @param Url $url
* @param Url $baseUrl
* @param mixed $externalImages
*
* @return bool
*/
private static function imageIsValid(Url $url, Url $baseUrl, $externalImages)
{
//base64 or same domain
if ($url->getContent() !== null || $url->getDomain() === $baseUrl->getDomain()) {
return true;
}
return is_bool($externalImages) ? $externalImages : $url->match($externalImages);
}
/**
* Returns the main element of the document.
*
* @param DOMDocument $html
*
* @return DOMElement
*/
private static function getMainElement(DOMDocument $html)
{
// <main>
$content = $html->getElementsByTagName('main');
if ($content->length !== 0) {
return $content->item(0);
}
// Popular ids: #main, #content, #page
$content = $html->getElementById('main') ?: $html->getElementById('content') ?: $html->getElementById('page');
if ($content) {
return $content;
}
// Wordpress ids: #post-*
foreach ($html->getElementsByTagName('article') as $article) {
if ($article->hasAttribute('id') && (strpos($article->getAttribute('id'), 'post-') === 0)) {
return $article;
}
}
// Returns <body> or <html>
return $html->getElementsByTagName('body')->item(0) ?: $html->getElementsByTagName('html')->item(0);
}
}

View file

@ -0,0 +1,107 @@
<?php
namespace Embed\Providers;
use Embed\Adapters\Adapter;
use Embed\Utils;
/**
* Provider to get the data from the Linkpulse elements in the HTML
* http://docs.linkpulse.com/docs/opengraph/
*/
class Linkpulse extends Provider
{
/**
* {@inheritdoc}
*/
public function __construct(Adapter $adapter)
{
parent::__construct($adapter);
if (!($html = $adapter->getResponse()->getHtmlContent())) {
return;
}
foreach ($html->getElementsByTagName('meta') as $meta) {
$name = trim(strtolower($meta->getAttribute('property')));
$value = $meta->getAttribute('content');
if (empty($name)) {
$name = trim(strtolower($meta->getAttribute('name')));
}
if (empty($name) || empty($value)) {
continue;
}
if (strpos($name, 'lp:') === 0) {
$name = substr($name, 3);
} elseif (strpos($name, 'lp.article:') !== 0) {
continue;
}
$this->bag->set($name, $value);
}
}
/**
* {@inheritdoc}
*/
public function getTitle()
{
return $this->bag->get('title');
}
/**
* {@inheritdoc}
*/
public function getDescription()
{
return $this->bag->get('description');
}
/**
* {@inheritdoc}
*/
public function getUrl()
{
$url = $this->normalizeUrl($this->bag->get('url'));
if ($url !== $this->adapter->getResponse()->getUrl()->getAbsolute('/')) {
return $url;
}
}
/**
* {@inheritdoc}
*/
public function getTags()
{
return (array) $this->bag->get('article.section') ?: [];
}
/**
* {@inheritdoc}
*/
public function getAuthorName()
{
return $this->bag->get('article.author');
}
/**
* {@inheritdoc}
*/
public function getImagesUrls()
{
return $this->normalizeUrls((array) $this->bag->get('image'));
}
/**
* {@inheritdoc}
*/
public function getPublishedTime()
{
return $this->bag->get('article.published_time')
?: $this->bag->get('article.modified_time');
}
}

View file

@ -0,0 +1,248 @@
<?php
namespace Embed\Providers;
use Embed\Adapters\Adapter;
use Embed\Http\Response;
use Embed\Http\Url;
/**
* Provider to get the data using the oEmbed API
*/
class OEmbed extends Provider
{
/**
* {@inheritdoc}
*/
public function __construct(Adapter $adapter)
{
parent::__construct($adapter);
$endPoint = $this->getEndPoint();
if ($endPoint) {
$this->extractOembed($adapter->getDispatcher()->dispatch($endPoint));
}
}
/**
* {@inheritdoc}
*/
public function getTitle()
{
return $this->bag->get('title');
}
/**
* {@inheritdoc}
*/
public function getDescription()
{
return $this->bag->get('description');
}
/**
* {@inheritdoc}
*/
public function getType()
{
$type = $this->bag->get('type');
if (strpos($type, ':') !== false) {
$type = substr(strrchr($type, ':'), 1);
}
switch ($type) {
case 'video':
case 'photo':
case 'link':
case 'rich':
return $type;
case 'movie':
return 'video';
}
}
/**
* {@inheritdoc}
*/
public function getTags()
{
if ($this->bag->has('meta[keywords]')) {
//it means we are using iframe.ly api
return array_map('trim', explode(',', $this->bag->get('meta[keywords]')));
}
return [];
}
/**
* {@inheritdoc}
*/
public function getCode()
{
return $this->bag->get('html', true);
}
/**
* {@inheritdoc}
*/
public function getUrl()
{
if ($this->getType() === 'photo') {
return $this->normalizeUrl($this->bag->get('web_page'));
}
return $this->normalizeUrl($this->bag->get('url') ?: $this->bag->get('web_page'));
}
/**
* {@inheritdoc}
*/
public function getAuthorName()
{
return $this->bag->get('author_name');
}
/**
* {@inheritdoc}
*/
public function getAuthorUrl()
{
return $this->normalizeUrl($this->bag->get('author_url'));
}
/**
* {@inheritdoc}
*/
public function getProviderName()
{
return $this->bag->get('provider_name');
}
/**
* {@inheritdoc}
*/
public function getProviderUrl()
{
return $this->normalizeUrl($this->bag->get('provider_url'));
}
/**
* {@inheritdoc}
*/
public function getImagesUrls()
{
$images = [];
if ($this->getType() === 'photo') {
$images[] = $this->bag->get('url');
}
foreach (['image', 'thumbnail', 'thumbnail_url'] as $type) {
if ($this->bag->has($type)) {
$ret = $this->bag->get($type);
if (is_array($ret)) {
$images = array_merge($images, $ret);
} else {
$images[] = $ret;
}
}
}
return $this->normalizeUrls($images);
}
/**
* {@inheritdoc}
*/
public function getWidth()
{
return $this->bag->get('width');
}
/**
* {@inheritdoc}
*/
public function getHeight()
{
return $this->bag->get('height');
}
/**
* {@inheritdoc}
*/
public function getLicense()
{
return $this->bag->get('license_url');
}
/**
* @return Url|null
*/
private function getEndPoint()
{
//Search using the domain
$class = 'Embed\\Providers\\OEmbed\\'.$this->adapter->getResponse()->getUrl()->getClassNameForDomain();
$extraParameters = (array) $this->adapter->getConfig('oembed[parameters]');
if (class_exists($class)) {
$endPoint = $class::create($this->adapter);
if ($endPoint && ($url = $endPoint->getEndPoint())) {
return $url->withAddedQueryParameters($extraParameters);
}
}
//Search in the DOM
$endPoint = OEmbed\DOM::create($this->adapter);
if ($endPoint && ($url = $endPoint->getEndPoint())) {
return $url->withAddedQueryParameters($extraParameters);
}
//Try with embedly
$endPoint = OEmbed\Embedly::create($this->adapter);
if ($endPoint && ($url = $endPoint->getEndPoint())) {
return $url->withAddedQueryParameters($extraParameters);
}
//Try with iframely
$endPoint = OEmbed\Iframely::create($this->adapter);
if ($endPoint && ($url = $endPoint->getEndPoint())) {
return $url->withAddedQueryParameters($extraParameters);
}
}
/**
* Save the oembed data in the bag.
*
* @param Response $response
*/
private function extractOembed(Response $response)
{
// extract from xml
if (($response->getUrl()->getExtension() === 'xml') || ($response->getUrl()->getQueryParameter('format') === 'xml')) {
if ($xml = $response->getXmlContent()) {
foreach ($xml as $element) {
$content = trim((string) $element);
if (stripos($content, '<![CDATA[') === 0) {
$content = substr($content, 9, -3);
}
$this->bag->set($element->getName(), $content);
}
}
// extract from json
} else {
if (($json = $response->getJsonContent()) && empty($json['Error'])) {
$this->bag->set($json);
}
}
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Embed\Providers\OEmbed;
class Amcharts extends EndPoint implements EndPointInterface
{
protected static $pattern = 'live.amcharts.com/*';
protected static $endPoint = 'https://live.amcharts.com/oembed';
}

View file

@ -0,0 +1,9 @@
<?php
namespace Embed\Providers\OEmbed;
class Bambuser extends EndPoint implements EndPointInterface
{
protected static $pattern = 'bambuser.com/v/*';
protected static $endPoint = 'https://api.bambuser.com/oembed.json';
}

View file

@ -0,0 +1,95 @@
<?php
namespace Embed\Providers\OEmbed;
use Embed\Adapters\Adapter;
use Embed\Http\Response;
use Embed\Http\Url;
use DOMDocument;
use Exception;
/**
* Class to detect the oembed endPoint.
*/
class DOM implements EndPointInterface
{
protected $response;
/**
* Create a instance of a OEmbedEndPoint.
*
* @param Adapter $adapter
*
* @return static
*/
public static function create(Adapter $adapter)
{
return new static($adapter->getResponse());
}
/**
* Construct.
*
* @param Response $response
*/
protected function __construct(Response $response)
{
$this->response = $response;
}
/**
* Returns the oembed endPoint.
*
* @return Url|null
*/
public function getEndPoint()
{
$html = $this->response->getHtmlContent();
if ($html && ($url = self::getEndPointFromDom($html))) {
try {
$endPoint = $this->response->getUrl()->createAbsolute($url);
} catch (Exception $exception) {
return null;
}
if ($endPoint->getExtension() !== 'xml' && !$endPoint->hasQueryParameter('format')) {
return $endPoint->withQueryParameter('format', 'json');
}
return $endPoint;
}
}
/**
* Extract oembed information from the <link rel="alternate"> elements
* Note: Some sites use <meta rel="alternate"> instead.
*
* @param DOMDocument $html
*
* @return string|null
*/
protected static function getEndPointFromDom(DOMDocument $html)
{
foreach (['link', 'meta'] as $tagName) {
foreach ($html->getElementsByTagName($tagName) as $link) {
$rel = trim(strtolower($link->getAttribute('rel')));
$href = $link->getAttribute('href');
if (empty($href)) {
continue;
}
if ($rel === 'alternate') {
switch (strtolower($link->getAttribute('type'))) {
case 'application/json+oembed':
case 'application/xml+oembed':
case 'text/json+oembed':
case 'text/xml+oembed':
return $href;
}
}
}
}
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Embed\Providers\OEmbed;
class Dailymotion extends EndPoint implements EndPointInterface
{
protected static $pattern = '*dailymotion.*';
protected static $endPoint = 'http://www.dailymotion.com/services/oembed';
}

View file

@ -0,0 +1,27 @@
<?php
namespace Embed\Providers\OEmbed;
use Embed\Http\Url;
class Deviantart extends EndPoint implements EndPointInterface
{
protected static $pattern = [
'*.deviantart.com/art/*',
'www.deviantart.com/#/d*',
];
protected static $endPoint = 'http://backend.deviantart.com/oembed';
/**
* {@inheritdoc}
*/
public function getEndPoint()
{
return Url::create(static::$endPoint)
->withQueryParameters([
'url' => (string) $this->response->getUrl(),
'format' => 'json',
'for' => 'embed',
]);
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Embed\Providers\OEmbed;
class Dotsub extends EndPoint implements EndPointInterface
{
protected static $pattern = 'dotsub.com/view/*';
protected static $endPoint = 'http://dotsub.com/services/oembed';
}

View file

@ -0,0 +1,50 @@
<?php
namespace Embed\Providers\OEmbed;
use Embed\Adapters\Adapter;
use Embed\Http\Response;
use Embed\Http\Url;
class Embedly implements EndPointInterface
{
private $response;
private $key;
/**
* {@inheritdoc}
*/
public static function create(Adapter $adapter)
{
$key = $adapter->getConfig('oembed[embedly_key]');
if (!empty($key)) {
return new static($adapter->getResponse(), $key);
}
}
/**
* Constructor.
*
* @param Response $response
* @param string $key
*/
protected function __construct(Response $response, $key)
{
$this->response = $response;
$this->key = $key;
}
/**
* {@inheritdoc}
*/
public function getEndPoint()
{
return Url::create('http://api.embed.ly/1/oembed')
->withQueryParameters([
'url' => (string) $this->response->getUrl(),
'format' => 'json',
'key' => $this->key,
]);
}
}

View file

@ -0,0 +1,51 @@
<?php
namespace Embed\Providers\OEmbed;
use Embed\Adapters\Adapter;
use Embed\Http\Response;
use Embed\Http\Url;
/**
* Abstract class extended by other classes.
*/
abstract class EndPoint
{
protected $response;
protected static $pattern;
protected static $endPoint;
/**
* {@inheritdoc}
*/
public static function create(Adapter $adapter)
{
$response = $adapter->getResponse();
if ($response->getUrl()->match(static::$pattern)) {
return new static($response);
}
}
/**
* Constructor.
*
* @param Response $response
*/
protected function __construct(Response $response)
{
$this->response = $response;
}
/**
* {@inheritdoc}
*/
public function getEndPoint()
{
return Url::create(static::$endPoint)
->withQueryParameters([
'url' => (string) $this->response->getUrl(),
'format' => 'json',
]);
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace Embed\Providers\OEmbed;
use Embed\Adapters\Adapter;
use Embed\Http\Url;
/**
* Interface for all oembed endPoint.
*/
interface EndPointInterface
{
/**
* Check the response and create new instance.
*
* @param Adapter $adapter
*
* @return EndPointInterface|null
*/
public static function create(Adapter $adapter);
/**
* Returns the oembed endPoint.
*
* @return Url|null
*/
public function getEndPoint();
}

View file

@ -0,0 +1,27 @@
<?php
namespace Embed\Providers\OEmbed;
use Embed\Http\Url;
class Facebook extends EndPoint implements EndPointInterface
{
protected static $pattern = 'www.facebook.com/*';
/**
* {@inheritdoc}
*/
public function getEndPoint()
{
if ($this->response->getUrl()->match(['*/videos/*', '/video.php'])) {
$endPoint = Url::create('https://www.facebook.com/plugins/video/oembed.json');
} else {
$endPoint = Url::create('https://www.facebook.com/plugins/post/oembed.json');
}
return $endPoint->withQueryParameters([
'url' => (string) $this->response->getUrl(),
'format' => 'json',
]);
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Embed\Providers\OEmbed;
class Flickr extends EndPoint implements EndPointInterface
{
protected static $pattern = 'www.flickr.com/*';
protected static $endPoint = 'http://flickr.com/services/oembed';
}

View file

@ -0,0 +1,50 @@
<?php
namespace Embed\Providers\OEmbed;
use Embed\Adapters\Adapter;
use Embed\Http\Response;
use Embed\Http\Url;
class Iframely implements EndPointInterface
{
private $response;
private $key;
/**
* {@inheritdoc}
*/
public static function create(Adapter $adapter)
{
$key = $adapter->getConfig('oembed[iframely_key]');
if (!empty($key)) {
return new static($adapter->getResponse(), $key);
}
}
/**
* Constructor.
*
* @param Response $response
* @param string $key
*/
protected function __construct(Response $response, $key)
{
$this->response = $response;
$this->key = $key;
}
/**
* {@inheritdoc}
*/
public function getEndPoint()
{
return Url::create('http://open.iframe.ly/api/oembed')
->withQueryParameters([
'url' => (string) $this->response->getUrl(),
'format' => 'json',
'api_key' => $this->key,
]);
}
}

View file

@ -0,0 +1,12 @@
<?php
namespace Embed\Providers\OEmbed;
class Imgur extends EndPoint implements EndPointInterface
{
protected static $pattern = [
'imgur.com/*',
'i.imgur.com/*',
];
protected static $endPoint = 'https://api.imgur.com/oembed';
}

View file

@ -0,0 +1,27 @@
<?php
namespace Embed\Providers\OEmbed;
use Embed\Http\Url;
class Infogram extends EndPoint implements EndPointInterface
{
protected static $pattern = [
'infogr.am/*',
'www.infogr.am/*',
];
protected static $endPoint = 'https://infogr.am/oembed';
/**
* {@inheritdoc}
*/
public function getEndPoint()
{
$url = $this->response->getUrl()->withScheme('https');
return Url::create(static::$endPoint)
->withQueryParameters([
'url' => (string) $url,
]);
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace Embed\Providers\OEmbed;
use Embed\Http\Url;
class Instagram extends EndPoint implements EndPointInterface
{
protected static $pattern = [
'instagram.com/p/*',
'www.instagram.com/p/*',
];
protected static $endPoint = 'http://api.instagram.com/oembed';
/**
* {@inheritdoc}
*/
public function getEndPoint()
{
$url = $this->response->getUrl()->withScheme('http');
return Url::create(static::$endPoint)
->withQueryParameters([
'url' => (string) $url,
'format' => 'json',
]);
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace Embed\Providers\OEmbed;
use Embed\Http\Url;
class Jsbin extends EndPoint implements EndPointInterface
{
protected static $pattern = 'output.jsbin.com/*';
protected static $endPoint = 'http://jsbin.com/oembed';
/**
* {@inheritdoc}
*/
public function getEndPoint()
{
$url = $this->response->getUrl()->withDirectoryPosition(2, 'embed');
return Url::create(static::$endPoint)
->withQueryParameters([
'url' => (string) $url,
'format' => 'json',
]);
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Embed\Providers\OEmbed;
class Kickstarter extends EndPoint implements EndPointInterface
{
protected static $pattern = 'www.kickstarter.com/*';
protected static $endPoint = 'http://www.kickstarter.com/services/oembed';
}

View file

@ -0,0 +1,9 @@
<?php
namespace Embed\Providers\OEmbed;
class Meetup extends EndPoint implements EndPointInterface
{
protected static $pattern = 'www.meetup.com/*';
protected static $endPoint = 'http://api.meetup.com/oembed';
}

View file

@ -0,0 +1,9 @@
<?php
namespace Embed\Providers\OEmbed;
class Photobucket extends EndPoint implements EndPointInterface
{
protected static $pattern = 'photobucket.com/*';
protected static $endPoint = 'http://s51.photobucket.com/oembed/';
}

View file

@ -0,0 +1,9 @@
<?php
namespace Embed\Providers\OEmbed;
class Polldaddy extends EndPoint implements EndPointInterface
{
protected static $pattern = 'polldaddy.com/poll/*';
protected static $endPoint = 'http://polldaddy.com/oembed';
}

View file

@ -0,0 +1,9 @@
<?php
namespace Embed\Providers\OEmbed;
class Reddit extends EndPoint implements EndPointInterface
{
protected static $pattern = 'www.reddit.com/*';
protected static $endPoint = 'https://www.reddit.com/oembed';
}

View file

@ -0,0 +1,28 @@
<?php
namespace Embed\Providers\OEmbed;
use Embed\Http\Url;
class Scribd extends EndPoint implements EndPointInterface
{
protected static $pattern = [
'www.scribd.com/doc/*',
'www.scribd.com/document/*',
];
protected static $endPoint = 'http://www.scribd.com/services/oembed';
/**
* {@inheritdoc}
*/
public function getEndPoint()
{
$url = $this->response->getUrl()->withDirectoryPosition(0, 'doc');
return Url::create(static::$endPoint)
->withQueryParameters([
'url' => (string) $url,
'format' => 'json',
]);
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Embed\Providers\OEmbed;
class Shoudio extends EndPoint implements EndPointInterface
{
protected static $pattern = 'shoudio.com/*';
protected static $endPoint = 'http://shoudio.com/api/oembed';
}

View file

@ -0,0 +1,9 @@
<?php
namespace Embed\Providers\OEmbed;
class Smugmug extends EndPoint implements EndPointInterface
{
protected static $pattern = 'www.smugmug.com/*';
protected static $endPoint = 'http://api.smugmug.com/services/oembed/';
}

View file

@ -0,0 +1,9 @@
<?php
namespace Embed\Providers\OEmbed;
class Soundcloud extends EndPoint implements EndPointInterface
{
protected static $pattern = 'soundcloud.com/*';
protected static $endPoint = 'http://soundcloud.com/oembed';
}

View file

@ -0,0 +1,23 @@
<?php
namespace Embed\Providers\OEmbed;
use Embed\Http\Url;
class Spotify extends EndPoint implements EndPointInterface
{
protected static $pattern = '*.spotify.com/*';
protected static $endPoint = 'https://embed.spotify.com/oembed';
/**
* {@inheritdoc}
*/
public function getEndPoint()
{
return Url::create(static::$endPoint)
->withQueryParameters([
'url' => (string) $this->response->getUrl()->withQueryParameters([]),
'format' => 'json'
]);
}
}

View file

@ -0,0 +1,39 @@
<?php
namespace Embed\Providers\OEmbed;
use Embed\Adapters\Adapter;
use Embed\Http\Response;
use Embed\Http\Url;
class Tumblr extends EndPoint implements EndPointInterface
{
protected static $pattern = [
'*.tumblr.com/post/*'
];
protected static $endPoint = 'https://www.tumblr.com/oembed/1.0';
/**
* {@inheritdoc}
*/
public static function create(Adapter $adapter)
{
$response = $adapter->getResponse();
if ($response->getStartingUrl()->match(static::$pattern)) {
return new static($response);
}
}
/**
* {@inheritdoc}
*/
public function getEndPoint()
{
return Url::create(static::$endPoint)
->withQueryParameters([
'url' => (string) $this->response->getStartingUrl()
]);
}
}

Some files were not shown because too many files have changed in this diff Show more