├── README.md ├── media.php ├── objects.php └── open-graph-protocol.php /README.md: -------------------------------------------------------------------------------- 1 | # Open Graph protocol 2 | 3 |  4 | 5 | [Open Graph protocol](http://ogp.me/ "Open Graph protocol community site") enhances information associated with a webpage through key-value pairs included as `` elements in your HTML. This additional data is consumed by social sharing sites, populating a story preview for shared links and referenced objects in the social graph. 6 | 7 | This project includes tools to validate and sanitize inputs before generating Open Graph protocol markup on your webpages. This project standardizes outputs for easy indexing by consuming agents. 8 | 9 | ## Consuming agents 10 | 11 | ### Facebook 12 | 13 | [Facebook indexes Open Graph protocol markup](http://developers.facebook.com/docs/opengraph/ "Facebook Open Graph protocol") found on pages shared in a member's social news feed. This markup also enhances social news feed stories generated by the [Like button social plugin](http://developers.facebook.com/docs/reference/plugins/like/ "Facebook Like button") and [Facebook Share](http://developers.facebook.com/docs/share/). 14 | 15 | Open Graph protocol markup turns webpages into objects within the Facebook social graph, increasing search exposure and uniquely classifying your site and story type within the social news feed. 16 | 17 | The [Facebook Object Debugger](http://developers.facebook.com/tools/debug) displays a Facebook interpretation of your site's Open Graph protocol content. 18 | 19 | ### mixi Check 20 | 21 | [Mixi indexes Open Graph protocol markup](http://groups.google.com/group/open-graph-protocol/browse_thread/thread/356d722abf70001d/397ec334ca87f122 "mixi Check Open Graph protocol Google Group announcement") in its [mixi Check](http://developer.mixi.co.jp/connect/mixi_graph_api/mixi_io_spec_top/check-api/) social sharing service. 22 | 23 | ### Google+ Snippet 24 | 25 | [Google indexes Open Graph protocol markup](https://developers.google.com/+/plugins/+1button/#plus-snippet) to populate a Google+ activity post. 26 | 27 | ## Sample code 28 | 29 | ### Core Open Graph protocol 30 | 31 | Support for structured properties for image, video, and audio objects. 32 | 33 | ```php 34 | setURL( 'http://example.com/image.jpg' ); 37 | $image->setSecureURL( 'https://example.com/image.jpg' ); 38 | $image->setType( 'image/jpeg' ); 39 | $image->setWidth( 400 ); 40 | $image->setHeight( 300 ); 41 | 42 | $video = new OpenGraphProtocolVideo(); 43 | $video->setURL( 'http://example.com/video.swf' ); 44 | $video->setSecureURL( 'https://example.com/video.swf' ); 45 | $video->setType( OpenGraphProtocolVideo::extension_to_media_type( pathinfo( parse_url( $video->getURL(), PHP_URL_PATH ), PATHINFO_EXTENSION ) ) ); 46 | $video->setWidth( 500 ); 47 | $video->setHeight( 400 ); 48 | 49 | $audio = new OpenGraphProtocolAudio(); 50 | $audio->setURL( 'http://example.com/audio.mp3' ); 51 | $audio->setSecureURL( 'https://example.com/audio.mp3' ); 52 | $audio->setType('audio/mpeg'); 53 | ?> 54 | ``` 55 | 56 | Declare a new `OpenGraphProtocol` object and set some properties. Add structured media objects. 57 | 58 | ```php 59 | setLocale( 'en_US' ); 62 | $ogp->setSiteName( 'Happy place' ); 63 | $ogp->setTitle( 'Hello world' ); 64 | $ogp->setDescription( 'We make the world happy.' ); 65 | $ogp->setType( 'website' ); 66 | $ogp->setURL( 'http://example.com/' ); 67 | $ogp->setDeterminer( 'the' ); 68 | $ogp->addImage($image); 69 | $ogp->addAudio($audio); 70 | $ogp->addVideo($video); 71 | ?> 72 | ``` 73 | 74 | Output your OpenGraphProtocol object as HTML `` elements. Default configuration uses the `property` attribute from RDFa. Change to `name` if you prefer HTML specification compliance and consuming agents support the `name` attribute as a `property` fallback. 75 | 76 | ```php 77 | toHTML(); 79 | ?> 80 | ``` 81 | 82 | ### Global objects 83 | 84 | Build global objects and attributes. Set time values using either an ISO 8601 formatted string or a DateTime object. DateTimes will be converted to the UTC timezone before output for consistency. 85 | 86 | ```php 87 | setPublishedTime( '2011-11-03T01:23:45Z' ); 90 | $article->setModifiedTime( new DateTime( 'now', new DateTimeZone( 'America/Los_Angeles' ) ) ); 91 | $article->setExpirationTime( '2011-12-31T23:59:59+00:00' ); 92 | $article->setSection( 'Front page' ); 93 | $article->addTag( 'weather' ); 94 | $article->addTag( 'football' ); 95 | $article->addAuthor( 'http://example.com/author.html' ); 96 | ?> 97 | ``` 98 | 99 | Convert a global object to `` elements just as you would with `OpenGraphProtocol.` 100 | 101 | ```php 102 | toHTML(); 104 | ?> 105 | ``` 106 | 107 | ### Combined 108 | 109 | A common use case might be storing Open Graph protocol objects in a Controller for use by your web application. You can add each object to an array and later iterate through the array for `
` and `` outputs. 110 | 111 | ```php 112 | toHTML() . PHP_EOL; 119 | } 120 | ?> 121 | 122 | 123 | 124 | ``` -------------------------------------------------------------------------------- /media.php: -------------------------------------------------------------------------------- 1 | 8 | * @version 1.3 9 | * @copyright Public Domain 10 | */ 11 | 12 | if ( !class_exists('OpenGraphProtocol') ) 13 | require_once(dirname(__FILE__) . '/open-graph-protocol.php'); 14 | 15 | /** 16 | * Describe a media object 17 | * 18 | * @version 1.3 19 | */ 20 | abstract class OpenGraphProtocolMedia { 21 | 22 | /** 23 | * HTTP scheme URL 24 | * 25 | * @var string 26 | * @since 1.3 27 | */ 28 | protected $url; 29 | 30 | /** 31 | * HTTPS scheme URL 32 | * 33 | * @var string 34 | * @since 1.3 35 | */ 36 | protected $secure_url; 37 | 38 | /** 39 | * Internet media type of the linked URLs 40 | * 41 | * @var string 42 | * @since 1.3 43 | */ 44 | protected $type; 45 | 46 | /** 47 | * Treat a string reference just like the base property 48 | */ 49 | public function toString() { 50 | return $this->url; 51 | } 52 | 53 | public function toArray() { 54 | return get_object_vars($this); 55 | } 56 | 57 | /** 58 | * @return string URL string or null if not set 59 | */ 60 | public function getURL() { 61 | return $this->url; 62 | } 63 | 64 | /** 65 | * Set the media URL 66 | * 67 | * @param string $url resource location 68 | */ 69 | public function setURL( $url ) { 70 | if ( is_string( $url ) && !empty( $url ) ) { 71 | $url = trim($url); 72 | if (OpenGraphProtocol::VERIFY_URLS) { 73 | $url = OpenGraphProtocol::is_valid_url( $url, array( 'text/html', 'application/xhtml+xml' ) ); 74 | } 75 | if (!empty($url)) 76 | $this->url = $url; 77 | } 78 | return $this; 79 | } 80 | 81 | /** 82 | * Remove the URL property. 83 | * Sets up the maximum compatibility between image and image:url indexers 84 | */ 85 | public function removeURL() { 86 | if ( !empty($this->url) ) 87 | unset($this->url); 88 | } 89 | 90 | /** 91 | * @return string secure URL string or null if not set 92 | */ 93 | public function getSecureURL() { 94 | return $this->url; 95 | } 96 | 97 | /** 98 | * Set the secure URL for display in a HTTPS page 99 | * 100 | * @param string $url resource location 101 | */ 102 | public function setSecureURL( $url ) { 103 | if ( is_string( $url ) && !empty( $url ) ) { 104 | $url = trim($url); 105 | if (OpenGraphProtocol::VERIFY_URLS) { 106 | if ( parse_url($url,PHP_URL_SCHEME) === 'https' ) { 107 | $url = OpenGraphProtocol::is_valid_url( $url, array( 'text/html', 'application/xhtml+xml' ) ); 108 | } else { 109 | $url = ''; 110 | } 111 | } 112 | if (!empty($url)) 113 | $this->secure_url = $url; 114 | } 115 | return $this; 116 | } 117 | 118 | /** 119 | * Get the Internet media type of the referenced resource 120 | * 121 | * @return string Internet media type or null if none set 122 | */ 123 | public function getType() { 124 | return $this->type; 125 | } 126 | } 127 | 128 | abstract class OpenGraphProtocolVisualMedia extends OpenGraphProtocolMedia { 129 | /** 130 | * Height of the media object in pixels 131 | * 132 | * @var int 133 | * @since 1.3 134 | */ 135 | protected $height; 136 | 137 | /** 138 | * Width of the media object in pixels 139 | * 140 | * @var int 141 | * @since 1.3 142 | */ 143 | protected $width; 144 | 145 | /** 146 | * @return int width in pixels 147 | */ 148 | public function getWidth() { 149 | return $this->width; 150 | } 151 | 152 | /** 153 | * Set the object width 154 | * 155 | * @param int $width width in pixels 156 | */ 157 | public function setWidth( $width ) { 158 | if ( is_int($width) && $width > 0 ) 159 | $this->width = $width; 160 | return $this; 161 | } 162 | 163 | /** 164 | * @return int height in pixels 165 | */ 166 | public function getHeight() { 167 | return $this->height; 168 | } 169 | 170 | /** 171 | * Set the height of the referenced object in pixels 172 | * @var int height of the referenced object in pixels 173 | */ 174 | public function setHeight( $height ) { 175 | if ( is_int($height) && $height > 0 ) 176 | $this->height = $height; 177 | return $this; 178 | } 179 | } 180 | 181 | /** 182 | * An image representing page content. Suitable for display alongside a summary of the webpage. 183 | */ 184 | class OpenGraphProtocolImage extends OpenGraphProtocolVisualMedia { 185 | /** 186 | * Map a file extension to a registered Internet media type 187 | * 188 | * @link http://www.iana.org/assignments/media-types/image/index.html IANA image types 189 | * @param string $extension file extension 190 | * @return string Internet media type in the format image/* 191 | */ 192 | public static function extension_to_media_type( $extension ) { 193 | if ( empty($extension) || ! is_string($extension) ) 194 | return; 195 | if ( $extension === 'jpeg' || $extension === 'jpg' ) 196 | return 'image/jpeg'; 197 | else if ( $extension === 'png' ) 198 | return 'image/png'; 199 | else if ( $extension === 'gif' ) 200 | return 'image/gif'; 201 | else if ( $extension === 'svg' ) 202 | return 'image/svg+sml'; 203 | else if ( $extension === 'ico' ) 204 | return 'image/vnd.microsoft.icon'; 205 | } 206 | 207 | /** 208 | * Set the Internet media type. Allow only image types. 209 | * 210 | * @param string $type Internet media type 211 | */ 212 | public function setType( $type ) { 213 | if ( substr_compare( $type, 'image/', 0, 6 ) === 0 ) 214 | $this->type = $type; 215 | return $this; 216 | } 217 | } 218 | 219 | /** 220 | * A video that complements the webpage content 221 | */ 222 | class OpenGraphProtocolVideo extends OpenGraphProtocolVisualMedia { 223 | /** 224 | * Map a file extension to a registered Internet media type 225 | * Include Flash as a video type due to its popularity as a wrapper 226 | * 227 | * @link http://www.iana.org/assignments/media-types/video/index.html IANA video types 228 | * @param string $extension file extension 229 | * @return string Internet media type in the format video/* or Flash 230 | */ 231 | public static function extension_to_media_type( $extension ) { 232 | if ( empty($extension) || ! is_string($extension) ) 233 | return; 234 | if ( $extension === 'swf' ) 235 | return 'application/x-shockwave-flash'; 236 | else if ( $extension === 'mp4' ) 237 | return 'video/mp4'; 238 | else if ( $extension === 'ogv' ) 239 | return 'video/ogg'; 240 | else if ( $extension === 'webm' ) 241 | return 'video/webm'; 242 | } 243 | 244 | /** 245 | * Set the Internet media type. Allow only video types + Flash wrapper. 246 | * 247 | * @param string $type Internet media type 248 | */ 249 | public function setType( $type ) { 250 | if ( $type === 'application/x-shockwave-flash' || substr_compare( $type, 'video/', 0, 6 ) === 0 ) 251 | $this->type = $type; 252 | return $this; 253 | } 254 | } 255 | 256 | /** 257 | * Audio file suitable for playback alongside the main linked content 258 | */ 259 | class OpenGraphProtocolAudio extends OpenGraphProtocolMedia { 260 | /** 261 | * Map a file extension to a registered Internet media type 262 | * Include Flash as a video type due to its popularity as a wrapper 263 | * 264 | * @link http://www.iana.org/assignments/media-types/audio/index.html IANA video types 265 | * @param string $extension file extension 266 | * @return string Internet media type in the format audio/* or Flash 267 | */ 268 | public static function extension_to_media_type( $extension ) { 269 | if ( empty($extension) || ! is_string($extension) ) 270 | return; 271 | if ( $extension === 'swf' ) 272 | return 'application/x-shockwave-flash'; 273 | else if ( $extension === 'mp3' ) 274 | return 'audio/mpeg'; 275 | else if ( $extension === 'm4a' ) 276 | return 'audio/mp4'; 277 | else if ( $extension === 'ogg' || $extension === 'oga' ) 278 | return 'audio/ogg'; 279 | } 280 | 281 | /** 282 | * Set the Internet media type. Allow only audio types + Flash wrapper. 283 | * 284 | * @param string $type Internet media type 285 | */ 286 | public function setType( $type ) { 287 | if ( $type === 'application/x-shockwave-flash' || substr_compare( $type, 'audio/', 0, 6 ) === 0 ) 288 | $this->type = $type; 289 | return $this; 290 | } 291 | } 292 | ?> -------------------------------------------------------------------------------- /objects.php: -------------------------------------------------------------------------------- 1 | 8 | * @version 1.3 9 | * @copyright Public Domain 10 | */ 11 | if ( !class_exists('OpenGraphProtocol') ) 12 | require_once dirname(__FILE__) . '/open-graph-protocol.php'; 13 | 14 | abstract class OpenGraphProtocolObject { 15 | const PREFIX =''; 16 | const NS=''; 17 | 18 | /** 19 | * Output the object as HTML elements 20 | * @return string HTML meta element string 21 | */ 22 | public function toHTML() { 23 | return rtrim( OpenGraphProtocol::buildHTML( get_object_vars($this), static::PREFIX ), PHP_EOL ); 24 | } 25 | 26 | /** 27 | * Convert a DateTime object to GMT and format as an ISO 8601 string. 28 | * @param DateTime $date date to convert 29 | * @return string ISO 8601 formatted datetime string 30 | */ 31 | public static function datetime_to_iso_8601( DateTime $date ) { 32 | $date->setTimezone(new DateTimeZone('GMT')); 33 | return $date->format('c'); 34 | } 35 | 36 | /** 37 | * Test a URL for validity. 38 | * 39 | * @uses OpenGraphProtocol::is_valid_url if OpenGraphProtocol::VERIFY_URLS is true 40 | * @param string $url absolute URL addressable from the public web 41 | * @return bool true if URL is non-empty string. if VERIFY_URLS set then URL must also properly respond to a HTTP request. 42 | */ 43 | public static function is_valid_url( $url ) { 44 | if ( is_string($url) && !empty($url) ) { 45 | if (OpenGraphProtocol::VERIFY_URLS) { 46 | $url = OpenGraphProtocol::is_valid_url( $url, array( 'text/html', 'application/xhtml+xml' ) ); 47 | if (!empty($url)) 48 | return true; 49 | } else { 50 | return true; 51 | } 52 | } 53 | return false; 54 | } 55 | } 56 | 57 | class OpenGraphProtocolArticle extends OpenGraphProtocolObject { 58 | /** 59 | * Property prefix 60 | * @var string 61 | */ 62 | const PREFIX = 'article'; 63 | 64 | /** 65 | * prefix namespace 66 | * @var string 67 | */ 68 | const NS = 'http://ogp.me/ns/article#'; 69 | 70 | /** 71 | * When the article was first published. 72 | * ISO 8601 formatted string. 73 | * @var string 74 | */ 75 | protected $published_time; 76 | 77 | /** 78 | * When the article was last changed 79 | * ISO 8601 formatted string. 80 | * @var string 81 | */ 82 | protected $modified_time; 83 | 84 | /** 85 | * When the article is considered out-of-date 86 | * ISO 8601 formatted string. 87 | * @var string 88 | */ 89 | protected $expiration_time; 90 | 91 | /** 92 | * Writers of the article. 93 | * Array of author URIs 94 | * @var array 95 | */ 96 | protected $author; 97 | 98 | /** 99 | * High-level section or category 100 | * @var string 101 | */ 102 | protected $section; 103 | 104 | /** 105 | * Content tag 106 | * Array of tag strings 107 | * @var array 108 | */ 109 | protected $tag; 110 | 111 | /** 112 | * Initialize arrays 113 | */ 114 | public function __construct() { 115 | $this->author = array(); 116 | $this->tag = array(); 117 | } 118 | 119 | /** 120 | * When the article was first published 121 | * @return string ISO 8601 formatted publication date and optional time 122 | */ 123 | public function getPublishedTime() { 124 | return $this->published_time; 125 | } 126 | 127 | /** 128 | * Set when the article was first published 129 | * @param DateTime|string $pubdate ISO 8601 formatted datetime string or DateTime object for conversion 130 | */ 131 | public function setPublishedTime( $pubdate ) { 132 | if ( $pubdate instanceof DateTime ) 133 | $this->published_time = static::datetime_to_iso_8601($pubdate); 134 | else if ( is_string($pubdate) && strlen($pubdate) >= 10 ) // at least YYYY-MM-DD 135 | $this->published_time = $pubdate; 136 | return $this; 137 | } 138 | 139 | /** 140 | * When article was last changed 141 | * @return string ISO 8601 formatted modified date and optional time 142 | */ 143 | public function getModifiedTime() { 144 | return $this->modified_time; 145 | } 146 | 147 | /** 148 | * Set when the article was last changed 149 | * @param DateTime|string $updated ISO 8601 formatted datetime string or DateTime object for conversion 150 | */ 151 | public function setModifiedTime( $updated ) { 152 | if ( $updated instanceof DateTime ) 153 | $this->modified_time = static::datetime_to_iso_8601($updated); 154 | else if ( is_string($updated) && strlen($updated) >= 10 ) // at least YYYY-MM-DD 155 | $this->modified_time = $updated; 156 | return $this; 157 | } 158 | 159 | /** 160 | * Time the article content expires 161 | * @return string ISO 8601 formatted expiration date and optional time 162 | */ 163 | public function getExpirationTime() { 164 | return $this->expiration_time; 165 | } 166 | 167 | /** 168 | * Set when the article content expires 169 | * @param DateTime|string $expires ISO formatted datetime string or DateTime object for conversion 170 | */ 171 | public function setExpirationTime( $expires ) { 172 | if ( $expires instanceof DateTime ) 173 | $this->expiration_time = static::datetime_to_iso_8601($expires); 174 | else if ( is_string($expires) && strlen($expires) >= 10 ) 175 | $this->expiration_time = $expires; 176 | return $this; 177 | } 178 | 179 | /** 180 | * Article author URIs 181 | * @return array Article author URIs 182 | */ 183 | public function getAuthors() { 184 | return $this->author; 185 | } 186 | 187 | /** 188 | * Add an author URI 189 | * @param string $author_uri Author URI 190 | */ 191 | public function addAuthor( $author_uri ) { 192 | if ( static::is_valid_url($author_uri) && !in_array($author_uri, $this->author)) 193 | $this->author[] = $author_uri; 194 | return $this; 195 | } 196 | 197 | /** 198 | * High-level section name 199 | */ 200 | public function getSection() { 201 | return $this->section; 202 | } 203 | 204 | /** 205 | * Set the top-level content section 206 | * @param string $section 207 | */ 208 | public function setSection( $section ) { 209 | if ( is_string($section) && !empty($section) ) 210 | $this->section = $section; 211 | return $this; 212 | } 213 | 214 | /** 215 | * Content tags 216 | * @return array content tags 217 | */ 218 | public function getTags() { 219 | return $this->tag; 220 | } 221 | 222 | /** 223 | * Add a content tag 224 | * @param string $tag content tag 225 | */ 226 | public function addTag( $tag ) { 227 | if ( is_string($tag) && !empty($tag) ) 228 | $this->tag[] = $tag; 229 | return $this; 230 | } 231 | } 232 | 233 | class OpenGraphProtocolProfile extends OpenGraphProtocolObject { 234 | /** 235 | * Property prefix 236 | * @var string 237 | */ 238 | const PREFIX = 'profile'; 239 | 240 | /** 241 | * prefix namespace 242 | * @var string 243 | */ 244 | const NS = 'http://ogp.me/ns/profile#'; 245 | 246 | /** 247 | * A person's given name 248 | * @var string 249 | */ 250 | protected $first_name; 251 | 252 | /** 253 | * A person's last name 254 | * @var string 255 | */ 256 | protected $last_name; 257 | 258 | /** 259 | * The profile's unique username 260 | * @var string 261 | */ 262 | protected $username; 263 | 264 | /** 265 | * Gender: male or female 266 | */ 267 | protected $gender; 268 | 269 | /** 270 | * Get the person's given name 271 | * @return string given name 272 | */ 273 | public function getFirstName() { 274 | return $this->first_name; 275 | } 276 | 277 | /** 278 | * Set the person's given name 279 | * @param string $first_name given name 280 | */ 281 | public function setFirstName( $first_name ) { 282 | if ( is_string($first_name) && !empty($first_name) ) 283 | $this->first_name = $first_name; 284 | return $this; 285 | } 286 | 287 | /** 288 | * The person's family name 289 | * @return string famil name 290 | */ 291 | public function getLastName() { 292 | return $this->last_name; 293 | } 294 | 295 | /** 296 | * Set the person's family name 297 | * @param string $last_name family name 298 | */ 299 | public function setLastName( $last_name ) { 300 | if ( is_string($last_name) && !empty($last_name) ) 301 | $this->last_name = $last_name; 302 | return $this; 303 | } 304 | 305 | /** 306 | * Person's username on your site 307 | * @return string account username 308 | */ 309 | public function getUsername() { 310 | return $this->username; 311 | } 312 | 313 | /** 314 | * Set the account username 315 | * @param string $username username 316 | */ 317 | public function setUsername( $username ) { 318 | if ( is_string($username) && !empty($username) ) 319 | $this->username = $username; 320 | return $this; 321 | } 322 | 323 | /** 324 | * The person's gender. male|female 325 | * @return string male|female 326 | */ 327 | public function getGender() { 328 | return $this->gender; 329 | } 330 | 331 | /** 332 | * Set the person's gender 333 | * @param string $gender male|female 334 | */ 335 | public function setGender( $gender ) { 336 | if ( is_string($gender) && ( $gender === 'male' || $gender === 'female' ) ) 337 | $this->gender = $gender; 338 | return $this; 339 | } 340 | } 341 | 342 | class OpenGraphProtocolBook extends OpenGraphProtocolObject { 343 | /** 344 | * Property prefix 345 | * @var string 346 | */ 347 | const PREFIX = 'book'; 348 | 349 | /** 350 | * prefix namespace 351 | * @var string 352 | */ 353 | const NS = 'http://ogp.me/ns/book#'; 354 | 355 | /** 356 | * Book authors as an array of URIs. 357 | * The target URI is expected to have an Open Graph protocol type of 'profile' 358 | * @var array 359 | */ 360 | protected $author; 361 | 362 | /** 363 | * International Standard Book Number. ISBN-10 and ISBN-13 accepted 364 | * @link http://en.wikipedia.org/wiki/International_Standard_Book_Number ISBN 365 | * @var string 366 | */ 367 | protected $isbn; 368 | 369 | /** 370 | * The date the book was released, or planned release if in future. 371 | * Stored as an ISO 8601 date string normalized to UTC for consistency 372 | * @var string 373 | */ 374 | protected $release_date; 375 | 376 | /** 377 | * Tag words describing book content and subjects 378 | * @var array 379 | */ 380 | protected $tag; 381 | 382 | public function __construct() { 383 | $this->author = array(); 384 | $this->tag = array(); 385 | } 386 | 387 | /** 388 | * Book author URIs 389 | * @return array author URIs 390 | */ 391 | public function getAuthors() { 392 | return $this->author; 393 | } 394 | 395 | /** 396 | * Add an author URI. 397 | * 398 | * @param string $author_uri 399 | */ 400 | public function addAuthor( $author_uri ) { 401 | if ( static::is_valid_url($author_uri) && !in_array($author_uri, $this->author)) 402 | $this->author[] = $author_uri; 403 | return $this; 404 | } 405 | 406 | /** 407 | * International Standard Book Number 408 | * 409 | * @return string 410 | */ 411 | public function getISBN() { 412 | return $this->isbn; 413 | } 414 | 415 | /** 416 | * Set an International Standard Book Number 417 | * 418 | * @param string $isbn 419 | */ 420 | public function setISBN( $isbn ) { 421 | if ( is_string( $isbn ) ) { 422 | $isbn = trim( str_replace('-', '', $isbn) ); 423 | if ( strlen($isbn) === 10 && is_numeric( substr($isbn, 0 , 9) ) ) { // published before 2007 424 | $verifysum = 0; 425 | $chars = str_split( $isbn ); 426 | for( $i=0; $i<9; $i++ ) { 427 | $verifysum += ( (int) $chars[$i] ) * (10 - $i); 428 | } 429 | $check_digit = 11 - ($verifysum % 11); 430 | if ( $check_digit == $chars[9] || ($chars[9] == 'X' && $check_digit == 10) ) 431 | $this->isbn = $isbn; 432 | } elseif ( strlen($isbn) === 13 && is_numeric( substr($isbn, 0, 12 ) ) ) { 433 | $verifysum = 0; 434 | $chars = str_split( $isbn ); 435 | for( $i=0; $i<12; $i++ ) { 436 | $verifysum += ( (int) $chars[$i] ) * ( ( ($i%2) ===0 ) ? 1:3 ); 437 | } 438 | $check_digit = 10 - ( $verifysum%10 ); 439 | if ( $check_digit == $chars[12] ) 440 | $this->isbn = $isbn; 441 | } 442 | } 443 | return $this; 444 | } 445 | 446 | /** 447 | * Book release date 448 | * 449 | * @return string release date in ISO 8601 format 450 | */ 451 | public function getReleaseDate() { 452 | return $this->release_date; 453 | } 454 | 455 | /** 456 | * Set the book release date 457 | * 458 | * @param DateTime|string $release_date release date as DateTime or as an ISO 8601 formatted string 459 | */ 460 | public function setReleaseDate( $release_date ) { 461 | if ( $release_date instanceof DateTime ) 462 | $this->release_date = static::datetime_to_iso_8601($release_date); 463 | else if ( is_string($release_date) && strlen($release_date) >= 10 ) // at least YYYY-MM-DD 464 | $this->release_date = $release_date; 465 | return $this; 466 | } 467 | 468 | /** 469 | * Book subject tags 470 | * 471 | * @return array Topic tags 472 | */ 473 | public function getTags() { 474 | return $this->tag; 475 | } 476 | 477 | /** 478 | * Add a book topic tag 479 | * 480 | * @param string $tag topic tag 481 | */ 482 | public function addTag( $tag ) { 483 | if ( is_string($tag) && !empty($tag) && !in_array($tag, $this->tag) ) 484 | $this->tag[] = $tag; 485 | return $this; 486 | } 487 | } 488 | 489 | /** 490 | * Video movie, TV show, and other all share the same properies. 491 | * Video episode extends this class to associate with a TV show 492 | * 493 | * @link http://ogp.me/#type_video Open Graph protocol video object 494 | */ 495 | class OpenGraphProtocolVideoObject extends OpenGraphProtocolObject { 496 | /** 497 | * Property prefix 498 | * @var string 499 | */ 500 | const PREFIX = 'video'; 501 | 502 | /** 503 | * prefix namespace 504 | * @var string 505 | */ 506 | const NS = 'http://ogp.me/ns/video#'; 507 | 508 | /** 509 | * Array of actor URLs 510 | * @var array 511 | */ 512 | protected $actor; 513 | 514 | /** 515 | * Array of director URLs 516 | * @var array 517 | */ 518 | protected $director; 519 | 520 | /** 521 | * Array of writer URIs 522 | * @var array 523 | */ 524 | protected $writer; 525 | 526 | /** 527 | * Video duration in whole seconds 528 | * @var int 529 | */ 530 | protected $duration; 531 | 532 | /** 533 | * The date the movie was first released. ISO 8601 formatted string 534 | * @var string 535 | */ 536 | protected $release_date; 537 | 538 | /** 539 | * Tag words associated with the movie 540 | * @var array 541 | */ 542 | protected $tag; 543 | 544 | public function __construct() { 545 | $this->actor = array(); 546 | $this->director = array(); 547 | $this->writer = array(); 548 | $this->tag = array(); 549 | } 550 | 551 | /** 552 | * Get an array of actor URLs 553 | * 554 | * @return array actor URLs 555 | */ 556 | public function getActors() { 557 | return $this->actor; 558 | } 559 | 560 | /** 561 | * Add an actor URL with an optional role association 562 | * 563 | * @param string $url Author URL of og:type profile 564 | * @param string $role The role the given actor played in this video work. 565 | */ 566 | public function addActor( $url, $role='' ) { 567 | if ( static::is_valid_url($url) && !in_array($url, $this->actor) ) { 568 | if ( !empty($role) && is_string($role) ) 569 | $this->actor[] = array( $url, 'role' => $role ); 570 | else 571 | $this->actor[] = $url; 572 | } 573 | return $this; 574 | } 575 | 576 | /** 577 | * An array of director URLs 578 | * 579 | * @return array director URLs 580 | */ 581 | public function getDirectors() { 582 | return $this->director; 583 | } 584 | 585 | /** 586 | * Add a director profile URL 587 | * 588 | * @param string $url director profile URL 589 | */ 590 | public function addDirector( $url ) { 591 | if ( static::is_valid_url($url) && !in_array($url, $this->director) ) 592 | $this->director[] = $url; 593 | return $this; 594 | } 595 | 596 | /** 597 | * An array of writer URLs 598 | * 599 | * @return array writer URLs 600 | */ 601 | public function getWriters() { 602 | return $this->writer; 603 | } 604 | 605 | /** 606 | * Add a writer profile URL 607 | * 608 | * @param string $url writer profile URL 609 | * 610 | * @return OpenGraphProtocolVideoObject 611 | */ 612 | public function addWriter( $url ) { 613 | if ( static::is_valid_url($url) && !in_array($url, $this->writer) ) 614 | $this->writer[] = $url; 615 | 616 | return $this; 617 | } 618 | 619 | /** 620 | * Duration of the video in whole seconds 621 | * 622 | * @return int duration in whole seconds 623 | */ 624 | public function getDuration() { 625 | return $this->duration; 626 | } 627 | 628 | /** 629 | * Set the video duration in whole seconds 630 | * 631 | * @param int $duration video duration in whole seconds 632 | */ 633 | public function setDuration( $duration ) { 634 | if ( is_int($duration) && $duration > 0 ) 635 | $this->duration = $duration; 636 | return $this; 637 | } 638 | 639 | /** 640 | * The release date as an ISO 8601 formatted string 641 | * 642 | * @return string release date as an ISO 8601 formatted string 643 | */ 644 | public function getReleaseDate() { 645 | return $this->release_date; 646 | } 647 | 648 | /** 649 | * Set the date this video was first released 650 | * 651 | * @param DateTime|string $release_date date video was first released 652 | */ 653 | public function setReleaseDate( $release_date ) { 654 | if ( $release_date instanceof DateTime ) 655 | $this->release_date = static::datetime_to_iso_8601($release_date); 656 | else if ( is_string($release_date) && strlen($release_date) >= 10 ) // at least YYYY-MM-DD 657 | $this->release_date = $release_date; 658 | return $this; 659 | } 660 | 661 | /** 662 | * An array of tag words associated with this video 663 | * 664 | * @return array tags 665 | */ 666 | public function getTags() { 667 | return $this->tag; 668 | } 669 | 670 | /** 671 | * Add a tag word or topic related to this video 672 | * 673 | * @param string $tag tag word or topic 674 | */ 675 | public function addTag( $tag ) { 676 | if ( is_string($tag) && !in_array($tag, $this->tag) ) 677 | $this->tag[] = $tag; 678 | return $this; 679 | } 680 | } 681 | 682 | /** 683 | * @link http://ogp.me/#type_video.episode Video episode 684 | */ 685 | class OpenGraphProtocolVideoEpisode extends OpenGraphProtocolVideoObject { 686 | /** 687 | * URL of a video.tv_show which this episode belongs to 688 | * @var string 689 | */ 690 | protected $series; 691 | 692 | /** 693 | * URL of a video.tv_show which this episode belongs to 694 | */ 695 | public function getSeries() { 696 | return $this->series; 697 | } 698 | 699 | /** 700 | * Set the URL of a video.tv_show which this episode belongs to 701 | * 702 | * @param string $url URL of a video.tv_show 703 | */ 704 | public function setSeries( $url ) { 705 | if ( static::is_valid_url($url) ) 706 | $this->series = $url; 707 | return $this; 708 | } 709 | } 710 | ?> -------------------------------------------------------------------------------- /open-graph-protocol.php: -------------------------------------------------------------------------------- 1 | 7 | * @version 1.3 8 | * @copyright Public Domain 9 | */ 10 | 11 | /** 12 | * Open Graph protocol type labels are passed through gettext message interpreters for the current context. 13 | * Fake the interpreter function alias if not defined 14 | */ 15 | if ( !function_exists('_') ): 16 | function _( $text, $domain='' ) { 17 | return $text; 18 | } 19 | endif; 20 | 21 | /** 22 | * Validate inputted text against Open Graph Protocol requirements by parameter. 23 | * 24 | * @link http://ogp.me/ Open Graph Protocol 25 | * @version 1.3 26 | */ 27 | class OpenGraphProtocol { 28 | /** 29 | * Version 30 | * @var string 31 | */ 32 | const VERSION = '1.3'; 33 | 34 | /** 35 | * Should we remotely request each referenced URL to make sure it exists and returns the expected Internet media type? 36 | * @var bool 37 | */ 38 | const VERIFY_URLS = false; 39 | 40 | /** 41 | * Meta attribute name. Use 'property' if you prefer RDF or 'name' if you prefer HTML validation 42 | * @var string 43 | */ 44 | const META_ATTR = 'property'; 45 | 46 | /** 47 | * Property prefix 48 | * @var string 49 | */ 50 | const PREFIX = 'og'; 51 | 52 | /** 53 | * prefix namespace 54 | * @var string 55 | */ 56 | const NS = 'http://ogp.me/ns#'; 57 | 58 | /** 59 | * Page classification according to a pre-defined set of base types. 60 | * 61 | * @var string 62 | * @since 1.0 63 | */ 64 | protected $type; 65 | 66 | /** 67 | * The title of your object as it should appear within the graph. 68 | * 69 | * @var string 70 | * @since 1.0 71 | */ 72 | protected $title; 73 | 74 | /** 75 | * If your object is part of a larger web site, the name which should be displayed for the overall site. 76 | * 77 | * @var string 78 | * @since 1.0 79 | */ 80 | protected $site_name; 81 | 82 | /** 83 | * A one to two sentence description of your object. 84 | * 85 | * @var string 86 | * @since 1.0 87 | */ 88 | protected $description; 89 | 90 | /** 91 | * The canonical URL of your object that will be used as its permanent ID in the graph. 92 | * 93 | * @var string 94 | * @since 1.0 95 | */ 96 | protected $url; 97 | 98 | /** 99 | * The word that appears before this object's title in a sentence 100 | * 101 | * @var string 102 | * @since 1.3 103 | */ 104 | protected $determiner; 105 | 106 | /** 107 | * Language and optional territory of page content. 108 | * @var string 109 | * @since 1.3 110 | */ 111 | protected $locale; 112 | 113 | /** 114 | * An array of OpenGraphProtocolImage objects 115 | * 116 | * @var array 117 | * @since 1.0 118 | */ 119 | protected $image; 120 | 121 | /** 122 | * An array of OpenGraphProtocolAudio objects 123 | * 124 | * @var array 125 | * @since 1.2 126 | */ 127 | protected $audio; 128 | 129 | /** 130 | * An array of OpenGraphProtocolVideo objects 131 | * 132 | * @var array 133 | * @since 1.2 134 | */ 135 | protected $video; 136 | 137 | /** 138 | * Build Open Graph protocol HTML markup based on an array 139 | * 140 | * @param array $og associative array of OGP properties and values 141 | * @param string $prefix optional prefix to prepend to all properties 142 | */ 143 | public static function buildHTML( array $og, $prefix=self::PREFIX ) { 144 | if ( empty($og) ) 145 | return; 146 | 147 | $s = ''; 148 | foreach ( $og as $property => $content ) { 149 | if ( is_object( $content ) || is_array( $content ) ) { 150 | if ( is_object( $content ) ) 151 | $content = $content->toArray(); 152 | if ( empty($property) || !is_string($property) ) 153 | $s .= static::buildHTML( $content, $prefix ); 154 | else 155 | $s .= static::buildHTML( $content, $prefix . ':' . $property ); 156 | } elseif ( !empty($content) ) { 157 | $s .= '' . PHP_EOL; 161 | } 162 | } 163 | return $s; 164 | } 165 | 166 | /** 167 | * A list of allowed page types in the Open Graph Protocol 168 | * 169 | * @param Bool $flatten true for grouped types one level deep 170 | * @link http://ogp.me/#types Open Graph Protocol object types 171 | * @return array Array of Open Graph Protocol object types 172 | */ 173 | public static function supported_types( $flatten=false ) { 174 | $types = array( 175 | _('Activities') => array( 176 | 'activity' => _('Activity'), 177 | 'sport' => _('Sport') 178 | ), 179 | _('Businesses') => array( 180 | 'company' => _('Company'), 181 | 'bar' => _('Bar'), 182 | 'cafe' => _('Cafe'), 183 | 'hotel' => _('Hotel'), 184 | 'restaurant' => _('Restaurant') 185 | ), 186 | _('Groups') => array( 187 | 'cause' => _('Cause'), 188 | 'sports_league' => _('Sports league'), 189 | 'sports_team' => _('Sports team') 190 | ), 191 | _('Organizations') => array( 192 | 'band' => _('Band'), 193 | 'government' => _('Government'), 194 | 'non_profit' => _('Non-profit'), 195 | 'school' => _('School'), 196 | 'university' => _('University') 197 | ), 198 | _('People') => array( 199 | 'actor' => _('Actor or actress'), 200 | 'athlete' => _('Athlete'), 201 | 'author' => _('Author'), 202 | 'director' => _('Director'), 203 | 'musician' => _('Musician'), 204 | 'politician' => _('Politician'), 205 | 'profile' => _('Profile'), 206 | 'public_figure' => _('Public Figure') 207 | ), 208 | _('Places') => array( 209 | 'city' => _('City or locality'), 210 | 'country' => _('Country'), 211 | 'landmark' => _('Landmark'), 212 | 'state_province' => _('State or province') 213 | ), 214 | _('Products and Entertainment') => array( 215 | 'music.album' => _('Music Album'), 216 | 'book' => _('Book'), 217 | 'drink' => _('Drink'), 218 | 'video.episode' => _('Video episode'), 219 | 'food' => _('Food'), 220 | 'game' => _('Game'), 221 | 'video.movie' => _('Movie'), 222 | 'music.playlist' => _('Music playlist'), 223 | 'product' => _('Product'), 224 | 'music.radio_station' => _('Radio station'), 225 | 'music.song' => _('Song'), 226 | 'video.tv_show' => _('Television show'), 227 | 'video.other' => _('Video') 228 | ), 229 | _('Websites') => array( 230 | 'article' => _('Article'), 231 | 'blog' => _('Blog'), 232 | 'website' => _('Website') 233 | ) 234 | ); 235 | if ( $flatten === true ) { 236 | $types_values = array(); 237 | foreach ( $types as $category=>$values ) { 238 | $types_values = array_merge( $types_values, array_keys($values) ); 239 | } 240 | return $types_values; 241 | } else { 242 | return $types; 243 | } 244 | } 245 | 246 | /** 247 | * Facebook maps languages to a default territory and only accepts locales in this list. A few popular languages such as English and French support multiple territories. 248 | * Map the Facebook list to avoid throwing errors in Facebook parsers that prevent further content indexing 249 | * 250 | * @link https://www.facebook.com/translations/FacebookLocales.xml Facebook locales 251 | * @param bool $keys_only return only keys 252 | * @return array associative array of locale code and locale name. locale code is in the format language_TERRITORY where language is an ISO 639-1 alpha-2 code and territory is an ISO 3166-1 alpha-2 code with special regions 'AR' and 'LA' for Arab region and Latin America respectively. 253 | */ 254 | public static function supported_locales( $keys_only=false ) { 255 | $locales = array( 256 | 'af_ZA' => _('Afrikaans'), 257 | 'ak_GH' => _('Akan'), 258 | 'am_ET' => _('Amharic'), 259 | 'ar_AR' => _('Arabic'), 260 | 'as_IN' => _('Assamese'), 261 | 'ay_BO' => _('Aymara'), 262 | 'az_AZ' => _('Azerbaijani'), 263 | 'be_BY' => _('Belarusian'), 264 | 'bg_BG' => _('Bulgarian'), 265 | 'bn_IN' => _('Bengali'), 266 | 'br_FR' => _('Breton'), 267 | 'bs_BA' => _('Bosnian'), 268 | 'ca_ES' => _('Catalan'), 269 | 'cb_IQ' => _('Sorani Kurdish'), 270 | 'ck_US' => _('Cherokee'), 271 | 'co_FR' => _('Corsican'), 272 | 'cs_CZ' => _('Czech'), 273 | 'cx_PH' => _('Cebuano'), 274 | 'cy_GB' => _('Welsh'), 275 | 'da_DK' => _('Danish'), 276 | 'de_DE' => _('German'), 277 | 'el_GR' => _('Greek'), 278 | 'en_GB' => _('English (UK)'), 279 | 'en_IN' => _('English (India)'), 280 | 'en_US' => _('English (US)'), 281 | 'eo_EO' => _('Esperanto'), 282 | 'es_CO' => _('Spanish (Colombia)'), 283 | 'es_ES' => _('Spanish (Spain)'), 284 | 'es_LA' => _('Spanish'), 285 | 'et_EE' => _('Estonian'), 286 | 'eu_ES' => _('Basque'), 287 | 'fa_IR' => _('Persian'), 288 | 'ff_NG' => _('Fulah'), 289 | 'fi_FI' => _('Finnish'), 290 | 'fo_FO' => _('Faroese'), 291 | 'fr_CA' => _('French (Canada)'), 292 | 'fr_FR' => _('French (France)'), 293 | 'fy_NL' => _('Frisian'), 294 | 'ga_IE' => _('Irish'), 295 | 'gl_ES' => _('Galician'), 296 | 'gn_PY' => _('Guarani'), 297 | 'gu_IN' => _('Gujarati'), 298 | 'gx_GR' => _('Classical Greek'), 299 | 'ha_NG' => _('Hausa'), 300 | 'he_IL' => _('Hebrew'), 301 | 'hi_IN' => _('Hindi'), 302 | 'hr_HR' => _('Croatian'), 303 | 'hu_HU' => _('Hungarian'), 304 | 'hy_AM' => _('Armenian'), 305 | 'id_ID' => _('Indonesian'), 306 | 'ig_NG' => _('Igbo'), 307 | 'is_IS' => _('Icelandic'), 308 | 'it_IT' => _('Italian'), 309 | 'ja_JP' => _('Japanese'), 310 | 'ja_KS' => _('Japanese (Kansai)'), 311 | 'jv_ID' => _('Javanese'), 312 | 'ka_GE' => _('Georgian'), 313 | 'kk_KZ' => _('Kazakh'), 314 | 'km_KH' => _('Khmer'), 315 | 'kn_IN' => _('Kannada'), 316 | 'ko_KR' => _('Korean'), 317 | 'ku_TR' => _('Kurdish (Kurmanji)'), 318 | 'la_VA' => _('Latin'), 319 | 'lg_UG' => _('Ganda'), 320 | 'li_NL' => _('Limburgish'), 321 | 'ln_CD' => _('Lingala'), 322 | 'lo_LA' => _('Lao'), 323 | 'lt_LT' => _('Lithuanian'), 324 | 'lv_LV' => _('Latvian'), 325 | 'mg_MG' => _('Malagasy'), 326 | 'mk_MK' => _('Macedonian'), 327 | 'ml_IN' => _('Malayalam'), 328 | 'mn_MN' => _('Mongolian'), 329 | 'mr_IN' => _('Marathi'), 330 | 'ms_MY' => _('Malay'), 331 | 'mt_MT' => _('Maltese'), 332 | 'my_MM' => _('Burmese'), 333 | 'nb_NO' => _('Norwegian (bokmal)'), 334 | 'nd_ZW' => _('Ndebele'), 335 | 'ne_NP' => _('Nepali'), 336 | 'nl_BE' => _('Dutch (België)'), 337 | 'nl_NL' => _('Dutch'), 338 | 'nn_NO' => _('Norwegian (nynorsk)'), 339 | 'ny_MW' => _('Chewa'), 340 | 'or_IN' => _('Oriya'), 341 | 'pa_IN' => _('Punjabi'), 342 | 'pl_PL' => _('Polish'), 343 | 'ps_AF' => _('Pashto'), 344 | 'pt_BR' => _('Portuguese (Brazil)'), 345 | 'pt_PT' => _('Portuguese (Portugal)'), 346 | 'qu_PE' => _('Quechua'), 347 | 'rm_CH' => _('Romansh'), 348 | 'ro_RO' => _('Romanian'), 349 | 'ru_RU' => _('Russian'), 350 | 'rw_RW' => _('Kinyarwanda'), 351 | 'sa_IN' => _('Sanskrit'), 352 | 'sc_IT' => _('Sardinian'), 353 | 'se_NO' => _('Northern Sámi'), 354 | 'si_LK' => _('Sinhala'), 355 | 'sk_SK' => _('Slovak'), 356 | 'sl_SI' => _('Slovenian'), 357 | 'sn_ZW' => _('Shona'), 358 | 'so_SO' => _('Somali'), 359 | 'sq_AL' => _('Albanian'), 360 | 'sr_RS' => _('Serbian'), 361 | 'sv_SE' => _('Swedish'), 362 | 'sw_KE' => _('Swahili'), 363 | 'sy_SY' => _('Syriac'), 364 | 'sz_PL' => _('Silesian'), 365 | 'ta_IN' => _('Tamil'), 366 | 'te_IN' => _('Telugu'), 367 | 'tg_TJ' => _('Tajik'), 368 | 'th_TH' => _('Thai'), 369 | 'tk_TM' => _('Turkmen'), 370 | 'tl_PH' => _('Filipino'), 371 | 'tr_TR' => _('Turkish'), 372 | 'tt_RU' => _('Tatar'), 373 | 'tz_MA' => _('Tamazight'), 374 | 'uk_UA' => _('Ukrainian'), 375 | 'ur_PK' => _('Urdu'), 376 | 'uz_UZ' => _('Uzbek'), 377 | 'vi_VN' => _('Vietnamese'), 378 | 'wo_SN' => _('Wolof'), 379 | 'xh_ZA' => _('Xhosa'), 380 | 'yi_DE' => _('Yiddish'), 381 | 'yo_NG' => _('Yoruba'), 382 | 'zh_CN' => _('Simplified Chinese (China)'), 383 | 'zh_HK' => _('Traditional Chinese (Hong Kong)'), 384 | 'zh_TW' => _('Traditional Chinese (Taiwan)'), 385 | 'zu_ZA' => _('Zulu'), 386 | 'zz_TR' => _('Zazaki') 387 | ); 388 | if ( $keys_only === true ) { 389 | return array_keys($locales); 390 | } else { 391 | return $locales; 392 | } 393 | } 394 | 395 | /** 396 | * Cleans a URL string, then checks to see if a given URL is addressable, returns a 200 OK response, and matches the accepted Internet media types (if provided). 397 | * 398 | * @param string $url Publicly addressable URL 399 | * @param array $accepted_mimes Given URL correspond to an accepted Internet media (MIME) type. 400 | * @return string cleaned URL string, or empty string on failure. 401 | */ 402 | public static function is_valid_url( $url, array $accepted_mimes = array() ) { 403 | if ( !is_string( $url ) || empty( $url ) ) 404 | return ''; 405 | 406 | /* 407 | * Validate URI string by letting PHP break up the string and put it back together again 408 | * Excludes username:password and port number URI parts 409 | */ 410 | $url_parts = parse_url( $url ); 411 | $url = ''; 412 | if ( isset( $url_parts['scheme'] ) && in_array( $url_parts['scheme'], array('http', 'https'), true ) ) { 413 | $url = "{$url_parts['scheme']}://{$url_parts['host']}{$url_parts['path']}"; 414 | if ( empty( $url_parts['path'] ) ) 415 | $url .= '/'; 416 | if ( !empty( $url_parts['query'] ) ) 417 | $url .= '?' . $url_parts['query']; 418 | if ( !empty( $url_parts['fragment'] ) ) 419 | $url .= '#' . $url_parts['fragment']; 420 | } 421 | 422 | if ( !empty( $url ) ) { 423 | // test if URL exists 424 | $ch = curl_init( $url ); 425 | curl_setopt( $ch, CURLOPT_TIMEOUT, 5 ); 426 | curl_setopt( $ch, CURLOPT_FORBID_REUSE, true ); 427 | curl_setopt( $ch, CURLOPT_NOBODY, true ); // HEAD 428 | curl_setopt( $ch, CURLOPT_USERAGENT, 'Open Graph protocol validator ' . self::VERSION . ' (+http://ogp.me/)' ); 429 | if ( !empty($accepted_mimes) ) 430 | curl_setopt( $ch, CURLOPT_HTTPHEADER, array( 'Accept: ' . implode( ',', $accepted_mimes ) ) ); 431 | $response = curl_exec( $ch ); 432 | if ( curl_getinfo( $ch, CURLINFO_HTTP_CODE ) == 200 ) { 433 | if ( !empty($accepted_mimes) ) { 434 | $content_type = explode( ';', curl_getinfo( $ch, CURLINFO_CONTENT_TYPE ) ); 435 | if ( empty( $content_type ) || !in_array( $content_type[0], $accepted_mimes ) ) 436 | return ''; 437 | } 438 | } else { 439 | return ''; 440 | } 441 | } 442 | return $url; 443 | } 444 | 445 | /** 446 | * Output the OpenGraphProtocol object as HTML elements string 447 | * 448 | * @return string meta elements 449 | */ 450 | public function toHTML() { 451 | return rtrim( static::buildHTML( get_object_vars($this) ), PHP_EOL ); 452 | } 453 | 454 | /** 455 | * @return String the type slug 456 | */ 457 | public function getType() { 458 | return $this->type; 459 | } 460 | 461 | /** 462 | * 463 | * @param String type slug 464 | */ 465 | public function setType( $type ) { 466 | if ( is_string($type) && in_array( $type, self::supported_types(true), true ) ) 467 | $this->type = $type; 468 | return $this; 469 | } 470 | 471 | /** 472 | * @return String document title 473 | */ 474 | public function getTitle() { 475 | return $this->title; 476 | } 477 | 478 | /** 479 | * @param String $title document title 480 | */ 481 | public function setTitle( $title ) { 482 | if ( is_string($title) ) { 483 | $title = trim( $title ); 484 | if ( strlen( $title ) > 128 ) 485 | $this->title = substr( $title, 0, 128 ); 486 | else 487 | $this->title = $title; 488 | } 489 | return $this; 490 | } 491 | 492 | /** 493 | * @return String Site name 494 | */ 495 | public function getSiteName() { 496 | return $this->site_name; 497 | } 498 | 499 | /** 500 | * @param String $site_name Site name 501 | */ 502 | public function setSiteName( $site_name ) { 503 | if ( is_string($site_name) && !empty($site_name) ) { 504 | $site_name = trim( $site_name ); 505 | if ( strlen( $site_name ) > 128 ) 506 | $this->site_name = substr( $site_name, 0, 128 ); 507 | else 508 | $this->site_name = $site_name; 509 | } 510 | return $this; 511 | } 512 | 513 | /** 514 | * @return String Description 515 | */ 516 | public function getDescription() { 517 | return $this->description; 518 | } 519 | 520 | /** 521 | * @param String $description Document description 522 | */ 523 | public function setDescription( $description ) { 524 | if ( is_string($description) && !empty($description) ) { 525 | $description = trim( $description ); 526 | if ( strlen( $description ) > 255 ) 527 | $this->description = substr( $description, 0, 255 ); 528 | else 529 | $this->description = $description; 530 | } 531 | return $this; 532 | } 533 | 534 | /** 535 | * @return String URL 536 | */ 537 | public function getURL() { 538 | return $this->url; 539 | } 540 | 541 | /** 542 | * @param String $url Canonical URL 543 | */ 544 | public function setURL( $url ) { 545 | if ( is_string( $url ) && !empty( $url ) ) { 546 | $url = trim($url); 547 | if (self::VERIFY_URLS) { 548 | $url = self::is_valid_url( $url, array( 'text/html', 'application/xhtml+xml' ) ); 549 | } 550 | if ( !empty( $url ) ) 551 | $this->url = $url; 552 | } 553 | return $this; 554 | } 555 | 556 | /** 557 | * @return string the determiner 558 | */ 559 | public function getDeterminer() { 560 | return $this->determiner; 561 | } 562 | 563 | public function setDeterminer( $determiner ) { 564 | if ( in_array($determiner, array('a','an','auto','the'), true) ) 565 | $this->determiner = $determiner; 566 | return $this; 567 | } 568 | 569 | /** 570 | * @return string language_TERRITORY 571 | */ 572 | public function getLocale() { 573 | return $this->locale; 574 | } 575 | 576 | /** 577 | * @var string $locale locale in the format language_TERRITORY 578 | */ 579 | public function setLocale( $locale ) { 580 | if ( is_string($locale) && in_array($locale, static::supported_locales(true)) ) 581 | $this->locale = $locale; 582 | return $this; 583 | } 584 | 585 | /** 586 | * @return array OpenGraphProtocolImage array 587 | */ 588 | public function getImage() { 589 | return $this->image; 590 | } 591 | 592 | /** 593 | * Add an image. 594 | * The first image added is given priority by the Open Graph Protocol spec. Implementors may choose a different image based on size requirements or preferences. 595 | * 596 | * @param OpenGraphProtocolImage $image image object to add 597 | */ 598 | public function addImage( OpenGraphProtocolImage $image ) { 599 | $image_url = $image->getURL(); 600 | if ( empty($image_url) ) 601 | return; 602 | $image->removeURL(); 603 | $value = array( $image_url, array($image) ); 604 | if ( ! isset( $this->image ) ) 605 | $this->image = array( $value ); 606 | else 607 | $this->image[] = $value; 608 | return $this; 609 | } 610 | 611 | /** 612 | * @return array OpenGraphProtocolAudio objects 613 | */ 614 | public function getAudio() { 615 | return $this->audio; 616 | } 617 | 618 | /** 619 | * Add an audio reference 620 | * The first audio is given priority by the Open Graph protocol spec. 621 | * 622 | * @param OpenGraphProtocolAudio $audio audio object to add 623 | */ 624 | public function addAudio( OpenGraphProtocolAudio $audio ) { 625 | $audio_url = $audio->getURL(); 626 | if ( empty($audio_url) ) 627 | return; 628 | $audio->removeURL(); 629 | $value = array( $audio_url, array($audio) ); 630 | if ( ! isset($this->audio) ) 631 | $this->audio = array($value); 632 | else 633 | $this->audio[] = $value; 634 | return $this; 635 | } 636 | 637 | /** 638 | * @return array OpenGraphProtocolVideo objects 639 | */ 640 | public function getVideo() { 641 | return $this->video; 642 | } 643 | 644 | /** 645 | * Add a video reference 646 | * The first video is given priority by the Open Graph protocol spec. Implementors may choose a different video based on size requirements or preferences. 647 | * 648 | * @param OpenGraphProtocolVideo $video video object to add 649 | */ 650 | public function addVideo( OpenGraphProtocolVideo $video ) { 651 | $video_url = $video->getURL(); 652 | if ( empty($video_url) ) 653 | return; 654 | $video->removeURL(); 655 | $value = array( $video_url, array($video) ); 656 | if ( ! isset( $this->video ) ) 657 | $this->video = array( $value ); 658 | else 659 | $this->video[] = $value; 660 | return $this; 661 | } 662 | } 663 | 664 | include_once dirname(__FILE__) . '/media.php'; // image, video, audio 665 | include_once dirname(__FILE__) . '/objects.php'; // global objects: profile, article 666 | ?> --------------------------------------------------------------------------------