Actualización

This commit is contained in:
Xes
2025-04-10 12:24:57 +02:00
parent 8969cc929d
commit 45420b6f0d
39760 changed files with 4303286 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Cache;
/**
* Handles caching.
*
* @package Essence.Cache
*/
interface Engine {
/**
* Returns if data exists for the given key.
*
* @param string $key The key to test.
* @return boolean Whether there is data for the key or not.
*/
public function has( $key );
/**
* Returns the data for the given key.
*
* @param string $key The key to search for.
* @param mixed $default Default value to return if there is no data.
* @return mixed The data.
*/
public function get( $key, $default = false );
/**
* Sets the data for the given key.
*
* @param string $key The key for the data.
* @param mixed $data The data.
* @return mixed $data The passed data.
*/
public function set( $key, $data );
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Cache\Engine;
use Essence\Cache\Engine;
/**
* Does absolutely nothing.
*
* @package Essence.Cache.Engine
*/
class Null implements Engine {
/**
* {@inheritDoc}
*/
public function has( $key ) {
return false;
}
/**
* {@inheritDoc}
*/
public function get( $key, $default = null ) {
return $default;
}
/**
* {@inheritDoc}
*/
public function set( $key, $data ) { }
}

View File

@@ -0,0 +1,64 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Cache\Engine;
use Essence\Cache\Engine;
/**
* Handles caching for a single session.
*
* @package Essence.Cache.Engine
*/
class Volatile implements Engine {
/**
* Data.
*
* @var array
*/
protected $_data = [ ];
/**
* {@inheritDoc}
*/
public function has( $key ) {
return array_key_exists( $key, $this->_data );
}
/**
* {@inheritDoc}
*/
public function get( $key, $default = null ) {
return $this->has( $key )
? $this->_data[ $key ]
: $default;
}
/**
* {@inheritDoc}
*/
public function set( $key, $data ) {
$this->_data[ $key ] = $data;
}
}

View File

@@ -0,0 +1,73 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence;
/**
* Allows an object to cache method calls.
*
* @package Essence
*/
trait Cacheable {
/**
* Internal cache engine.
*
* @var Essence\Cache\Engine
*/
protected $_Cache = null;
/**
* Returns the cached result of a method call.
*
* @param string $method The method to cache.
* @param ... mixed Parameters to be passed to the method.
* @return mixed Cached result.
*/
protected function _cached( $method ) {
$signature = $method;
$args = [ ];
if ( func_num_args( ) > 1 ) {
$args = array_slice( func_get_args( ), 1 );
$signature .= json_encode( $args );
}
$key = $this->_cacheKey( $signature );
if ( $this->_Cache->has( $key )) {
return $this->_Cache->get( $key );
}
$result = call_user_func_array( [ $this, $method ], $args );
$this->_Cache->set( $key, $result );
return $result;
}
/**
* Generates a key from the given signature.
*
* @param string $signature Method signature.
* @return string Generated key.
*/
protected function _cacheKey( $signature ) {
return md5( $signature );
}
}

View File

@@ -0,0 +1,175 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence;
/**
* Makes a class configurable.
*
* @package Essence
*/
trait Configurable {
/**
* An array of properties, to be defined in classes using the trait.
*
* @var array
*/
// protected $_properties = array( );
/**
* @see has( )
*/
public function __isset( $property ) {
return $this->has( $property );
}
/**
* @see get( )
*/
public function __get( $property ) {
return $this->get( $property );
}
/**
* @see set( )
*/
public function __set( $property, $value ) {
return $this->set( $property, $value );
}
/**
* Returns if there is any value for the given property.
*
* @param string $property Property name.
* @param boolean True if the property exists, otherwise false.
*/
public function has( $property ) {
return !empty( $this->_properties[ $property ]);
}
/**
* Returns the value of the given property.
*
* @param string $property Property name.
* @param mixed $default Default value to be returned in case the property
* doesn't exists.
* @return mixed The property value, or $default.
*/
public function get( $property, $default = null ) {
return isset( $this->_properties[ $property ])
? $this->_properties[ $property ]
: $default;
}
/**
* Sets the value of the given property.
*
* @param string $property Property name.
* @param string $value New value.
*/
public function set( $property, $value ) {
$this->_properties[ $property ] = $value;
}
/**
* Sets the value of a property if it is empty.
*
* @param string $property Property name.
* @param string $default Default value.
*/
public function setDefault( $property, $default ) {
if ( !$this->has( $property )) {
$this->set( $property, $default );
}
}
/**
* Sets default values.
*
* @see setDefault( )
* @param string $properties Default properties.
*/
public function setDefaults( $properties ) {
$this->_properties += $properties;
}
/**
* Returns the entire set of properties.
*
* @return array Properties.
*/
public function properties( ) {
return $this->_properties;
}
/**
* Sets the entire set of properties.
*
* @param array $properties Properties to set.
*/
public function setProperties( array $properties ) {
$this->_properties = $properties;
}
/**
* Merges the given properties with the current ones.
*
* @param array $properties Properties to merge.
*/
public function configure( array $properties ) {
$this->_properties = array_merge( $this->_properties, $properties );
}
}

View File

@@ -0,0 +1,84 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Di;
use Essence\Configurable;
use Closure;
/**
* A simple dependency injection container.
* Inspired by Pimple (https://github.com/fabpot/Pimple).
*
* @package Essence.Di
*/
class Container {
use Configurable;
/**
* Container properties.
*
* @var array
*/
protected $_properties = [ ];
/**
* Returns the value of the given property.
*
* @param string $property Property name.
* @param mixed $default Default value to be returned in case the property
* doesn't exists.
* @return mixed The property value, or the result of the closure execution
* if property is a closure, or $default.
*/
public function get( $property, $default = null ) {
$value = $default;
if ( $this->has( $property )) {
$value = $this->_properties[ $property ];
if ( $value instanceof Closure ) {
$value = $value( $this );
}
}
return $value;
}
/**
* Returns a wrapper that memoizes the result of the given closure.
*
* @param Closure $closure Closure to wrap.
* @return Closure Wrapper.
*/
public static function unique( Closure $closure ) {
return function( $Container ) use ( $closure ) {
static $result = null;
if ( $result === null ) {
$result = $closure( $Container );
}
return $result;
};
}
}

View File

@@ -0,0 +1,183 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Di\Container;
use Essence\Essence;
use Essence\Di\Container;
use Essence\Cache\Engine\Volatile as VolatileCacheEngine;
use Essence\Dom\Parser\Native as NativeDomParser;
use Essence\Http\Client\Curl as CurlHttpClient;
use Essence\Http\Client\Native as NativeHttpClient;
use Essence\Log\Logger\NullClass as NullLogger;
use Essence\Provider\Collection;
use Essence\Provider\OEmbed;
use Essence\Provider\OEmbed\Vimeo;
use Essence\Provider\OEmbed\Youtube;
use Essence\Provider\OpenGraph;
use Essence\Media\Preparator;
use Essence\Media\Preparator\Bandcamp as BandcampPreparator;
use Essence\Media\Preparator\Vine as VinePreparator;
use Essence\Media\Preparator\Youtube as YoutubePreparator;
/**
* Contains the default injection properties.
*
* @package Essence.Di.Container
*/
class Standard extends Container {
/**
* Sets the default properties.
*/
public function __construct( array $properties = [ ]) {
$this->_properties = $properties + [
// Providers are loaded from the default config file
'providers' => ESSENCE_DEFAULT_PROVIDERS,
// A volatile cache engine is shared across the application
'Cache' => Container::unique( function( ) {
return new VolatileCacheEngine( );
}),
// User agent
'HttpUserAgent' => 'Essence',
// A cURL HTTP client is shared across the application
// If cURL isn't available, a native client is used
'Http' => Container::unique( function( $C ) {
$Http = function_exists( 'curl_init' )
? new CurlHttpClient( )
: new NativeHttpClient( );
$Http->setUserAgent( $C->get( 'HttpUserAgent' ));
return $Http;
}),
// A native DOM parser is shared across the application
'Dom' => Container::unique( function( ) {
return new NativeDomParser( );
}),
// A null logger is shared across the application
'Log' => Container::unique( function( ) {
return new NullLogger( );
}),
//
'Preparator' => Container::unique( function( ) {
return new Preparator( );
}),
// The OEmbed provider uses the shared HTTP client, DOM parser
// and logger.
'OEmbed' => function( $C ) {
return new OEmbed(
$C->get( 'Http' ),
$C->get( 'Dom' ),
$C->get( 'Log' ),
$C->get( 'Preparator' )
);
},
// The Vimeo provider uses the shared HTTP client, DOM parser
// and logger.
'Vimeo' => function( $C ) {
return new Vimeo(
$C->get( 'Http' ),
$C->get( 'Dom' ),
$C->get( 'Log' ),
$C->get( 'Preparator' )
);
},
//
'YoutubePreparator' => Container::unique( function( ) {
return new YoutubePreparator( );
}),
// The Youtube provider uses the shared HTTP client, DOM parser
// and logger.
'Youtube' => function( $C ) {
return new Youtube(
$C->get( 'Http' ),
$C->get( 'Dom' ),
$C->get( 'Log' ),
$C->get( 'YoutubePreparator' )
);
},
// The OpenGraph provider uses the shared HTTP client, DOM parser
// and logger.
'OpenGraph' => function( $C ) {
return new OpenGraph(
$C->get( 'Http' ),
$C->get( 'Dom' ),
$C->get( 'Log' ),
$C->get( 'Preparator' )
);
},
//
'BandcampPreparator' => Container::unique( function( ) {
return new BandcampPreparator( );
}),
// The Bandcamp provider uses the shared HTTP client, DOM parser
// and logger.
'Bandcamp' => function( $C ) {
return new OpenGraph(
$C->get( 'Http' ),
$C->get( 'Dom' ),
$C->get( 'Log' ),
$C->get( 'BandcampPreparator' )
);
},
//
'VinePreparator' => Container::unique( function( ) {
return new VinePreparator( );
}),
// The Vine provider uses the shared HTTP client, DOM parser
// and logger.
'Vine' => function( $C ) {
return new OpenGraph(
$C->get( 'Http' ),
$C->get( 'Dom' ),
$C->get( 'Log' ),
$C->get( 'VinePreparator' )
);
},
// The provider collection uses the container
'Collection' => function( $C ) {
$Collection = new Collection( $C );
$Collection->load( $C->get( 'providers' ));
return $Collection;
},
// Essence uses the provider collection, and the shared cache engine,
// HTTP client and DOM parser.
'Essence' => function( $C ) {
return new Essence(
$C->get( 'Collection' ),
$C->get( 'Cache' ),
$C->get( 'Http' ),
$C->get( 'Dom' ),
$C->get( 'Log' )
);
}
];
}
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Dom;
/**
* Handles HTML related operations.
*
* @package Essence.Dom
*/
interface Parser {
/**
* Extracts tags attributes from the given HTML document.
*
* Getting all attributes of all img tags in the document:
*
* @code
* $attributes = Parser::extractAttributes( $html, [ 'img' ]);
* @endcode
*
* Getting src attribute of all img tags in the document:
* (if a tag doesn't have the src attribute, it will not be taken into
* account)
*
* @code
* $attributes = Parser::extractAttributes( $html, [ 'img' => 'src' ]);
* @endcode
*
* Getting src and alt attributes of all img tags in the document:
* (if a tag doesn't have the src or alt attribute, it will not be taken
* into account)
*
* @code
* $attributes = Parser::extractAttributes( $html, [
* 'img' => [
* 'src',
* 'alt'
* ]
* ]);
* @endcode
*
* Getting src attribute of all img tags in the document, where their
* src attribute matches a pattern:
* (if the src attribute of a tag doesn't match the pattern, the tag will
* not be taken into account)
*
* @code
* $attributes = Parser::extractAttributes( $html, [
* 'img' => [
* 'src' => '/foo/i',
* 'alt'
* ]
* ]);
* @endcode
*
* Example result:
*
* @code
* $attributes = Parser::extractAttributes( $html, [
* 'img' => 'src',
* 'a' => [
* 'href' => '/foo/i',
* 'alt'
* ]
* ]);
*
* => [
* 'img' => [
* [ 'src' => 'http://www.website.com/foo.png' ],
* [ 'src' => 'http://www.website.com/bar.png' ],
* ],
* 'a' => [
* [
* 'href' => 'http://www.foo.com',
* 'alt' => 'foo'
* ]
* ]
* ]
* @endcode
*
* @param string $html An HTML document.
* @param array $options Options defining which attributes to extract.
* @return array Extracted attributes indexed by tag name.
*/
public function extractAttributes( $html, array $options );
}

View File

@@ -0,0 +1,148 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Dom\Parser;
use Essence\Dom\Parser;
use Essence\Exception;
use Essence\Utility\Hash;
use DomDocument;
use DomNode;
/**
* Handles HTML related operations through DomDocument.
*
* @package Essence.Dom.Parser
*/
class Native implements Parser {
/**
* {@inheritDoc}
*/
public function extractAttributes( $html, array $options ) {
$Document = $this->_document( $html );
$options = Hash::normalize( $options, [ ]);
$data = [ ];
foreach ( $options as $name => $required ) {
$tags = $Document->getElementsByTagName( $name );
$required = Hash::normalize(( array )$required, '' );
$data[ $name ] = [ ];
foreach ( $tags as $Tag ) {
if ( $Tag->hasAttributes( )) {
$attributes = $this->_extractAttributesFromTag(
$Tag,
$required
);
if ( !empty( $attributes )) {
$data[ $name ][ ] = $attributes;
}
}
}
}
return $data;
}
/**
* Builds and returns a DomDocument from the given HTML source.
*
* @param string $html HTML source.
* @return DomDocument DomDocument.
*/
protected function _document( $html ) {
$reporting = error_reporting( 0 );
$html = $this->fixCharset( $html );
$Document = DomDocument::loadHTML( $html );
error_reporting( $reporting );
if ( !$Document ) {
throw new Exception( 'Unable to load HTML document.' );
}
return $Document;
}
/**
* If necessary, fixes the given HTML's charset to work with the current
* version of Libxml (used by DomDocument). Older versions of Libxml
* recognize only
*
* <META http-equiv="Content-Type" content="text/html; charset=UTF-8">
*
* from HTML4, and not the new HTML5 form:
*
* <meta charset="utf-8">
*
* with the result that parsed strings can have funny characters.
*
* @param string $html HTML source.
* @return string the fixed HTML
* @see "HTML5, character encodings and DOMDocument loadHTML and loadHTMLFile"
* http://www.glenscott.co.uk/blog/html5-character-encodings-and-domdocument-loadhtml-and-loadhtmlfile/
*/
protected function fixCharset( $html ) {
// The fix is from https://github.com/glenscott/dom-document-charset/blob/master/DOMDocumentCharset.php
if ( LIBXML_VERSION < 20800 && stripos($html, 'meta charset') !== false ) {
$html = preg_replace( '/<meta charset=["\']?([^"\']+)"/i',
'<meta http-equiv="Content-Type" content="text/html; charset=$1"',
$html );
}
return $html;
}
/**
* Extracts attributes from the given tag.
*
* @param DOMNode $Tag Tag to extract attributes from.
* @param array $required Required attributes.
* @return array Extracted attributes.
*/
protected function _extractAttributesFromTag( DOMNode $Tag, array $required ) {
$attributes = [ ];
foreach ( $Tag->attributes as $name => $Attribute ) {
if ( !empty( $required )) {
if ( isset( $required[ $name ])) {
$pattern = $required[ $name ];
if ( $pattern && !preg_match( $pattern, $Attribute->value )) {
return [ ];
}
} else {
continue;
}
}
$attributes[ $name ] = $Attribute->value;
}
$diff = array_diff_key( $required, $attributes );
return empty( $diff )
? $attributes
: [ ];
}
}

View File

@@ -0,0 +1,344 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence;
use Essence\Cacheable;
use Essence\Configurable;
use Essence\Di\Container\Standard as StandardContainer;
use Essence\Cache\Engine as CacheEngine;
use Essence\Dom\Parser as DomParser;
use Essence\Http\Client as HttpClient;
use Essence\Log\Logger;
use Essence\Provider\Collection;
use Essence\Exception;
/**
* Gathers embed informations from URLs.
*
* @package Essence
*/
class Essence {
use Cacheable;
use Configurable;
/**
* A collection of providers to query.
*
* @var Essence\ProviderCollection
*/
protected $_Collection = null;
/**
* Internal HTTP client.
*
* @var Essence\Http\Client
*/
protected $_Http = null;
/**
* Internal DOM parser.
*
* @var Essence\Dom\Parser
*/
protected $_Dom = null;
/**
* Internal Logger.
*
* @var Essence\Log\Logger
*/
protected $_Logger = null;
/**
* Configuration options.
*
* ### Options
*
* - 'urlPattern' string A pattern to match URLs.
*
* @var array
*/
protected $_properties = [
// http://daringfireball.net/2010/07/improved_regex_for_matching_urls
'urlPattern' =>
'#
(?<url>
(?<!=["\'])
(?:https?:)//
(?:www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)?
(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+
(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'"\.,<>?«»“”‘’])
)
#ix'
];
/**
* Constructor.
*
* @param Essence\ProviderCollection $Collection Provider collection.
* @param Essence\Cache\Engine $Cache Cache engine.
* @param Essence\Http\Client $Http HTTP client.
* @param Essence\Dom\Parser $Cache DOM parser.
* @param Essence\Log\Logger $Logger Logger.
*/
public function __construct(
Collection $Collection,
CacheEngine $Cache,
HttpClient $Http,
DomParser $Dom,
Logger $Logger
) {
$this->_Collection = $Collection;
$this->_Cache = $Cache;
$this->_Http = $Http;
$this->_Dom = $Dom;
$this->_Logger = $Logger;
}
/**
* Builds a fully configured instance of Essence.
*
* @param array $configuration Dependency injection configuration.
* @return Essence\Essence Essence instance.
*/
public static function instance( array $configuration = [ ]) {
$Container = new StandardContainer( $configuration );
return $Container->get( 'Essence' );
}
/**
* Extracts embeddable URLs from either an URL or an HTML source.
*
* @param string $source The URL or HTML source to be extracted.
* @return array An array of extracted URLs.
*/
public function extract( $source ) {
return $this->_cached( '_extract', $source );
}
/**
* Implementation of the extract method.
*
* @see extract( )
* @param string $source The URL or HTML source to be extracted.
* @return array An array of extracted URLs.
*/
protected function _extract( $source ) {
if ( filter_var( $source, FILTER_VALIDATE_URL )) {
try {
$source = $this->_Http->get( $source );
} catch ( Exception $Exception ) {
$this->_Logger->log(
Logger::notice,
"Unable to fetch $source",
[ 'exception' => $Exception ]
);
return [ ];
}
}
$urls = $this->_extractUrls( $source );
$embeddable = [ ];
foreach ( $urls as $url ) {
if ( $this->_Collection->hasProvider( $url )) {
$embeddable[ ] = $url;
}
}
return array_unique( $embeddable );
}
/**
* Extracts URLs from an HTML source.
*
* @param string $html The HTML source to extract URLs from.
* @return array Extracted URLs.
*/
protected function _extractUrls( $html ) {
$options = [
'a' => 'href',
'embed' => 'src',
'iframe' => 'src'
];
try {
$attributes = $this->_Dom->extractAttributes( $html, $options );
} catch ( Exception $Exception ) {
$this->_Logger->log(
Logger::notice,
'Error parsing HTML source',
[ 'exception' => $Exception, 'html' => $html ]
);
return [ ];
}
$urls = [ ];
foreach ( $options as $tagName => $attributeName ) {
foreach ( $attributes[ $tagName ] as $tag ) {
$urls[ ] = $tag[ $attributeName ];
}
}
return $urls;
}
/**
* Fetches embed informations from the given URL.
*
* This method now supports an array of options that can be interpreted
* at will by the providers.
*
* Thanks to Peter Niederlag (https://github.com/t3dev) for his request
* (https://github.com/felixgirault/essence/pull/1).
*
* @param string $url URL to fetch informations from.
* @param array $options Custom options to be interpreted by a provider.
* @return Essence\Media Embed informations.
*/
public function embed( $url, array $options = [ ]) {
return $this->_cached( '_embed', $url, $options );
}
/**
* Implementation of the embed method.
*
* @see embed( )
* @param string $url URL to fetch informations from.
* @param array $options Custom options to be interpreted by a provider.
* @return Essence\Media Embed informations.
*/
protected function _embed( $url, array $options ) {
$providers = $this->_Collection->providers( $url );
$Media = null;
foreach ( $providers as $Provider ) {
if ( $Media = $Provider->embed( $url, $options )) {
break;
}
}
return $Media;
}
/**
* Fetches embed informations from the given URLs.
*
* @param array $urls An array of URLs to fetch informations from.
* @param array $options Custom options to be interpreted by a provider.
* @return array An array of embed informations, indexed by URL.
*/
public function embedAll( array $urls, array $options = [ ]) {
$medias = [ ];
foreach ( $urls as $url ) {
$medias[ $url ] = $this->embed( $url, $options );
}
return $medias;
}
/**
* Replaces URLs in the given text by media informations if they point on
* an embeddable resource.
* By default, links will be replaced by the html property of Media.
* If $callback is a callable function, it will be used to generate
* replacement strings, given a Media object.
*
* @code
* $text = $Essence->replace( $text, function( $Media ) {
* return '<div class="title">' . $Media->title . '</div>';
* });
* @endcode
*
* This behavior should make it easy to integrate third party templating
* engines.
* The pattern to match urls can be configured using the 'urlPattern'
* configuration option.
*
* Thanks to Stefano Zoffoli (https://github.com/stefanozoffoli) for his
* idea (https://github.com/felixgirault/essence/issues/4).
*
* @param string $text Text in which to replace URLs.
* @param callable $callback Templating callback.
* @param array $options Custom options to be interpreted by a provider.
* @return string Text with replaced URLs.
*/
public function replace( $text, $callback = null, array $options = [ ]) {
return preg_replace_callback(
$this->urlPattern,
function ( $matches ) use ( $callback, $options ) {
if ( $Media = $this->embed( $matches['url'], $options )) {
return is_callable( $callback )
? call_user_func( $callback, $Media )
: $Media->get( 'html' );
}
return $matches['url'];
},
$text
);
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence;
use Exception as NativeException;
/**
* The base exception class of the Essence API.
*
* @package Essence
*/
class Exception extends NativeException {
/**
* Wraps a native PHP exception.
*
* @param NativeException Native exception.
* @return Exception Essence exception.
*/
public static function wrap( NativeException $Exception ) {
return new Exception(
$Exception->getMessage( ),
$Exception->getCode( ),
$Exception
);
}
/**
* An alias to fit the Essence coding style.
* I'm probably mad.
*
* @return string The exception message.
*/
public function message( ) {
return $this->getMessage( );
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Http;
/**
* Handles HTTP related operations.
*
* @package Essence.Http
*/
abstract class Client {
/**
* User agent.
*
* @var string
*/
protected $_userAgent = '';
/**
* Retrieves contents from the given URL.
*
* @param string $url The URL fo fetch contents from.
* @return string The contents.
* @throws Essence\Http\Exception
*/
abstract public function get( $url );
/**
* Sets the user agent for HTTP requests.
*
* @param string $userAgent User agent.
*/
public function setUserAgent( $userAgent ) {
$this->_userAgent = $userAgent;
}
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Http\Client;
use Essence\Http\Client;
use Essence\Http\Exception;
/**
* Handles HTTP related operations through cURL.
*
* @package Essence.Http.Client
*/
class Curl extends Client {
/**
* CURL handle.
*
* @var resource
*/
protected $_curl = null;
/**
* Default cURL options, takes precedence over the user options.
*
* @var array
*/
protected $_defaults = [
CURLOPT_HEADER => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true
];
/**
* Initializes cURL with the given options.
*
* @param array cURL options.
*/
public function __construct( array $options = [ ]) {
$this->_curl = curl_init( );
curl_setopt_array(
$this->_curl,
$this->_defaults + $options
);
}
/**
* Closes cURL connexion.
*/
public function __destruct( ) {
curl_close( $this->_curl );
}
/**
* {@inheritDoc}
*/
public function get( $url ) {
curl_setopt( $this->_curl, CURLOPT_URL, $url );
curl_setopt( $this->_curl, CURLOPT_USERAGENT, $this->_userAgent );
$contents = curl_exec( $this->_curl );
if ( $contents === false ) {
throw new Exception(
$url,
curl_getinfo( $this->_curl, CURLINFO_HTTP_CODE )
);
}
return $contents;
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Http\Client;
use Essence\Http\Client;
use Essence\Http\Exception;
/**
* Handles HTTP related operations through file_get_contents( ).
*
* @package Essence.Http.Client
*/
class Native extends Client {
/**
* Default HTTP status code.
*
* @var int
*/
protected $_defaultCode;
/**
* Constructor.
*
* @param int $defaultCode The default HTTP status code to assume if
* response headers cannot be parsed.
*/
public function __construct( $defaultCode = 404 ) {
$this->_defaultCode = $defaultCode;
}
/**
* Retrieves contents from the given URL.
* Thanks to Diije for the hint on $http_response_header
* (http://www.felix-girault.fr/astuces/recuperer-une-page-web-en-php/#comment-1029).
*
* @param string $url The URL fo fetch contents from.
* @return string The fetched contents.
* @throws Essence\Http\Exception
*/
public function get( $url ) {
$options = [
'http' => [
'user_agent' => $this->_userAgent
]
];
$context = stream_context_create( $options );
$reporting = error_reporting( 0 );
$contents = file_get_contents( $url, false, $context );
error_reporting( $reporting );
if ( $contents === false ) {
$code = $this->_defaultCode;
if ( isset( $http_response_header )) {
preg_match(
'#^HTTP/[0-9\.]+\s(?P<code>[0-9]+)#i',
$http_response_header[ 0 ],
$matches
);
if ( isset( $matches['code'])) {
$code = $matches['code'];
}
}
// let's assume the file doesn't exists
throw new Exception( $url, $code );
}
return $contents;
}
}

View File

@@ -0,0 +1,107 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Http;
use Essence\Exception as EssenceException;
/**
* An HTTP related exception.
*
* @package Essence.Http
*/
class Exception extends EssenceException {
/**
* Error URL.
*
* @var string
*/
protected $_url = '';
/**
* Messages corresponding to HTTP codes.
* Thanks to Hinnerk Brügmann
* (http://www.multiasking.com/2011/05/http-error-codes-as-php-array/).
*
* @var array
*/
protected $_messages = [
// Client errors
400 => 'Bad request',
401 => 'Unauthorized',
402 => 'Payment required',
403 => 'Forbidden',
404 => 'Not found',
405 => 'Method not allowed',
406 => 'Not acceptable',
407 => 'Proxy authentication required',
408 => 'Request timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length required',
412 => 'Precondition failed',
413 => 'Request entity too large',
414 => 'Request-URL too long',
415 => 'Unsupported media type',
416 => 'Requested range not satisfiable',
417 => 'Expectation failed',
// Server errors
500 => 'Internal server error',
501 => 'Not implemented',
502 => 'Bad gateway',
503 => 'Service unavailable',
504 => 'Gateway timeout',
505 => 'HTTP version not supported'
];
/**
* Constructs the exception with the given HTTP status code, and the URL
* that triggered the error.
*
* @param string $url URL.
* @param int $code HTTP status code.
* @param Exception $Previous Previous exception.
*/
public function __construct( $url, $code = 0, Exception $Previous = null ) {
$this->_url = $url;
parent::__construct(
isset( $this->_messages[ $code ])
? $this->_messages[ $code ]
: 'HTTP error',
$code,
$Previous
);
}
/**
* Returns the URL that triggered the error.
*
* @return string URL.
*/
public function url( ) {
return $this->_url;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Log;
/**
* A very basic logger.
* Inspired by PSR log (https://github.com/php-fig/log).
*
* @package Essence.Log
*/
interface Logger {
/**
* Log level for normal but significant events.
*
* @var string
*/
const notice = 'notice';
/**
* Logs a message.
*
* @param mixed $level Level.
* @param string $message Message.
* @param array $context Context.
*/
public function log( $level, $message, array $context = [ ]);
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Log\Logger;
use Essence\Log\Logger;
/**
* Does absolutely nothing.
*
* @package Essence.Log.Logger
*/
class NullClass implements Logger {
/**
* {@inheritDoc}
*/
public function log( $level, $message, array $context = [ ]) { }
}

View File

@@ -0,0 +1,144 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence;
use ArrayIterator;
use IteratorAggregate;
use JsonSerializable;
use Essence\Configurable;
/**
* Stores informations about an embed response.
* This class is useful to ensure that any response from any provider will
* follow the same conventions.
*
* @package Essence
*/
class Media implements IteratorAggregate, JsonSerializable {
use Configurable;
/**
* Embed data, indexed by property name. Providers must try to fill these
* default properties with appropriate data before adding their own, to
* ensure consistency accross the API.
*
* These default properties are gathered from the OEmbed and OpenGraph
* protocols, and provide all the basic informations needed to embed a
* media.
*
* @var array
*/
protected $_properties = [
// OEmbed type
// OG type
'type' => '',
// OEmbed version
'version' => '',
// OEmbed title
// OG title
'title' => '',
// Sometimes provided in OEmbed (i.e. Vimeo)
// OG description
'description' => '',
// OEmbed author_name
'authorName' => '',
// OEmbed author_url
'authorUrl' => '',
// OEmbed provider_name
// OG site_name
'providerName' => '',
// OEmbed provider_url
'providerUrl' => '',
// OEmbed cache_age
'cacheAge' => '',
// OEmbed thumbnail_url
// OG image
// OG image:url
'thumbnailUrl' => '',
// OEmbed thumbnail_width
'thumbnailWidth' => '',
// OEmbed thumbnail_height
'thumbnailHeight' => '',
// OEmbed html
'html' => '',
// OEmbed width
// OG image:width
// OG video:width
'width' => '',
// OEmbed height
// OG image:height
// OG video:height
'height' => '',
// OEmbed url
// OG url
'url' => ''
];
/**
* Constructs a Media from the given dataset.
*
* @see $properties
* @param array $properties An array of media informations.
*/
public function __construct( array $properties ) {
$this->configure( $properties );
}
/**
* Returns an iterator for the media properties.
*
* @return ArrayIterator Iterator.
*/
public function getIterator( ) {
return new ArrayIterator( $this->_properties );
}
/**
* Returns serialized properties.
*
* @return string JSON representation.
*/
public function jsonSerialize( ) {
return $this->_properties;
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Media;
use Essence\Media;
/**
*
*
* @package Essence.Media
*/
class Preparator {
/**
*
*/
protected $_defaults = [
'width' => 640,
'height' => 490
];
/**
* Builds an HTML code from the given media's properties to fill its
* 'html' property.
*
* @param Essence\Media $Media A reference to the Media.
* @param array $options Options.
*/
public function complete( Media $Media, array $options = [ ]) {
if ( $Media->has( 'html' )) {
return;
}
$title = htmlspecialchars( $Media->get( 'title', $Media->url ));
$description = $Media->has( 'description' )
? htmlspecialchars( $Media->description )
: $title;
$options += $this->_defaults;
$width = $Media->setDefault( 'width', $options['width']);
$height = $Media->setDefault( 'height', $options['height']);
switch ( $Media->type ) {
// builds an <img> tag pointing to the photo
case 'photo':
$Media->set( 'html', sprintf(
'<img src="%s" alt="%s" width="%d" height="%d" />',
$Media->url,
$description,
$width,
$height
));
break;
// builds an <iframe> tag pointing to the video
case 'video':
$Media->set( 'html', sprintf(
'<iframe src="%s" width="%d" height="%d" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen />',
$Media->url,
$width,
$height
));
break;
// builds an <a> tag pointing to the original resource
default:
$Media->set( 'html', sprintf(
'<a href="%s" alt="%s">%s</a>',
$Media->url,
$description,
$title
));
break;
}
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Media\Preparator;
use Essence\Media\Preparator;
use Essence\Media;
/**
* Builds an HTML code for the Bandcamp player.
*
* @package Essence.Media.Preparator
*/
class Bandcamp extends Preparator {
/**
* {@inheritDoc}
*/
public function complete( Media $Media, array $options = [ ]) {
parent::complete( $Media, $options );
if (
$Media->has( 'og:video' )
&& ( $Media->get( 'og:video:type') === 'application/x-shockwave-flash' )
&& preg_match( '/((album|track)=\d+)/', $Media->get( 'og:video' ), $matches )
) {
$url = htmlspecialchars( $Media->get( 'url' ));
$title = htmlspecialchars( $Media->get( 'title' ));
$height = ( $matches[ 2 ] == 'album' )
? 470
: 442;
$Media->set( 'html:small', '<iframe style="border: 0; width: 100%; height: 42px;" src="http://bandcamp.com/EmbeddedPlayer/' . $matches[ 1 ] . '/size=small/bgcol=ffffff/linkcol=0687f5/transparent=true/" seamless><a href="' . htmlspecialchars($Media->get('url')) . '">' . $title . '</a></iframe>' );
$Media->set( 'html:medium', '<iframe style="border: 0; width: 100%; height: 120px;" sr="http://bandcamp.com/EmbeddedPlayer/' . $matches[ 1 ] . '/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=false/artwork=small/transparent=true/" seamless><a href="' . $url . '">' . $title . '</a></iframe>' );
$Media->set( 'html:large', '<iframe style="border: 0; width: 350px; height: ' . $height . 'px;" src="http://bandcamp.com/EmbeddedPlayer/' . $matches[ 1 ] . '/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=false/transparent=true/" seamless><a href="' . $url . '">' . $title . '</a></iframe>' );
$Media->set('html', $Media->get( 'html:small' ));
}
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Media\Preparator;
use Essence\Media\Preparator;
use Essence\Media;
/**
* Builds an HTML code for the Vine player.
*
* @package Essence.Media.Preparator
*/
class Vine extends Preparator {
/**
* {@inheritDoc}
*/
public function complete( Media $Media, array $options = [ ]) {
parent::complete( $Media, $options );
if (
( $Media->get( 'type' ) === 'vine-app:video' )
&& preg_match( '#https?://vine.co/v/[a-zA-Z0-9]+#i', $Media->get( 'url' ), $matches )
) {
$Media->set( 'html:small', '<iframe class="vine-embed" src="' . $matches[ 0 ] . '/embed/postcard" width="320" height="320" frameborder="0"></iframe><script async src="//platform.vine.co/static/scripts/embed.js" charset="utf-8"></script>' );
$Media->set( 'html:medium', '<iframe class="vine-embed" src="' . $matches[ 0 ] . '/embed/postcard" width="480" height="480" frameborder="0"></iframe><script async src="//platform.vine.co/static/scripts/embed.js" charset="utf-8"></script>' );
$Media->set( 'html:large', '<iframe class="vine-embed" src="' . $matches[ 0 ] . '/embed/postcard" width="600" height="600" frameborder="0"></iframe><script async src="//platform.vine.co/static/scripts/embed.js" charset="utf-8"></script>' );
$Media->set( 'html', $Media->get( 'html:small' ));
}
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Media\Preparator;
use Essence\Media\Preparator;
use Essence\Media;
/**
* Handles custom thumbnail formats.
*
* @package Essence.Media.Preparator
*/
class Youtube extends Preparator {
/**
* {@inheritDoc}
*
* @param array $options Embed options.
* - 'thumbnailFormat' string
*/
public function complete( Media $Media, array $options = [ ]) {
parent::complete( $Media, $options );
if ( isset( $options['thumbnailFormat'])) {
$url = $Media->get( 'thumbnailUrl' );
switch ( $options['thumbnailFormat']) {
case 'small':
$url = str_replace( 'hqdefault', 'default', $url );
break;
case 'medium':
$url = str_replace( 'hqdefault', 'mqdefault', $url );
break;
// CAUTION!
// this thumbnail format might not be available for all videos
case 'max':
$url = str_replace( 'hqdefault', 'maxresdefault', $url );
break;
case 'large':
default:
// unchanged
break;
}
$Media->set( 'thumbnailUrl', $url );
}
}
}

View File

@@ -0,0 +1,143 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence;
use Essence\Configurable;
use Essence\Exception;
use Essence\Media;
use Essence\Media\Preparator;
use Essence\Log\Logger;
/**
* Base class for a Provider.
*
* @package Essence
*/
abstract class Provider {
use Configurable;
/**
* Internal logger.
*
* @var Essence\Log\Logger
*/
protected $_Logger = null;
/**
* Media preparator.
*
* @var Essence\Media\Preparator
*/
protected $_Preparator = null;
/**
* Configuration options.
*
* ### Options
*
* - 'prepare' callable( string $url ) A function to prepare the given URL.
*
* @var array
*/
protected $_properties = [
'prepare' => 'self::prepareUrl'
];
/**
* Constructor.
*
* @param Essence\Log\Logger $Logger Logger.
* @param Essence\Log\Preparator $Preparator Preparator.
*/
public function __construct( Logger $Logger, Preparator $Preparator = null ) {
$this->_Logger = $Logger;
$this->_Preparator = $Preparator;
}
/**
* Fetches embed information from the given URL.
*
* @param string $url URL to fetch informations from.
* @param array $options Custom options to be interpreted by the provider.
* @return Media|null Embed informations, or null if nothing could be
* fetched.
*/
public final function embed( $url, array $options = [ ]) {
$Media = null;
if ( is_callable( $this->prepare )) {
$url = call_user_func( $this->prepare, $url, $options );
}
try {
$Media = $this->_embed( $url, $options );
$Media->setDefault( 'url', $url );
if ( $this->_Preparator ) {
$this->_Preparator->complete( $Media, $options );
}
} catch ( Exception $Exception ) {
$this->_Logger->log(
Logger::notice,
"Unable to embed $url",
[ 'exception' => $Exception ]
);
}
return $Media;
}
/**
* Does the actual fetching of informations.
*
* @param string $url URL to fetch informations from.
* @param array $options Custom options to be interpreted by the provider.
* @return Media Embed informations.
* @throws Essence\Exception
*/
abstract protected function _embed( $url, array $options );
/**
* Trims and returns the given string.
*
* @param string $url URL.
* @param array $options Embed options.
* @return string Trimmed URL.
*/
public static function prepareUrl( $url, array $options = [ ]) {
return trim( $url );
}
}

View File

@@ -0,0 +1,188 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Provider;
use Essence\Configurable;
use Essence\Di\Container;
use Essence\Exception;
/**
* A collection of providers which can find the provider of an url.
*
* @package Essence.Provider
*/
class Collection {
use Configurable;
/**
* Dependency injection container.
*
* @var Essence\Di\Container
*/
protected $_Container = null;
/**
* A list of provider configurations.
*
* ### Options
*
* - 'name' string Name of the provider.
* - 'class' string The provider class.
* - 'filter' string|callable A regex or callback to filter URLs
* that will be processed by the provider.
* - ... mixed Provider specific options.
*
* @var array
*/
protected $_properties = [ ];
/**
* A list of providers.
*
* @var array
*/
protected $_providers = [ ];
/**
* Constructor.
*
* @param Essence\Di\Container $Container Dependency injection container
* used to build providers.
*/
public function __construct( Container $Container ) {
$this->_Container = $Container;
}
/**
* Loads configuration from an array or a file.
*
* @throws Essence\Exception If the configuration is not an array.
* @param array|string $config A configuration array, or a configuration
* file returning such an array.
*/
public function load( $config ) {
if ( is_string( $config ) && file_exists( $config )) {
$config = include $config;
}
if ( !is_array( $config )) {
throw new Exception(
'The configuration must be an array.'
);
}
$this->configure( $config );
}
/**
* Tells if a provider was found for the given url.
*
* @param string $url An url which may be embedded.
* @return mixed The url provider if any, otherwise null.
*/
public function hasProvider( $url ) {
foreach ( $this->_properties as $config ) {
if ( $this->_matches( $config['filter'], $url )) {
return true;
}
}
return false;
}
/**
* Finds providers of the given url.
*
* @todo Use PHP generators to yield providers.
* @param string $url An url which may be embedded.
* @return array An array of Essence\Provider.
*/
public function providers( $url ) {
$providers = [ ];
foreach ( $this->_properties as $name => $config ) {
if ( $this->_matches( $config['filter'], $url )) {
$providers[ ] = $this->_provider( $name, $config );
}
}
return $providers;
}
/**
* Tells if an URL matches a filter.
*
* @param string|callable $filter Regex or callback to filter URL.
* @param string $url URL to filter.
* @return Whether the URL matches the filter or not.
*/
protected function _matches( $filter, $url ) {
return is_callable( $filter )
? call_user_func( $filter, $url )
: preg_match( $filter, $url );
}
/**
* Lazy loads a provider given its name and configuration.
*
* @param string $name Name.
* @param string $config Configuration.
* @return Provider Instance.
*/
protected function _provider( $name, $config ) {
if ( !isset( $this->_providers[ $name ])) {
$class = $config['class'];
$Provider = $this->_Container->has( $class )
? $this->_Container->get( $class )
: new $class( );
$Provider->configure( $config );
$this->_providers[ $name ] = $Provider;
}
return $this->_providers[ $name ];
}
}

View File

@@ -0,0 +1,267 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Provider;
use Essence\Exception;
use Essence\Media;
use Essence\Media\Preparator;
use Essence\Provider;
use Essence\Dom\Parser as DomParser;
use Essence\Http\Client as HttpClient;
use Essence\Log\Logger;
use Essence\Utility\Hash;
use Essence\Utility\Json;
use Essence\Utility\Xml;
/**
* Base class for an OEmbed provider.
* This kind of provider extracts embed informations through the OEmbed protocol.
*
* @package Essence.Provider
*/
class OEmbed extends Provider {
/**
* JSON response format.
*
* @var string
*/
const json = 'json';
/**
* XML response format.
*
* @var string
*/
const xml = 'xml';
/**
* Internal HTTP client.
*
* @var Essence\Http\Client
*/
protected $_Http = null;
/**
* Internal DOM parser.
*
* @var Essence\Dom\Parser
*/
protected $_Dom = null;
/**
* ### Options
*
* - 'endpoint' string The OEmbed endpoint.
* - 'format' string The expected response format.
*/
protected $_properties = [
'prepare' => 'static::prepareUrl',
'endpoint' => '',
'format' => self::json
];
/**
* Constructor.
*
* @param Essence\Http\Client $Http HTTP client.
* @param Essence\Dom\Parser $Dom DOM parser.
* @param Essence\Log\Logger $Log Logger.
* @param Essence\Log\Preparator $Preparator Preparator.
*/
public function __construct(
HttpClient $Http,
DomParser $Dom,
Logger $Log,
Preparator $Preparator = null
) {
$this->_Http = $Http;
$this->_Dom = $Dom;
parent::__construct( $Log, $Preparator );
}
/**
* Strips arguments and anchors from the given URL.
*
* @param string $url Url to prepare.
* @return string Prepared url.
*/
public static function prepareUrl( $url, array $options = [ ]) {
$url = trim( $url );
if ( !self::strip( $url, '?' )) {
self::strip( $url, '#' );
}
return $url;
}
/**
* Strips the end of a string after a delimiter.
*
* @param string $string The string to strip.
* @param string $delimiter The delimiter from which to strip the string.
* @return boolean True if the string was modified, otherwise false.
*/
public static function strip( &$string, $delimiter ) {
$position = strrpos( $string, $delimiter );
$found = ( $position !== false );
if ( $found ) {
$string = substr( $string, 0, $position );
}
return $found;
}
/**
* {@inheritDoc}
*
* @note If no endpoint was specified in the configuration, the page at
* the given URL will be parsed to find one.
* @throws Essence\Exception If the parsed page doesn't provide any endpoint.
*/
protected function _embed( $url, array $options ) {
if ( $this->endpoint ) {
$endpoint = sprintf( $this->endpoint, urlencode( $url ));
$format = $this->format;
} else if ( !$this->_extractEndpoint( $url, $endpoint, $format )) {
throw new Exception(
"Unable to extract any endpoint from '$url'."
);
}
if ( $options ) {
$this->_completeEndpoint( $endpoint, $options );
}
return $this->_embedEndpoint( $endpoint, $format );
}
/**
* Extracts an oEmbed endpoint from the given URL.
*
* @param string $url URL from which to extract an endpoint.
* @param string $endpoint The extracted endpoint.
* @param string $format The extracted format.
* @return boolean If an endpoint was extracted.
*/
protected function _extractEndpoint( $url, &$endpoint, &$format ) {
$attributes = $this->_Dom->extractAttributes( $this->_Http->get( $url ), [
'link' => [
'rel' => '#alternate#i',
'type',
'href'
]
]);
foreach ( $attributes['link'] as $link ) {
if ( preg_match( '#(?<format>json|xml)#i', $link['type'], $matches )) {
$endpoint = $link['href'];
$format = $matches['format'];
return true;
}
}
return false;
}
/**
* Appends a set of options as parameters to the given endpoint URL.
*
* @param string $endpoint Endpoint URL.
* @param array $options Options to append.
*/
protected function _completeEndpoint( &$endpoint, $options ) {
if ( $options ) {
$endpoint .= ( strrpos( $endpoint, '?' ) === false ) ? '?' : '&';
$endpoint .= http_build_query( $options );
}
}
/**
* Fetches embed information from the given endpoint.
*
* @param string $endpoint Endpoint to fetch informations from.
* @param string $format Response format.
* @return Media Embed informations.
*/
protected function _embedEndpoint( $endpoint, $format ) {
$response = $this->_Http->get( $endpoint );
switch ( $format ) {
case self::json:
$data = Json::parse( $response );
break;
case self::xml:
$data = Xml::parse( $response );
break;
default:
throw new Exception( 'Unsupported response format.' );
}
return new Media(
Hash::reindex( $data, [
'author_name' => 'authorName',
'author_url' => 'authorUrl',
'provider_name' => 'providerName',
'provider_url' => 'providerUrl',
'cache_age' => 'cacheAge',
'thumbnail_url' => 'thumbnailUrl',
'thumbnail_width' => 'thumbnailWidth',
'thumbnail_height' => 'thumbnailHeight'
])
);
}
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Provider\OEmbed;
use Essence\Provider\OEmbed;
/**
*
*
* @package Essence.Provider.OEmbed
*/
class Vimeo extends OEmbed {
/**
* Refactors URLs like these:
* - http://player.vimeo.com/video/20830433
*
* in such form:
* - http://www.vimeo.com/20830433
*
* @param string $url Url to prepare.
* @return string Prepared url.
*/
public static function prepareUrl( $url, array $options = [ ]) {
$url = parent::prepareUrl( $url );
if ( preg_match( '#player\.vimeo\.com/video/(?<id>[0-9]+)#i', $url, $matches )) {
$url = 'http://www.vimeo.com/' . $matches['id'];
}
return $url;
}
}

View File

@@ -0,0 +1,46 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Provider\OEmbed;
use Essence\Media;
use Essence\Provider\OEmbed;
/**
*
* @package Essence.Provider.OEmbed
*/
class Youtube extends OEmbed {
/**
* Refactors URLs like these:
* - http://www.youtube.com/watch?v=oHg5SJYRHA0&noise=noise
* - http://www.youtube.com/v/oHg5SJYRHA0
* - http://www.youtube.com/embed/oHg5SJYRHA0
* - http://youtu.be/oHg5SJYRHA0
*
* in such form:
* - http://www.youtube.com/watch?v=oHg5SJYRHA0
*
* @param string $url Url to prepare.
* @return string Prepared url.
*/
public static function prepareUrl( $url, array $options = [ ]) {
$url = trim( $url );
if ( preg_match( '#(?:v=|v/|embed/|youtu\.be/)(?<id>[a-z0-9_-]+)#i', $url, $matches )) {
$url = 'http://www.youtube.com/watch?v=' . $matches['id'];
}
return $url;
}
}

View File

@@ -0,0 +1,132 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @author Laughingwithu <laughingwithu@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Provider;
use Essence\Exception;
use Essence\Media;
use Essence\Media\Preparator;
use Essence\Provider;
use Essence\Dom\Parser as DomParser;
use Essence\Http\Client as HttpClient;
use Essence\Log\Logger;
use Essence\Utility\Hash;
/**
* Base class for an OpenGraph provider.
* This kind of provider extracts embed informations from OpenGraph meta tags.
*
* @package Essence.Provider
*/
class OpenGraph extends Provider {
/**
* Internal HTTP client.
*
* @var Essence\Http\Client
*/
protected $_Http = null;
/**
* Internal DOM parser.
*
* @var Essence\Dom\Parser
*/
protected $_Dom = null;
/**
* Constructor.
*
* @param Essence\Http\Client $Http HTTP client.
* @param Essence\Dom\Parser $Dom DOM parser.
* @param Essence\Log\Logger $Log Logger.
* @param Essence\Log\Preparator $Preparator Preparator.
*/
public function __construct(
HttpClient $Http,
DomParser $Dom,
Logger $Log,
Preparator $Preparator = null
) {
$this->_Http = $Http;
$this->_Dom = $Dom;
parent::__construct( $Log, $Preparator );
}
/**
* {@inheritDoc}
*/
protected function _embed( $url, array $options ) {
return new Media(
Hash::reindex( $this->_extractInformations( $url ), [
'og:type' => 'type',
'og:title' => 'title',
'og:description' => 'description',
'og:site_name' => 'providerName',
'og:image' => 'thumbnailUrl',
'og:image:url' => 'thumbnailUrl',
'og:image:width' => 'width',
'og:image:height' => 'height',
'og:video:width' => 'width',
'og:video:height' => 'height',
'og:url' => 'url'
])
);
}
/**
* Extracts OpenGraph informations from the given URL.
*
* @param string $url URL to fetch informations from.
* @return array Extracted informations.
*/
protected function _extractInformations( $url ) {
$attributes = $this->_Dom->extractAttributes( $this->_Http->get( $url ), [
'meta' => [
'property' => '#^og:.+#i',
'content'
]
]);
$og = [ ];
if ( empty( $attributes['meta'])) {
throw new Exception(
"Unable to extract OpenGraph data from '$url'."
);
} else {
foreach ( $attributes['meta'] as $meta ) {
if ( !isset( $og[ $meta['property']])) {
$og[ $meta['property']] = trim( $meta['content']);
}
}
}
return $og;
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Utility;
/**
* A simple PSR-0 compliant class loader.
*
* @package Essence.Utility
*/
class Autoload {
/**
* Sets autoload up on the given path.
*
* @param string $basePath Base include path for all class files.
*/
public static function setup( $basePath ) {
$basePath = rtrim( $basePath, DIRECTORY_SEPARATOR );
spl_autoload_register( function( $className ) use ( $basePath ) {
if (strpos($className, 'Essence\\') === false) {
return;
}
$path = $basePath
. DIRECTORY_SEPARATOR
. str_replace( '\\', DIRECTORY_SEPARATOR, $className )
. '.php';
if ( file_exists( $path )) {
require_once $path;
}
});
}
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Utility;
/**
* An utility class to manipulate data sets.
*
* @package Essence.Utility
*/
class Hash {
/**
* Reindexes an array, according to the given correspondances.
*
* @param array $data The data to be reindexed.
* @param array $correspondances An array of index correspondances of the
* form `array( 'currentIndex' => 'newIndex' )`.
* @return array Reindexed array.
*/
public static function reindex( array $data, array $correspondances ) {
$result = $data;
foreach ( $correspondances as $from => $to ) {
if ( isset( $data[ $from ])) {
$result[ $to ] = $data[ $from ];
}
}
return $result;
}
/**
* Every element that is numerically indexed becomes a key, given
* $default as value.
*
* @param array $data The array to normalize.
* @param mixed $default Default value.
* @return array The normalized array.
*/
public static function normalize( array $data, $default ) {
$normalized = [ ];
foreach ( $data as $key => $value ) {
if ( is_numeric( $key )) {
$key = $value;
$value = $default;
}
$normalized[ $key ] = $value;
}
return $normalized;
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Utility;
use Essence\Exception;
/**
* A simple JSON parser.
*
* @package Essence.Utility
*/
class Json {
/**
* JSON error messages.
*
* @var array
*/
protected static $_errors = [
JSON_ERROR_NONE => 'no error',
JSON_ERROR_DEPTH => 'depth error',
JSON_ERROR_STATE_MISMATCH => 'state mismatch error',
JSON_ERROR_CTRL_CHAR => 'control character error',
JSON_ERROR_SYNTAX => 'syntax error',
JSON_ERROR_UTF8 => 'UTF-8 error'
];
/**
* Parses a JSON document and returns an array of data.
*
* @param string $json JSON document.
* @return array Data.
*/
public static function parse( $json ) {
$data = json_decode( $json, true );
if ( $data === null ) {
throw new Exception(
'Error parsing JSON response: '
. self::$_errors[ json_last_error( )]
. '.'
);
}
return $data;
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
namespace Essence\Utility;
use Essence\Exception;
use Exception as NativeException;
use SimpleXmlIterator;
/**
* A simple XML parser.
*
* @package Essence.Utility
*/
class Xml {
/**
* Parses an XML document and returns an array of data.
*
* @param string $xml XML document.
* @return array Data.
*/
public static function parse( $xml ) {
$internal = libxml_use_internal_errors( true );
$data = [ ];
try {
$iterator = new SimpleXmlIterator( $xml );
} catch ( NativeException $Exception ) {
throw Exception::wrap( $Exception );
}
foreach ( $iterator as $key => $value ) {
$data[ $key ] = strval( $value );
}
libxml_use_internal_errors( $internal );
return $data;
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
use Essence\Utility\Autoload;
require_once dirname( __FILE__ )
. DIRECTORY_SEPARATOR . 'Essence'
. DIRECTORY_SEPARATOR . 'Utility'
. DIRECTORY_SEPARATOR . 'Autoload.php';
/**
* Definitions
*/
if ( !defined( 'ESSENCE_LIB' )) {
define( 'ESSENCE_LIB', dirname( __FILE__ ) . DIRECTORY_SEPARATOR );
}
if ( !defined( 'ESSENCE_DEFAULT_PROVIDERS' )) {
define( 'ESSENCE_DEFAULT_PROVIDERS', ESSENCE_LIB . 'providers.php' );
}
/**
* Autoload.
*/
Autoload::setup( ESSENCE_LIB );

380
vendor/essence/essence/lib/providers.php vendored Normal file
View File

@@ -0,0 +1,380 @@
<?php
/**
* @author Félix Girault <felix.girault@gmail.com>
* @license FreeBSD License (http://opensource.org/licenses/BSD-2-Clause)
*/
/**
* Default providers configuration.
*
* @see Essence\Provider\Collection::$_properties
* @var array
*/
return [
'23hq' => [
'class' => 'OEmbed',
'filter' => '#23hq\.com/.+/photo/.+#i',
'endpoint' => 'http://www.23hq.com/23/oembed?format=json&url=%s'
],
'Animoto' => [
'class' => 'OEmbed',
'filter' => '#animoto\.com/play/.+#i',
'endpoint' => 'http://animoto.com/oembeds/create?format=json&url=%s'
],
'Aol' => [
'class' => 'OEmbed',
'filter' => '#on\.aol\.com/video/.+#i',
'endpoint' => 'http://on.aol.com/api?format=json&url=%s'
],
'App.net' => [
'class' => 'OEmbed',
'filter' => '#(alpha|photo)\.app\.net/.+(/post)?/.+#i',
'endpoint' => 'https://alpha-api.app.net/oembed?format=json&url=%s'
],
'Bambuser' => [
'class' => 'OEmbed',
'filter' => '#bambuser\.com/(v|channel)/.+#i',
'endpoint' => 'http://api.bambuser.com/oembed.json?url=%s'
],
'Bandcamp' => [
'class' => 'Bandcamp',
// OpenGraph subclasses should strictly match the start of the URL
// to prevent spoofing.
'filter' => '#^https?://(?:[^\.]+\.)?bandcamp\.com/(album|track)/#i'
],
'Blip.tv' => [
'class' => 'OEmbed',
'filter' => '#blip\.tv/.+#i',
'endpoint' => 'http://blip.tv/oembed?format=json&url=%s'
],
'Cacoo' => [
'class' => 'OEmbed',
'filter' => '#cacoo\.com/.+#i',
'endpoint' => 'http://cacoo.com/oembed.json?url=%s'
],
'CanalPlus' => [
'class' => 'OpenGraph',
'filter' => '#canalplus\.fr#i'
],
'Chirb.it' => [
'class' => 'OEmbed',
'filter' => '#chirb\.it/.+#i',
'endpoint' => 'http://chirb.it/oembed.json?url=%s'
],
'CircuitLab' => [
'class' => 'OEmbed',
'filter' => '#circuitlab\.com/circuit/.+#i',
'endpoint' => 'https://www.circuitlab.com/circuit/oembed?format=json&url=%s'
],
'Clikthrough' => [
'class' => 'OEmbed',
'filter' => '#clikthrough\.com/theater/video/\d+#i',
'endpoint' => 'http://clikthrough.com/services/oembed?format=json&url=%s'
],
'CollegeHumorOEmbed' => [
'class' => 'OEmbed',
'filter' => '#collegehumor\.com/(video|embed)/.+#i',
'endpoint' => 'http://www.collegehumor.com/oembed.json?url=%s'
],
'CollegeHumorOpenGraph' => [
'class' => 'OpenGraph',
'filter' => '#collegehumor\.com/(picture|article)/.+#i'
],
'Coub' => [
'class' => 'OEmbed',
'filter' => '#coub\.com/(view|embed)/.+#i',
'endpoint' => 'http://coub.com/api/oembed.json?url=%s'
],
'CrowdRanking' => [
'class' => 'OEmbed',
'filter' => '#crowdranking\.com/.+/.+#i',
'endpoint' => 'http://crowdranking.com/api/oembed.json?url=%s'
],
'DailyMile' => [
'class' => 'OEmbed',
'filter' => '#dailymile\.com/people/.+/entries/.+#i',
'endpoint' => 'http://api.dailymile.com/oembed?format=json&url=%s'
],
'Dailymotion' => [
'class' => 'OEmbed',
'filter' => '#dailymotion\.com#i',
'endpoint' => 'http://www.dailymotion.com/services/oembed?format=json&url=%s'
],
'Deviantart' => [
'class' => 'OEmbed',
'filter' => '#deviantart\.com/.+#i',
'endpoint' => 'http://backend.deviantart.com/oembed?format=json&url=%s'
],
'Dipity' => [
'class' => 'OEmbed',
'filter' => '#dipity\.com/.+#i',
'endpoint' => 'http://www.dipity.com/oembed/timeline?format=json&url=%s'
],
'Dotsub' => [
'class' => 'OEmbed',
'filter' => '#dotsub\.com/view/.+#i',
'endpoint' => 'http://dotsub.com/services/oembed?format=json&url=%s'
],
'Edocr' => [
'class' => 'OEmbed',
'filter' => '#edocr\.com/doc/[0-9]+/.+#i',
'endpoint' => 'http://www.edocr.com/api/oembed?format=json&url=%s'
],
'Flickr' => [
'class' => 'OEmbed',
'filter' => '#flickr\.com/photos/[a-zA-Z0-9@\\._]+/[0-9]+#i',
'endpoint' => 'http://flickr.com/services/oembed?format=json&url=%s'
],
'FunnyOrDie' => [
'class' => 'OEmbed',
'filter' => '#funnyordie\.com/videos/.+#i',
'endpoint' => 'http://www.funnyordie.com/oembed?format=json&url=%s'
],
'Gist' => [
'class' => 'OEmbed',
'filter' => '#gist\.github\.com/.+/[0-9]+#i',
'endpoint' => 'https://github.com/api/oembed?format=json&url=%s'
],
'Gmep' => [
'class' => 'OEmbed',
'filter' => '#gmep\.org/media/.+#i',
'endpoint' => 'https://gmep.org/oembed.json?url=%s'
],
'HowCast' => [
'class' => 'OpenGraph',
'filter' => '#howcast\.com/.+/.+#i'
],
'Huffduffer' => [
'class' => 'OEmbed',
'filter' => '#huffduffer\.com/[-.\w@]+/\d+#i',
'endpoint' => 'http://huffduffer.com/oembed?format=json&url=%s'
],
'Hulu' => [
'class' => 'OEmbed',
'filter' => '#hulu\.com/watch/.+#i',
'endpoint' => 'http://www.hulu.com/api/oembed.json?url=%s'
],
'Ifixit' => [
'class' => 'OEmbed',
'filter' => '#ifixit\.com/.+#i',
'endpoint' => 'http://www.ifixit.com/Embed?format=json&url=%s'
],
'Ifttt' => [
'class' => 'OEmbed',
'filter' => '#ifttt\.com/recipes/.+#i',
'endpoint' => 'http://www.ifttt.com/oembed?format=json&url=%s'
],
'Imgur' => [
'class' => 'OEmbed',
'filter' => '#(imgur\.com/(gallery|a)/.+|imgur\.com/.+)#i',
'endpoint' => 'http://api.imgur.com/oembed?format=json&url=%s'
],
'Instagram' => [
'class' => 'OEmbed',
'filter' => '#instagr(\.am|am\.com)/p/.+#i',
'endpoint' => 'http://api.instagram.com/oembed?format=json&url=%s'
],
'Jest' => [
'class' => 'OEmbed',
'filter' => '#jest\.com/video/.+#i',
'endpoint' => 'http://www.jest.com/oembed.json?url=%s'
],
'Justin.tv' => [
'class' => 'OEmbed',
'filter' => '#justin\.tv/.+#i',
'endpoint' => 'http://api.justin.tv/api/embed/from_url.json?url=%s'
],
'Kickstarter' => [
'class' => 'OEmbed',
'filter' => '#kickstarter\.com/projects/.+#i',
'endpoint' => 'http://www.kickstarter.com/services/oembed?format=json&url=%s'
],
'Meetup' => [
'class' => 'OEmbed',
'filter' => '#meetup\.(com|ps)/.+#i',
'endpoint' => 'https://api.meetup.com/oembed?format=json&url=%s'
],
'Mixcloud' => [
'class' => 'OEmbed',
'filter' => '#mixcloud\.com/.+/.+#i',
'endpoint' => 'http://www.mixcloud.com/oembed?format=json&url=%s'
],
'Mobypicture' => [
'class' => 'OEmbed',
'filter' => '#(moby.to|mobypicture\.com/user/.+/view)/.+#i',
'endpoint' => 'http://api.mobypicture.com/oEmbed?format=json&url=%s'
],
'Nfb' => [
'class' => 'OEmbed',
'filter' => '#nfb\.ca/films/.+#i',
'endpoint' => 'http://www.nfb.ca/remote/services/oembed?format=json&url=%s'
],
'Official.fm' => [
'class' => 'OEmbed',
'filter' => '#official\.fm/.+#i',
'endpoint' => 'http://official.fm/services/oembed?format=json&url=%s'
],
'Polldaddy' => [
'class' => 'OEmbed',
'filter' => '#polldaddy\.com/.+#i',
'endpoint' => 'http://polldaddy.com/oembed?format=json&url=%s'
],
'PollEverywhere' => [
'class' => 'OEmbed',
'filter' => '#polleverywhere\.com/(polls|multiple_choice_polls|free_text_polls)/.+#i',
'endpoint' => 'http://www.polleverywhere.com/services/oembed?format=json&url=%s'
],
'Prezi' => [
'class' => 'OpenGraph',
'filter' => '#prezi\.com/.+/.+#i'
],
'Qik' => [
'class' => 'OEmbed',
'filter' => '#qik\.com/\w+#i',
'endpoint' => 'http://qik.com/api/oembed.json?url=%s'
],
'Rdio' => [
'class' => 'OEmbed',
'filter' => '#rdio\.com/(artist|people)/.+#i',
'endpoint' => 'http://www.rdio.com/api/oembed?format=json&url=%s'
],
'Revision3' => [
'class' => 'OEmbed',
'filter' => '#revision3\.com/[a-z0-9]+/.+#i',
'endpoint' => 'http://revision3.com/api/oembed?format=json&url=%s'
],
'Roomshare' => [
'class' => 'OEmbed',
'filter' => '#roomshare\.jp(/en)?/post/.+#i',
'endpoint' => 'http://roomshare.jp/en/oembed.json?&url=%s'
],
'Sapo' => [
'class' => 'OEmbed',
'filter' => '#videos\.sapo\.pt/.+#i',
'endpoint' => 'http://videos.sapo.pt/oembed?format=json&url=%s'
],
'Screenr' => [
'class' => 'OEmbed',
'filter' => '#screenr\.com/.+#i',
'endpoint' => 'http://www.screenr.com/api/oembed.json?url=%s'
],
'Scribd' => [
'class' => 'OEmbed',
'filter' => '#scribd\.com/doc/[0-9]+/.+#i',
'endpoint' => 'http://www.scribd.com/services/oembed?format=json&url=%s'
],
'Shoudio' => [
'class' => 'OEmbed',
'filter' => '#(shoudio\.com|shoud\.io)/.+#i',
'endpoint' => 'http://shoudio.com/api/oembed?format=json&url=%s'
],
'Sketchfab' => [
'class' => 'OEmbed',
'filter' => '#sketchfab\.com/show/.+#i',
'endpoint' => 'http://sketchfab.com/oembed?format=json&url=%s'
],
'SlideShare' => [
'class' => 'OEmbed',
'filter' => '#slideshare\.net/.+/.+#i',
'endpoint' => 'http://www.slideshare.net/api/oembed/2?format=json&url=%s'
],
'SoundCloud' => [
'class' => 'OEmbed',
'filter' => '#soundcloud\.com/[a-zA-Z0-9-_]+/[a-zA-Z0-9-]+#i',
'endpoint' => 'http://soundcloud.com/oembed?format=json&url=%s'
],
'SpeakerDeck' => [
'class' => 'OEmbed',
'filter' => '#speakerdeck\.com/.+/.+#i',
'endpoint' => 'https://speakerdeck.com/oembed.json?url=%s'
],
'Spotify' => [
'class' => 'OEmbed',
'filter' => '#(open|play)\.spotify\.com/.+#i',
'endpoint' => 'https://embed.spotify.com/oembed?format=json&url=%s'
],
'TedOEmbed' => [
'class' => 'OEmbed',
'filter' => '#ted\.com/talks/.+#i',
'endpoint' => 'http://www.ted.com/talks/oembed.json?url=%s'
],
'TedOpenGraph' => [
'class' => 'OpenGraph',
'filter' => '#ted\.com/talks#i'
],
'Twitter' => [
'class' => 'OEmbed',
'filter' => '#twitter\.com/[a-zA-Z0-9_]+/status(es)?/.+#i',
'endpoint' => 'https://api.twitter.com/1/statuses/oembed.json?url=%s'
],
'Ustream' => [
'class' => 'OEmbed',
'filter' => '#ustream\.(tv|com)/.+#i',
'endpoint' => 'http://www.ustream.tv/oembed?format=json&url=%s'
],
'Vhx' => [
'class' => 'OEmbed',
'filter' => '#vhx\.tv/.+#i',
'endpoint' => 'http://vhx.tv/services/oembed.json?url=%s'
],
'Viddler' => [
'class' => 'OEmbed',
'filter' => '#viddler\.com/.+#i',
'endpoint' => 'http://www.viddler.com/oembed/?url=%s'
],
'Videojug' => [
'class' => 'OEmbed',
'filter' => '#videojug\.com/(film|interview)/.+#i',
'endpoint' => 'http://www.videojug.com/oembed.json?url=%s'
],
'Vimeo' => [
'class' => 'Vimeo',
'filter' => '#vimeo\.com#i',
'endpoint' => 'http://vimeo.com/api/oembed.json?url=%s'
],
'Vine' => [
'class' => 'Vine',
// OpenGraph subclasses should strictly match the start of the URL
// to prevent spoofing.
'filter' => '#^https?://vine.co/v/[a-zA-Z0-9]+#i'
],
'Wistia' => [
'class' => 'OEmbed',
'filter' => '#https?://(.+)?(wistia.com|wi.st)/.*#i',
'endpoint' => 'http://fast.wistia.com/oembed?format=json&url=%s',
],
'WordPress' => [
'class' => 'OEmbed',
'filter' => '#wordpress\\.com/.+#i',
'endpoint' => 'http://public-api.wordpress.com/oembed/1.0?format=json&for=me&url=%s'
],
'Yfrog' => [
'class' => 'OEmbed',
'filter' => '#yfrog\.(com|ru|com\.tr|it|fr|co\.il|co\.uk|com\.pl|pl|eu|us)/.+#i',
'endpoint' => 'http://www.yfrog.com/api/oembed?format=json&url=%s'
],
'Youtube' => [
'class' => 'Youtube',
'filter' => '#youtube\.com|youtu\.be#i',
'endpoint' => 'http://www.youtube.com/oembed?format=json&url=%s'
]
/**
* The following providers will try to embed any URL.
*/
/*
'OEmbed' => [
'class' => 'OEmbed',
'filter' => '#.+#'
],
'OpenGraph' => [
'class' => 'OpenGraph',
'filter' => '#.+#'
],
*/
];