void
*/
final public function register_service( BP_Links_Embed_Service $service )
{
// make sure service hasn't been registered already
foreach ( $this->services as $service_protoype ) {
if ( get_class( $service ) === get_class( $service_protoype ) ) {
return true;
} elseif ( $service->key() === $service_protoype->key() ) {
// service key already registered, possible hijack attempt, what do we do?
throw new BP_Links_Embed_Fatal_Exception( sprintf( 'Service %s has already been registered.', get_class( $service_protoype ) ) );
}
}
// append service to registry
$this->services[$service->key()] = $service;
return true;
}
/**
* Loop through registered services and try to locate using callback method
*
* @param string $interface
* @param string $string
* @return BP_Links_Embed_Service|false
*/
final public function locate_service( $interface, $string )
{
// need a non-empty string to continue
if ( empty( $string ) === true || is_string( $string ) === false ) {
throw new BP_Links_Embed_Fatal_Exception( 'Second argument must be a string' );
}
// ALWAYS switch through these for security reasons
// and to handle default string cleaning
switch ( $interface ) {
case self::INTERFACE_URL:
case self::INTERFACE_XML:
case self::INTERFACE_OEMBED:
case self::INTERFACE_JSON:
case self::INTERFACE_HTML:
$string_clean = trim( $string );
break;
default:
throw new BP_Links_Embed_Fatal_Exception( 'Invalid interface' );
}
// name of the service interface, and locate method
$service_interface = self::INTERFACE_PREFIX . $interface;
$locate_method = strtolower( $interface );
// store fallback keys in an array
$fallback_services = array();
// loop through all registered services
foreach ( $this->services as $service_protoype ) {
// if service supports the required interface, try to construct
if ( $service_protoype instanceof $service_interface ) {
// clone it!!! otherwise a reference to the registered object will be returned
$service = clone $service_protoype;
// is this service acting as a fallback?
if ( $service->is_fallback() ) {
// save this service for later
$fallback_services[] = $service;
continue;
}
// exec locate method, return service on success
if ( call_user_func( array( $service, $locate_method ), $string_clean ) ) {
return $service;
}
}
}
// no luck, try the fallback services (if any)
foreach ( $fallback_services as $fallback_service ) {
if ( call_user_func( array( $fallback_service, $locate_method ), $string_clean ) ) {
return $fallback_service;
}
}
// no service could handle the string with given method
return false;
}
/**
* Try to load a service from embed data object or serialized string
*
* @param BP_Links_Embed_Data|string $embed_data_mixed
* @param string $service_key providing this will speed up loading
* @return BP_Links_Embed_Service|false
*/
final public function load_service( $embed_data_mixed, $service_key = null )
{
// did we get a key?
if ( empty( $service_key ) === false && strlen( $service_key ) == 32 ) {
// look for service in registry
if ( array_key_exists( $service_key, $this->services ) && $this->services[$service_key] instanceof BP_Links_Embed_Service ) {
// clone it!!!
$service = clone $this->services[$service_key];
// load data based on type
if ( $embed_data_mixed instanceof BP_Links_Embed_Data ) {
$load_result = $service->from_data( $embed_data_mixed );
} elseif ( is_string( $embed_data_mixed ) === true ) {
$load_result = $service->import_data( $embed_data_mixed );
} else {
throw new BP_Links_Embed_Fatal_Exception( 'Invalid data received' );
}
// if load was sucessful, return the service object
return ( $load_result === true ) ? $service : false;
}
// service not found in registry
return null;
} else {
// no key provided, use search method
return $this->load_service_search( $embed_data_mixed );
}
}
/**
* Try to load a service when we don't have the service key
*
* @param BP_Links_Embed_Data|string $embed_data_mixed
* @return BP_Links_Embed_Service|false
*/
private function load_service_search( $embed_data_mixed )
{
// if we have a string, unserialize it
if ( is_string( $embed_data_mixed ) === true ) {
$embed_data = unserialize( base64_decode( $embed_data_mixed ) );
}
// if we have a valid embed data object, try to load service
if ( $embed_data instanceof BP_Links_Embed_Data ) {
// loop through services and try to match keys
foreach ( $this->services as $service_protoype ) {
// clone it!!!
$service = clone $service_protoype;
// if keys match, try to hydrate the service
if ( $service->key() === $embed_data->key() ) {
return ( $service->from_data( $embed_data ) ) ? $service : false;
}
}
// no service key match found
return null;
} else {
throw new BP_Links_Embed_Fatal_Exception( 'Invalid data received' );
}
}
/**
* Return array of URL regex patterns for services that support an interface
*
* @param string $interface
* @return array
*/
final public function get_service_patterns( $interface )
{
// the array of patterns to return
$patterns = array();
// ALWAYS switch through these for security reasons
switch ( $interface ) {
case self::INTERFACE_URL:
break;
default:
throw new BP_Links_Embed_Fatal_Exception( 'Invalid interface' );
}
// name of the service interface, and locate method
$service_interface = self::INTERFACE_PREFIX . $interface;
$locate_method = strtolower( $interface ) . '_pattern';
// loop through all registered services
foreach ( $this->services as $service ) {
// if service supports the required interface, get pattern
if ( $service instanceof $service_interface ) {
// exec pattern method and append to array
$patterns[] = call_user_func( array( $service, $locate_method ) );
}
}
return $patterns;
}
/**
* Get singleton instance
*
* @return BP_Links_Embed
*/
final public static function GetInstance()
{
if ( empty( self::$instance ) ) {
self::$instance = new BP_Links_Embed();
// register natively supported services
self::$instance->register_service( new BP_Links_Embed_Service_PicApp() );
self::$instance->register_service( new BP_Links_Embed_Service_Fotoglif() );
self::$instance->register_service( new BP_Links_Embed_Service_YouTube() );
self::$instance->register_service( new BP_Links_Embed_Service_Flickr() );
self::$instance->register_service( new BP_Links_Embed_Service_MetaCafe() );
self::$instance->register_service( new BP_Links_Embed_Service_WebPage(true) );
}
return self::$instance;
}
/**
* Register an embed service object statically
*
* @param BP_Links_Embed_Service $service
* @return void
*/
final public static function RegisterService( BP_Links_Embed_Service $service )
{
return self::GetInstance()->register_service( $service );
}
/**
* Try to statically load a service when all we have is the embed data object or serialized string thereof
*
* @param BP_Links_Embed_Data|string $embed_data_mixed embed data object or serialized string thereof
* @return BP_Links_Embed_Service|false
*/
final public static function LoadService( $embed_data_mixed )
{
return self::GetInstance()->load_service( $embed_data_mixed );
}
/**
* Attempt to load an embed service object from a URL string
*
* @param string $url
* @return BP_Links_Embed_Service|false
*/
final public static function FromUrl( $url )
{
return self::GetInstance()->locate_service( self::INTERFACE_URL, $url );
}
/**
* Return array of URL regex patterns for services that support URL embedding
*
* @return array
*/
final public static function FromUrlPatterns()
{
return self::GetInstance()->get_service_patterns( self::INTERFACE_URL );
}
/**
* Attempt to load an embed service object from HTML markup
*
* @param string $html
* @return BP_Links_Embed_Service|false
*/
final public static function FromHtml( $html )
{
return self::GetInstance()->locate_service( self::INTERFACE_HTML, $html );
}
/**
* Attempt to load an embed service object from XML markup
*
* @param string $xml
* @return BP_Links_Embed_Service|false
*/
final public static function FromXml( $xml )
{
return self::GetInstance()->locate_service( self::INTERFACE_XML, $xml );
}
/**
* Attempt to load an embed service object from oEmbed markup
*
* @param string $oembed
* @return BP_Links_Embed_Service|false
*/
final public static function FromOembed( $oembed )
{
return self::GetInstance()->locate_service( self::INTERFACE_OEMBED, $oembed );
}
/**
* Attempt to load an embed service object from a JSON encoded string
*
* @param string $json
* @return BP_Links_Embed_Service|false
*/
final public static function FromJson( $json )
{
return self::GetInstance()->locate_service( self::INTERFACE_JSON, $json );
}
}
/**
* Embed data storage class
*
* The intented use of this class is for caching SMALL bits of API or other
* meta data for later use. DO NOT use for session or any kind of temp data.
* Hashing is used to check if the data has been modified after serialization,
* but since the hash is public, its possible to simply change the serialized
* data and then generate a new hash. This is more of a sanity check than
* a security measure.
*
* @package BP_Links
* @author Marshall Sorenson
*/
final class BP_Links_Embed_Data
{
/**
* Service key
*
* @var string
*/
protected $k;
/**
* Data hash
*
* @var string
*/
protected $h;
/**
* Properties to store
*
* @var array
*/
protected $p = array();
/**
* An embed service must pass itself to the constructor for key exchange
*
* @param BP_Links_Embed_Service $service
*/
final public function __construct( BP_Links_Embed_Service $service )
{
$this->k = $service->key();
}
/**
* Return key that was received from the service
*
* @return string
*/
final public function key()
{
return $this->k;
}
/**
* Facilitate setting overload properties, with constraints
*
* @param string $p property to set, must be 1 to 12 word chars in length
* @param string $v value
*/
final public function __set( $p, $v )
{
if ( preg_match( '/^\w{1,24}$/', $p ) ) {
$this->p[$p] = $v;
} else {
throw new BP_Links_Embed_Fatal_Exception( 'Property name must contain only word chars and be 1 to 24 chars in length!' );
}
}
/**
* Facilitate getting overload properties
*
* @param string $p property to get
* @return mixed
*/
final public function __get( $p )
{
if ( array_key_exists( $p, $this->p ) ) {
return $this->p[$p];
} else {
throw new BP_Links_Embed_Fatal_Exception( sprintf( 'Property name "%s" has not been set!', $p ) );
}
}
/**
* Ensure that isset works on overloaded properties
*
* @param tring $p property to check
* @return boolean
*/
final public function __isset( $p )
{
return isset( $this->p[$p] );
}
/**
* Ensure that unset works on overloaded properties
*
* @param tring $p property to unset
* @return boolean
*/
final public function __unset( $p )
{
unset( $this->p[$p] );
}
/**
* When serialize() is called on this object, make hash and specify members to store.
*
* @return array
*/
final public function __sleep()
{
// calculate hash of serialized properties array
$this->h = md5( serialize( $this->p ) );
// only serialize these members
return array( 'k', 'h', 'p' );
}
/**
* When unserialize() is called on this object, check hash.
*
* @return array
*/
final public function __wakeup()
{
// calculate hash of serialized properties array
$hash = md5( serialize( $this->p ) );
// does it match the stored hash?
if ( $hash !== $this->h ) {
// nope, possible hijack
throw new BP_Links_Embed_Fatal_Exception( 'Data was modified.' );
}
}
}
/**
* An abstract embed service
*
* @package BP_Links
* @author Marshall Sorenson
*/
abstract class BP_Links_Embed_Service
extends BP_Links_Embed_Base
implements BP_Links_Embed_From_Data
{
/**
* Unique key which is an MD5 hash of the class name
*
* To `hijack` another service, all you have to do is write
* a class with a name who's hash matches the other service's
* class name hash, and register your service first. You can mess
* with their data and output all you want until they register their
* service and a fatal exception is thrown. Have fun.
*
* @var string
*/
private $key;
/**
* Data storage object
*
* @var BP_Links_Embed_Data
*/
private $data;
/**
* Whether this service should behave like a fallback
*
* @var boolean
*/
private $act_as_fallback = false;
/**
* Override one or more of the from_*() methods to complete contruction
*
* @param boolean $act_as_fallback Setting this to true will cause this service to be tried after all other services
*/
final public function __construct( $act_as_fallback = false )
{
if ( $act_as_fallback === true ) {
$this->act_as_fallback = true;
}
}
/**
* Return fallback status
*
* @return boolean
*/
final public function is_fallback()
{
return $this->act_as_fallback;
}
/**
* Return api key set by concrete embed service class.
*
* @return string
*/
final public function key()
{
// check if set, and set (cache) if necessary
if ( !$this->key ) {
// MD5 hash of the class name
$this->key = md5( get_class( $this ) );
}
return $this->key;
}
/**
* Make data object available to concrete classes
*
* @return BP_Links_Embed_Data
*/
final protected function data()
{
// initialize data object if necessary
if ( !$this->data instanceof BP_Links_Embed_Data ) {
$this->data = new BP_Links_Embed_Data( $this );
}
return $this->data;
}
/**
* Return byte stream representation of data
*
* @return string|null serialized object that is base64 encoded
*/
final public function export_data()
{
if ( !empty( $this->data ) ) {
return base64_encode( serialize( $this->data ) );
} else {
return null;
}
}
/**
* Import byte stream representation of data
*
* @param $string serialized object that is base64 encoded
* @return boolean
*/
final public function import_data( $string )
{
// need a non-empty string to continue
if ( empty( $string ) === false && is_string( $string ) === true ) {
// resurrect object
$embed_data = unserialize( base64_decode( $string ) );
// if we have a valid embed data object, try to load data
if ( $embed_data instanceof BP_Links_Embed_Data ) {
return $this->from_data( $embed_data );
} else {
throw new BP_Links_Embed_Fatal_Exception( 'Invalid data received' );
}
} else {
throw new BP_Links_Embed_Fatal_Exception( 'Argument must be a string' );
}
}
/**
* Construct this service from an embed data object
*
* @param BP_Links_Embed_Data $embed_data
*/
final public function from_data( BP_Links_Embed_Data $embed_data )
{
// do keys match?
if ( $this->key() === $embed_data->key() ) {
$this->data = $embed_data;
return true;
}
return false;
}
/**
* Provide backwards compatibility for versions before 0.2.1
*
* @return boolean
*/
final public function avatar_only() {
return ( $this instanceof BP_Links_Embed_Avatar_Only );
}
//
// optional concrete methods
//
public function image_width() { return false; }
public function image_height() { return false; }
public function avatar_class() { return false; }
public function avatar_play_video() { return false; }
public function avatar_play_photo() { return false; }
public function avatar_max_width() { return false; }
public function avatar_max_height() { return false; }
//
// error message helpers
//
final protected function err_embed_url()
{
return sprintf( __( 'The URL you entered is not a valid %1$s link.', 'buddypress-links' ), $this->service_name() );
}
final protected function err_embed_code()
{
return sprintf( __( 'The code you entered is not valid %1$s embedding code.', 'buddypres
Fatal error: Class 'BP_Links_Embed_Service' not found in /home/ciucuda/public_html/wp-content/plugins/buddypress-links/bp-links-embed-services.php on line 35