├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.markdown ├── composer.json ├── demo ├── images.html ├── reader.php ├── test.html ├── writer.html └── writer.php ├── phpunit.xml.dist ├── src └── Opengraph │ ├── Meta.php │ ├── Opengraph.php │ ├── Reader.php │ └── Writer.php └── tests └── Opengraph ├── MetaTest.php ├── ReaderTest.php └── WriterTest.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.yml] 11 | indent_size = 2 12 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.php diff=php 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build-test: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: php-actions/composer@v6 18 | - uses: php-actions/phpunit@v3 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | vendor 3 | composer.phar 4 | composer.lock 5 | .phpunit.result.cache 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - '7.1' 4 | - '7.2' 5 | - '7.3' 6 | - nightly 7 | - hhvm 8 | - hhvm-nightly 9 | sudo: false 10 | before_script: 11 | - curl -s https://getcomposer.org/installer | php 12 | - php composer.phar install --dev 13 | script: ./vendor/bin/phpunit -c phpunit.xml.dist 14 | matrix: 15 | allow_failures: 16 | - php: nightly 17 | - php: hhvm 18 | - php: hhvm-nightly 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Axel Etcheverry 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Opengraph 2 | 3 | [![Build Status](https://secure.travis-ci.org/euskadi31/Opengraph.png)](http://travis-ci.org/euskadi31/Opengraph) 4 | 5 | ## Test with [Atoum](https://github.com/atoum/atoum) 6 | 7 | cd Opengraph/ 8 | curl -s https://getcomposer.org/installer | php 9 | php composer.phar install --dev 10 | ./vendor/bin/phpunit -c phpunit.xml.dist 11 | 12 | ## Writer 13 | 14 | ```php 15 | append(Opengraph\Writer::OG_TITLE, 'The Rock'); 26 | $writer->append(Opengraph\Writer::OG_TYPE, Opengraph\Writer::TYPE_VIDEO_MOVIE); 27 | $writer->append(Opengraph\Writer::OG_URL, 'http://www.imdb.com/title/tt0117500/'); 28 | $writer->append(Opengraph\Writer::OG_IMAGE, 'http://ia.media-imdb.com/images/rock.jpg'); 29 | 30 | echo $writer->render() . PHP_EOL; 31 | 32 | ?> 33 | ``` 34 | 35 | Output 36 | 37 | ```html 38 | 39 | 40 | 41 | 42 | ``` 43 | 44 | ## Reader 45 | 46 | ```php 47 | parse(file_get_contents('http://www.imdb.com/title/tt0117500/')); 58 | print_r($reader->getArrayCopy()); 59 | 60 | ?> 61 | ``` 62 | 63 | Output 64 | 65 | Array 66 | ( 67 | [og:url] => http://www.imdb.com/title/tt0117500/ 68 | [og:title] => Rock (1996) 69 | [og:type] => video.movie 70 | [og:image] => Array 71 | ( 72 | [0] => Array 73 | ( 74 | [og:image:url] => http://ia.media-imdb.com/images/M/MV5BMTM3MTczOTM1OF5BMl5BanBnXkFtZTYwMjc1NDA5._V1._SX98_SY140_.jpg 75 | ) 76 | 77 | ) 78 | 79 | [og:site_name] => IMDb 80 | [fb:app_id] => 115109575169727 81 | ) 82 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "euskadi31/opengraph", 3 | "type": "library", 4 | "description": "A PHP 5.3+ framework for OpenGraph Protocol", 5 | "keywords": ["sdk","opengraph", "og", "OpenGraph Protocol"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Axel Etcheverry", 10 | "email": "axel@etcheverry.biz", 11 | "homepage": "http://www.axel-etcheverry.com" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=5.3.0" 16 | }, 17 | "require-dev": { 18 | "phpunit/phpunit": "9.5.26" 19 | }, 20 | "autoload": { 21 | "psr-0": { 22 | "Opengraph": "src/" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /demo/images.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | Skyfall (2012) - IMDb 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |

test

26 | 27 | -------------------------------------------------------------------------------- /demo/reader.php: -------------------------------------------------------------------------------- 1 | parse(file_get_contents('http://www.imdb.com/title/tt0117500/')); 13 | print_r($reader->getArrayCopy()); -------------------------------------------------------------------------------- /demo/test.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | Skyfall (2012) - IMDb 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |

test

24 | 25 | -------------------------------------------------------------------------------- /demo/writer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /demo/writer.php: -------------------------------------------------------------------------------- 1 | append(Opengraph\Writer::OG_TITLE, 'The Rock'); 13 | $writer->append(Opengraph\Writer::OG_TYPE, Opengraph\Writer::TYPE_VIDEO_MOVIE); 14 | $writer->append(Opengraph\Writer::OG_URL, 'http://www.imdb.com/title/tt0117500/'); 15 | $writer->append(Opengraph\Writer::OG_IMAGE, 'http://ia.media-imdb.com/images/rock.jpg'); 16 | 17 | echo $writer->render() . PHP_EOL; -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | . 6 | 7 | 8 | ./demo 9 | ./vendor 10 | 11 | 12 | 13 | 14 | ./tests 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Opengraph/Meta.php: -------------------------------------------------------------------------------- 1 | 5 | * @copyright Copyright (c) 2011 Axel Etcheverry (http://www.axel-etcheverry.com) 6 | * Displays MIT 7 | * @license http://creativecommons.org/licenses/MIT/deed.fr MIT 8 | */ 9 | 10 | /** 11 | * @namespace 12 | */ 13 | namespace Opengraph; 14 | 15 | class Meta 16 | { 17 | /** 18 | * @var String 19 | */ 20 | protected $_property; 21 | 22 | /** 23 | * @var Mixed 24 | */ 25 | protected $_content; 26 | 27 | /** 28 | * @param String $property 29 | * @param String $content 30 | * @return \Opengraph\Meta 31 | */ 32 | public function __construct($property, $content) 33 | { 34 | $this->_property = $property; 35 | $this->_content = $content; 36 | } 37 | 38 | /** 39 | * Set property 40 | * 41 | * @param String $property 42 | * @return \Opengraph\Meta 43 | */ 44 | public function setProperty($property) 45 | { 46 | $this->_property = $property; 47 | 48 | return $this; 49 | } 50 | 51 | /** 52 | * Get property name 53 | * 54 | * @return String 55 | */ 56 | public function getProperty() 57 | { 58 | return $this->_property; 59 | } 60 | 61 | /** 62 | * Set content 63 | * 64 | * @param String $content 65 | * @return \Opengraph\Meta 66 | */ 67 | public function setContent($content) 68 | { 69 | $this->_content = $content; 70 | 71 | return $this; 72 | } 73 | 74 | /** 75 | * Get content 76 | * 77 | * @return String 78 | */ 79 | public function getContent() 80 | { 81 | if(is_array($this->_content)) { 82 | return implode(',', $this->_content); 83 | } 84 | return $this->_content; 85 | } 86 | 87 | /** 88 | * Render meta tag 89 | * 90 | * @return String 91 | */ 92 | public function render() 93 | { 94 | return ''; 95 | } 96 | } -------------------------------------------------------------------------------- /src/Opengraph/Opengraph.php: -------------------------------------------------------------------------------- 1 | 5 | * @copyright Copyright (c) 2011 Axel Etcheverry (http://www.axel-etcheverry.com) 6 | * Displays MIT 7 | * @license http://creativecommons.org/licenses/MIT/deed.fr MIT 8 | */ 9 | 10 | /** 11 | * @namespace 12 | */ 13 | namespace Opengraph; 14 | 15 | use ArrayObject; 16 | use Iterator; 17 | use ArrayIterator; 18 | use Serializable; 19 | use Countable; 20 | 21 | abstract class Opengraph implements Iterator, Serializable, Countable 22 | { 23 | /** 24 | * Basic Metadata 25 | */ 26 | const OG_TITLE = 'og:title'; 27 | const OG_TYPE = 'og:type'; 28 | const OG_IMAGE = 'og:image'; 29 | const OG_URL = 'og:url'; 30 | 31 | /** 32 | * Optional Metadata 33 | */ 34 | const OG_IMAGE_SECURE_URL = 'og:image:secure_url'; 35 | const OG_IMAGE_TYPE = 'og:image:type'; 36 | const OG_IMAGE_WIDTH = 'og:image:width'; 37 | const OG_IMAGE_HEIGHT = 'og:image:height'; 38 | 39 | const OG_AUDIO = 'og:audio'; 40 | const OG_AUDIO_SECURE_URL = 'og:audio:secure_url'; 41 | const OG_AUDIO_TYPE = 'og:audio:type'; 42 | 43 | const OG_DESCRIPTION = 'og:description'; 44 | const OG_DETERMINER = 'og:determiner'; 45 | const OG_LOCALE = 'og:locale'; 46 | const OG_LOCALE_ALTERNATE = 'og:locale:alternate'; 47 | const OG_SITE_NAME = 'og:site_name'; 48 | 49 | const OG_VIDEO = 'og:video'; 50 | const OG_VIDEO_SECURE_URL = 'og:video:secure_url'; 51 | const OG_VIDEO_TYPE = 'og:video:type'; 52 | const OG_VIDEO_WIDTH = 'og:video:width'; 53 | const OG_VIDEO_HEIGHT = 'og:video:height'; 54 | 55 | const OG_AVAILABILITY = 'og:availability'; 56 | const OG_PRICE_AMOUNT = 'og:price:amount'; 57 | const OG_PRICE_CURRENCY = 'og:price:currency'; 58 | 59 | /** 60 | * Facebook Metadata 61 | */ 62 | const FB_ADMINS = 'fb:admins'; 63 | const FB_PAGE = 'fb:page_id'; 64 | const FB_APP = 'fb:app_id'; 65 | 66 | /** 67 | * DEPRECATED PROPERTIES! DO NOT READ ON UNLESS YOU MUST! 68 | */ 69 | const OG_LATITUDE = 'og:latitude'; 70 | const OG_LONGITUDE = 'og:longitude'; 71 | const OG_STREET_ADDRESS = 'og:street-address'; 72 | const OG_LOCALITY = 'og:locality'; 73 | const OG_REGION = 'og:region'; 74 | const OG_POSTAL_CODE = 'og:postal-code'; 75 | const OG_COUNTRY_NAME = 'og:country-name'; 76 | const OG_EMAIL = 'og:email'; 77 | const OG_PHONE_NUMBER = 'og:phone_number'; 78 | const OG_FAX_NUMBER = 'og:fax_number'; 79 | const OG_ISBN = 'og:isbn'; 80 | const OG_UPC = 'og:upc'; 81 | const OG_AUDIO_TITLE = 'og:audio:title'; 82 | const OG_AUDIO_ARTIST = 'og:audio:artist'; 83 | const OG_AUDIO_ALBUM = 'og:audio:album'; 84 | 85 | /** 86 | * Types 87 | */ 88 | const TYPE_MUSIC_SONG = 'music.song'; 89 | const TYPE_MUSIC_ALBUM = 'music.album'; 90 | const TYPE_MUSIC_PLAYLIST = 'music.playlist'; 91 | const TYPE_MUSIC_RADIOSTATION = 'music.radio_station'; 92 | 93 | const TYPE_VIDEO_MOVIE = 'video.movie'; 94 | const TYPE_VIDEO_EPISODE = 'video.episode'; 95 | const TYPE_VIDEO_TVSHOW = 'video.tv_show'; 96 | const TYPE_VIDEO_OTHER = 'video.other'; 97 | 98 | const TYPE_ARTICLE = 'article'; 99 | const TYPE_BOOK = 'book'; 100 | const TYPE_PROFILE = 'profile'; 101 | const TYPE_WEBSITE = 'website'; 102 | 103 | const TYPE_PRODUCT = 'product'; 104 | const TYPE_PRODUCT_GROUP = 'product.group'; 105 | const TYPE_PRODUCT_ITEM = 'product.item'; 106 | 107 | /** 108 | * Article content fields 109 | */ 110 | const ARTICLE_PUBLISHED_TIME = 'article:published_time'; 111 | const ARTICLE_MODIFIED_TIME = 'article:modified_time'; 112 | const ARTICLE_AUTHOR = 'article:author'; 113 | const ARTICLE_SECTION = 'article:section'; 114 | const ARTICLE_TAG = 'article:tag'; 115 | 116 | /** 117 | * Positions 118 | */ 119 | const APPEND = 'append'; 120 | const PREPEND = 'prepend'; 121 | 122 | /** 123 | * @var \ArrayObject 124 | */ 125 | protected static $storage; 126 | 127 | /** 128 | * @var Integer 129 | */ 130 | protected $_position = 0; 131 | 132 | 133 | public function __construct() 134 | { 135 | if(is_null(static::$storage)) { 136 | static::$storage = new ArrayObject(); 137 | //static::$position = 0; 138 | } 139 | } 140 | 141 | /** 142 | * Add meta 143 | * 144 | * @param String $property 145 | * @param Mixed $content 146 | * @param String $position 147 | * @return \Opengraph\Opengraph 148 | */ 149 | public function addMeta($property, $content, $position) 150 | { 151 | $content = $this->_normalizeContent($property, $content); 152 | 153 | switch($property) { 154 | case self::OG_TITLE: 155 | case self::OG_TYPE: 156 | case self::OG_DESCRIPTION: 157 | case self::OG_LOCALE: 158 | case self::OG_SITE_NAME: 159 | case self::OG_URL: 160 | if($this->hasMeta($property)) { 161 | $this->removeMeta($property); 162 | //$this->getMeta($property)->setContent($content); 163 | } 164 | break; 165 | } 166 | 167 | if($position == self::APPEND) { 168 | static::$storage->append(new Meta($property, $content)); 169 | } else { 170 | $values = static::$storage->getArrayCopy(); 171 | array_unshift($values, new Meta($property, $content)); 172 | static::$storage->exchangeArray($values); 173 | unset($values); 174 | } 175 | 176 | return $this; 177 | } 178 | 179 | /** 180 | * Check is meta exists 181 | * 182 | * @param String $property 183 | * @return Boolean 184 | */ 185 | public function hasMeta($property) 186 | { 187 | foreach(static::$storage as $meta) { 188 | if($meta->getProperty() == $property) { 189 | return true; 190 | } 191 | } 192 | 193 | return false; 194 | } 195 | 196 | /** 197 | * Get meta by property name 198 | * 199 | * @param String $property 200 | * @return \Opengraph\Meta 201 | */ 202 | public function getMeta($property) 203 | { 204 | foreach(static::$storage as $meta) { 205 | if($meta->getProperty() == $property) { 206 | return $meta->getContent(); 207 | } 208 | } 209 | 210 | return false; 211 | } 212 | 213 | /** 214 | * Remove meta 215 | * 216 | * @param String $property 217 | * @return Boolean 218 | */ 219 | public function removeMeta($property) 220 | { 221 | foreach(static::$storage as $i => $meta) { 222 | if($meta->getProperty() == $property) { 223 | unset(static::$storage[$i]); 224 | return true; 225 | } 226 | } 227 | 228 | return false; 229 | } 230 | 231 | /** 232 | * @return \ArrayObject 233 | */ 234 | public function getMetas() 235 | { 236 | return static::$storage; 237 | } 238 | 239 | /** 240 | * Normalize content 241 | * 242 | * @param String $property 243 | * @param Mixed $content 244 | * @return Mixed 245 | */ 246 | protected function _normalizeContent($property, $content) 247 | { 248 | if($property == self::FB_ADMINS && is_string($content)) { 249 | return (array)explode(',', $content); 250 | } 251 | 252 | return $content; 253 | } 254 | 255 | /** 256 | * Get array 257 | * 258 | * @return Array 259 | */ 260 | public function getArrayCopy() 261 | { 262 | $graph = array(); 263 | 264 | $metas = static::$storage->getArrayCopy(); 265 | 266 | foreach($metas as $i => $meta) { 267 | 268 | $property = $meta->getProperty(); 269 | $content = $meta->getContent(); 270 | 271 | switch($property) { 272 | 273 | case self::OG_IMAGE: 274 | 275 | $data = array( 276 | $property . ':url' => $content 277 | ); 278 | 279 | for($j = ($i+1); $j <= ($i+4); $j++) { 280 | 281 | if(isset($metas[$j])) { 282 | $next = $metas[$j]; 283 | 284 | if(!empty($next)) { 285 | $nextProperty = $next->getProperty(); 286 | 287 | switch($nextProperty) { 288 | case self::OG_IMAGE_SECURE_URL: 289 | case self::OG_IMAGE_HEIGHT: 290 | case self::OG_IMAGE_WIDTH: 291 | case self::OG_IMAGE_TYPE: 292 | if(!isset($data[$nextProperty])) { 293 | $data[$nextProperty] = $next->getContent(); 294 | unset($metas[$j]); 295 | } 296 | break; 297 | } 298 | } 299 | } 300 | } 301 | 302 | if(!isset($graph[$property])) { 303 | $graph[$property] = array(); 304 | } 305 | 306 | $graph[$property][] = $data; 307 | unset($data); 308 | 309 | break; 310 | 311 | case self::OG_VIDEO: 312 | $data = array( 313 | $property . ':url' => $content 314 | ); 315 | 316 | for($j = ($i+1); $j <= ($i+4); $j++) { 317 | if(isset($metas[$j])) { 318 | $next = $metas[$j]; 319 | if(!empty($next)) { 320 | $nextProperty = $next->getProperty(); 321 | 322 | switch($nextProperty) { 323 | case self::OG_VIDEO_SECURE_URL: 324 | case self::OG_VIDEO_HEIGHT: 325 | case self::OG_VIDEO_WIDTH: 326 | case self::OG_VIDEO_TYPE: 327 | if(!isset($data[$nextProperty])) { 328 | $data[$nextProperty] = $next->getContent(); 329 | unset($metas[$j]); 330 | } 331 | break; 332 | } 333 | } 334 | } 335 | } 336 | 337 | if(!isset($graph[$property])) { 338 | $graph[$property] = array(); 339 | } 340 | 341 | $graph[$property][] = $data; 342 | unset($data); 343 | 344 | break; 345 | 346 | case self::OG_AUDIO: 347 | $data = array( 348 | $property . ':url' => $content 349 | ); 350 | 351 | for($j = ($i+1); $j <= ($i+2); $j++) { 352 | if(isset($metas[$j])) { 353 | $next = $metas[$j]; 354 | if(!empty($next)) { 355 | $nextProperty = $next->getProperty(); 356 | 357 | switch($nextProperty) { 358 | case self::OG_AUDIO_SECURE_URL: 359 | case self::OG_AUDIO_TYPE: 360 | if(!isset($data[$nextProperty])) { 361 | $data[$nextProperty] = $next->getContent(); 362 | unset($metas[$j]); 363 | } 364 | break; 365 | } 366 | } 367 | } 368 | } 369 | 370 | if(!isset($graph[$property])) { 371 | $graph[$property] = array(); 372 | } 373 | 374 | $graph[$property][] = $data; 375 | unset($data); 376 | 377 | break; 378 | 379 | default: 380 | $denyProperties = array( 381 | self::OG_AUDIO_SECURE_URL, 382 | self::OG_AUDIO_TYPE, 383 | self::OG_VIDEO_SECURE_URL, 384 | self::OG_VIDEO_HEIGHT, 385 | self::OG_VIDEO_WIDTH, 386 | self::OG_VIDEO_TYPE, 387 | self::OG_IMAGE_SECURE_URL, 388 | self::OG_IMAGE_HEIGHT, 389 | self::OG_IMAGE_WIDTH, 390 | self::OG_IMAGE_TYPE 391 | ); 392 | 393 | if(!in_array($property, $denyProperties)) { 394 | $graph[$property] = $content; 395 | unset($metas[$i]); 396 | } 397 | 398 | } 399 | } 400 | unset($metas); 401 | 402 | return $graph; 403 | } 404 | 405 | /** 406 | * Rewind the Iterator to the first element 407 | * 408 | * @return void 409 | */ 410 | public function rewind(): void 411 | { 412 | $this->_position = 0; 413 | } 414 | 415 | /** 416 | * Return the current element 417 | * 418 | * @return mixed 419 | */ 420 | public function current(): mixed 421 | { 422 | return static::$storage[$this->_position]; 423 | } 424 | 425 | /** 426 | * Return the key of the current element 427 | * 428 | * @return scalar 429 | */ 430 | public function key(): mixed 431 | { 432 | return $this->_position; 433 | } 434 | 435 | /** 436 | * Move forward to next element 437 | * 438 | * @return void 439 | */ 440 | public function next(): void 441 | { 442 | ++$this->_position; 443 | } 444 | 445 | /** 446 | * Checks if current position is valid 447 | * 448 | * @return boolean 449 | */ 450 | public function valid(): bool 451 | { 452 | return $this->_position < sizeof(static::$storage); 453 | } 454 | 455 | /** 456 | * Count elements of an object 457 | * 458 | * @return integer 459 | */ 460 | public function count(): int 461 | { 462 | return count(static::$storage); 463 | } 464 | 465 | /** 466 | * String representation of object (PHP 8.1) 467 | * 468 | * @return array 469 | */ 470 | public function __serialize(): array 471 | { 472 | return (array)serialize(static::$storage); 473 | } 474 | 475 | /** 476 | * String representation of object 477 | * 478 | * @return string 479 | */ 480 | public function serialize(): string 481 | { 482 | return serialize(static::$storage); 483 | } 484 | 485 | /** 486 | * Constructs the object form string (PHP 8.1) 487 | * 488 | * @param string $data The string representation of the object. 489 | * @return void 490 | */ 491 | public function __unserialize($data): void 492 | { 493 | static::$storage = unserialize($data); 494 | } 495 | 496 | /** 497 | * Constructs the object form string 498 | * 499 | * @param string $data The string representation of the object. 500 | * @return void 501 | */ 502 | public function unserialize($data): void 503 | { 504 | static::$storage = unserialize($data); 505 | } 506 | 507 | /** 508 | * Clear elements of storage 509 | * 510 | * @return void 511 | */ 512 | public function clear(): void 513 | { 514 | static::$storage->exchangeArray(array()); 515 | } 516 | } 517 | -------------------------------------------------------------------------------- /src/Opengraph/Reader.php: -------------------------------------------------------------------------------- 1 | 5 | * @copyright Copyright (c) 2011 Axel Etcheverry (http://www.axel-etcheverry.com) 6 | * Displays MIT 7 | * @license http://creativecommons.org/licenses/MIT/deed.fr MIT 8 | */ 9 | 10 | /** 11 | * @namespace 12 | */ 13 | namespace Opengraph; 14 | 15 | use DOMDocument; 16 | use RuntimeException; 17 | use ArrayObject; 18 | 19 | class Reader extends Opengraph 20 | { 21 | /** 22 | * @var \ArrayObject 23 | */ 24 | protected static $storage; 25 | 26 | public function __construct() 27 | { 28 | static::$storage = new ArrayObject(); 29 | } 30 | 31 | /** 32 | * parse html tags 33 | * 34 | * @param $contents 35 | * @param bool $includeDefaults 36 | * @return $this 37 | */ 38 | public function parse($contents, $includeDefaults = false) 39 | { 40 | if (empty($contents)) { 41 | throw new RuntimeException('Contents is empty'); 42 | } 43 | 44 | $old_libxml_error = libxml_use_internal_errors(true); 45 | 46 | $dom = new DOMDocument; 47 | 48 | if(@$dom->loadHTML($contents) === false) { 49 | throw new RuntimeException("Contents is empty"); 50 | } 51 | 52 | libxml_use_internal_errors($old_libxml_error); 53 | 54 | foreach($dom->getElementsByTagName('meta') as $tag) { 55 | if($includeDefaults && $tag->hasAttribute('name') && $tag->hasAttribute('content') && $tag->getAttribute('name') == 'description') { 56 | $this->addMeta('non-og-description', $tag->getAttribute('content'), self::APPEND); 57 | } else if($tag->hasAttribute('property') && $tag->hasAttribute('content')) { 58 | $this->addMeta($tag->getAttribute('property'), $tag->getAttribute('content'), self::APPEND); 59 | } 60 | } 61 | 62 | if($includeDefaults) { 63 | $titles = $dom->getElementsByTagName('title'); 64 | if ($titles->length > 0) { 65 | $this->addMeta('non-og-title', $titles->item(0)->textContent, self::APPEND); 66 | } 67 | } 68 | 69 | unset($dom); 70 | 71 | return $this; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Opengraph/Writer.php: -------------------------------------------------------------------------------- 1 | 5 | * @copyright Copyright (c) 2011 Axel Etcheverry (http://www.axel-etcheverry.com) 6 | * Displays MIT 7 | * @license http://creativecommons.org/licenses/MIT/deed.fr MIT 8 | */ 9 | 10 | /** 11 | * @namespace 12 | */ 13 | namespace Opengraph; 14 | 15 | class Writer extends Opengraph 16 | { 17 | /** 18 | * @var \ArrayObject 19 | */ 20 | protected static $storage; 21 | 22 | /** 23 | * @var Integer 24 | */ 25 | //protected static $position; 26 | 27 | /** 28 | * Append meta 29 | * 30 | * @param String $property 31 | * @param String $content 32 | * @return \Opengraph\Opengraph 33 | */ 34 | public function append($property, $content) 35 | { 36 | return $this->addMeta($property, $content, self::APPEND); 37 | } 38 | 39 | /** 40 | * Prepend meta 41 | * 42 | * @param String $property 43 | * @param String $content 44 | * @return \Opengraph\Opengraph 45 | */ 46 | public function prepend($property, $content) 47 | { 48 | return $this->addMeta($property, $content, self::PREPEND); 49 | } 50 | 51 | /** 52 | * Render all meta tags 53 | * 54 | * @return String 55 | */ 56 | public function render($indent = "\t") 57 | { 58 | $html = ''; 59 | foreach(self::$storage as $meta) { 60 | $html .= $indent . $meta->render() . PHP_EOL; 61 | } 62 | 63 | return $html; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/Opengraph/MetaTest.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | use PHPUnit\Framework\TestCase; 12 | 13 | class MetaTest extends TestCase 14 | { 15 | public function testMeta() 16 | { 17 | $meta = new Opengraph\Meta(Opengraph\Opengraph::OG_TITLE, 'test'); 18 | 19 | $this->assertInstanceOf('\Opengraph\Meta', $meta); 20 | 21 | $this->assertEquals('og:title', $meta->getProperty()); 22 | 23 | $this->assertEquals('test', $meta->getContent()); 24 | 25 | $this->assertEquals('', $meta->render()); 26 | 27 | 28 | $meta->setProperty(Opengraph\Opengraph::OG_TYPE); 29 | 30 | $this->assertEquals('og:type', $meta->getProperty()); 31 | 32 | $meta->setContent(Opengraph\Opengraph::TYPE_BOOK); 33 | 34 | $this->assertEquals('book', $meta->getContent()); 35 | 36 | 37 | $this->assertEquals('', $meta->render()); 38 | 39 | $meta->setContent(array(123, 456)); 40 | 41 | $this->assertEquals('', $meta->render()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Opengraph/ReaderTest.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | use PHPUnit\Framework\TestCase; 12 | 13 | class Reader extends TestCase 14 | { 15 | public function testClass() 16 | { 17 | $reader = new Opengraph\Reader(); 18 | 19 | $this->assertInstanceOf('\Opengraph\Opengraph', $reader); 20 | $this->assertInstanceOf('\Iterator', $reader); 21 | $this->assertInstanceOf('\Serializable', $reader); 22 | $this->assertInstanceOf('\Countable', $reader); 23 | } 24 | 25 | public function testReaderParseException() 26 | { 27 | $this->expectException(\RuntimeException::class); 28 | $this->expectExceptionMessage('Contents is empty'); 29 | 30 | $reader = new Opengraph\Reader(); 31 | $reader->parse(''); 32 | } 33 | 34 | public function testReader() 35 | { 36 | $html = ' 37 | 38 | 39 | 40 | 41 | '; 42 | 43 | $reader = new Opengraph\Reader(); 44 | 45 | $this->assertInstanceOf('\Opengraph\Reader', $reader); 46 | 47 | $reader->parse($html); 48 | 49 | $this->assertEquals(6, $reader->count()); 50 | 51 | $this->assertEquals([ 52 | 'og:url' => 'http://www.imdb.com/title/tt1074638/', 53 | 'og:title' => 'Skyfall (2012)', 54 | 'og:type' => 'video.movie', 55 | 'og:image' => [ 56 | 0 => [ 57 | 'og:image:url' => 'http://ia.media-imdb.com/images/M/MV5BMTczMjQ5NjE4NV5BMl5BanBnXkFtZTcwMjk0NjAwNw@@._V1._SX95_SY140_.jpg', 58 | ], 59 | ], 60 | 'og:site_name' => 'IMDb', 61 | 'fb:app_id' => '115109575169727', 62 | ], $reader->getArrayCopy()); 63 | 64 | $this->assertInstanceOf('\ArrayObject', $reader->getMetas()); 65 | 66 | $this->assertInstanceOf('\Opengraph\Meta', $reader->current()); 67 | 68 | $this->assertEquals(0, $reader->key()); 69 | 70 | $reader->next(); 71 | 72 | $this->assertEquals(1, $reader->key()); 73 | 74 | $this->assertTrue($reader->valid()); 75 | 76 | $reader->next(); 77 | $reader->next(); 78 | $reader->next(); 79 | $reader->next(); 80 | $reader->next(); 81 | 82 | $this->assertFalse($reader->valid()); 83 | 84 | $reader->rewind(); 85 | 86 | $this->assertEquals(0, $reader->key()); 87 | 88 | $html = 'Skyfall Title 89 | 90 | 91 | 92 | 93 | 94 | 95 | '; 96 | 97 | $reader = new Opengraph\Reader(); 98 | 99 | $this->assertInstanceOf('\Opengraph\Reader', $reader); 100 | 101 | $reader->parse($html, true); 102 | 103 | $this->assertEquals(8, $reader->count()); 104 | 105 | $this->assertEquals([ 106 | 'og:url' => 'http://www.imdb.com/title/tt1074638/', 107 | 'og:title' => 'Skyfall (2012)', 108 | 'og:type' => 'video.movie', 109 | 'og:image' => [ 110 | 0 => [ 111 | 'og:image:url' => 'http://ia.media-imdb.com/images/M/MV5BMTczMjQ5NjE4NV5BMl5BanBnXkFtZTcwMjk0NjAwNw@@._V1._SX95_SY140_.jpg', 112 | ], 113 | ], 114 | 'og:site_name' => 'IMDb', 115 | 'non-og-description' => 'Skyfall meta description', 116 | 'og:description' => 'Skyfall og description', 117 | 'non-og-title' => 'Skyfall Title' 118 | ], $reader->getArrayCopy()); 119 | 120 | $reader = new Opengraph\Reader(); 121 | 122 | $reader->parse($html, false); 123 | 124 | $this->assertEquals(6, $reader->count()); 125 | 126 | $this->assertEquals([ 127 | 'og:url' => 'http://www.imdb.com/title/tt1074638/', 128 | 'og:title' => 'Skyfall (2012)', 129 | 'og:type' => 'video.movie', 130 | 'og:image' => [ 131 | 0 => [ 132 | 'og:image:url' => 'http://ia.media-imdb.com/images/M/MV5BMTczMjQ5NjE4NV5BMl5BanBnXkFtZTcwMjk0NjAwNw@@._V1._SX95_SY140_.jpg', 133 | ], 134 | ], 135 | 'og:site_name' => 'IMDb', 136 | 'og:description' => 'Skyfall og description' 137 | ], $reader->getArrayCopy()); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /tests/Opengraph/WriterTest.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | use PHPUnit\Framework\TestCase; 12 | 13 | class WriterTest extends TestCase 14 | { 15 | public function testClass() 16 | { 17 | $writer = new Opengraph\Writer(); 18 | 19 | $this->assertInstanceOf('\Opengraph\Opengraph', $writer); 20 | $this->assertInstanceOf('\Iterator', $writer); 21 | $this->assertInstanceOf('\Serializable', $writer); 22 | $this->assertInstanceOf('\Countable', $writer); 23 | } 24 | 25 | public function testWriter() 26 | { 27 | $writer = new Opengraph\Writer(); 28 | $writer->append(Opengraph\Writer::OG_TITLE, 'test'); 29 | 30 | $this->assertEquals("\t" . '' . PHP_EOL, $writer->render()); 31 | 32 | $this->assertInstanceOf('\Opengraph\Writer', $writer->addMeta( 33 | Opengraph\Writer::OG_TYPE, 34 | Opengraph\Writer::TYPE_WEBSITE, 35 | Opengraph\Writer::APPEND 36 | )); 37 | 38 | $this->assertInstanceOf('\Opengraph\Writer', $writer->append(Opengraph\Writer::OG_TYPE, Opengraph\Writer::TYPE_WEBSITE)); 39 | 40 | $this->assertInstanceOf('\Opengraph\Writer', $writer->prepend(Opengraph\Writer::OG_IMAGE, 'http://www.google.com/')); 41 | 42 | $this->assertInstanceOf('\ArrayObject', $writer->getMetas()); 43 | 44 | $this->assertEquals('d2d171c74585f44ee53ba4c351ba0416', md5($writer->serialize())); 45 | 46 | $this->assertEquals(3, $writer->count()); 47 | 48 | $this->assertInstanceOf('\Opengraph\Meta', $writer->current()); 49 | 50 | $this->assertEquals(0, $writer->key()); 51 | 52 | $writer->next(); 53 | 54 | $this->assertEquals(1, $writer->key()); 55 | 56 | $this->assertTrue($writer->valid()); 57 | 58 | $writer->next(); 59 | $writer->next(); 60 | $writer->next(); 61 | 62 | $this->assertFalse($writer->valid()); 63 | 64 | $writer->rewind(); 65 | 66 | $this->assertEquals(0, $writer->key()); 67 | 68 | $writer->unserialize('C:11:"ArrayObject":751:{x:i:0;a:6:{i:0;O:14:"Opengraph\Meta":2:{s:12:"*_property";s:6:"og:url";s:11:"*_content";s:36:"http://www.imdb.com/title/tt0117500/";}i:1;O:14:"Opengraph\Meta":2:{s:12:"*_property";s:8:"og:title";s:11:"*_content";s:11:"Rock (1996)";}i:2;O:14:"Opengraph\Meta":2:{s:12:"*_property";s:7:"og:type";s:11:"*_content";s:11:"video.movie";}i:3;O:14:"Opengraph\Meta":2:{s:12:"*_property";s:8:"og:image";s:11:"*_content";s:99:"http://ia.media-imdb.com/images/M/MV5BMTM3MTczOTM1OF5BMl5BanBnXkFtZTYwMjc1NDA5._V1._SX98_SY140_.jpg";}i:4;O:14:"Opengraph\Meta":2:{s:12:"*_property";s:12:"og:site_name";s:11:"*_content";s:4:"IMDb";}i:5;O:14:"Opengraph\Meta":2:{s:12:"*_property";s:9:"fb:app_id";s:11:"*_content";s:15:"115109575169727";}};m:a:0:{}}'); 69 | 70 | $this->assertEquals(6, $writer->count()); 71 | 72 | $this->assertEquals([ 73 | 'og:url' => 'http://www.imdb.com/title/tt0117500/', 74 | 'og:title' => 'Rock (1996)', 75 | 'og:type' => 'video.movie', 76 | 'og:image' => [ 77 | 0 => [ 78 | 'og:image:url' => 'http://ia.media-imdb.com/images/M/MV5BMTM3MTczOTM1OF5BMl5BanBnXkFtZTYwMjc1NDA5._V1._SX98_SY140_.jpg' 79 | ] 80 | ], 81 | 'og:site_name' => 'IMDb', 82 | 'fb:app_id' => '115109575169727' 83 | ], $writer->getArrayCopy()); 84 | 85 | $this->assertEquals('Rock (1996)', $writer->getMeta(Opengraph\Writer::OG_TITLE)); 86 | 87 | $this->assertTrue($writer->hasMeta(Opengraph\Writer::OG_TITLE)); 88 | 89 | $this->assertTrue($writer->removeMeta(Opengraph\Writer::OG_TITLE)); 90 | 91 | $this->assertEquals(5, $writer->count()); 92 | 93 | $this->assertFalse($writer->removeMeta(Opengraph\Writer::OG_TITLE)); 94 | 95 | $this->assertFalse($writer->getMeta(Opengraph\Writer::OG_TITLE)); 96 | 97 | $this->assertFalse($writer->hasMeta(Opengraph\Writer::OG_TITLE)); 98 | 99 | $writer->addMeta(Opengraph\Writer::OG_TYPE, Opengraph\Writer::TYPE_BOOK, Opengraph\Writer::APPEND); 100 | 101 | $this->assertEquals(Opengraph\Writer::TYPE_BOOK, $writer->getMeta(Opengraph\Writer::OG_TYPE)); 102 | 103 | $writer = new Opengraph\Writer(); 104 | $this->assertEquals(5, $writer->count()); 105 | 106 | $writer = new Opengraph\Writer(); 107 | $writer->clear(); 108 | 109 | $this->assertEquals(0, $writer->count()); 110 | 111 | $writer->append(Opengraph\Writer::FB_ADMINS, 12345567657868); 112 | 113 | $this->assertEquals("\t" . '' . PHP_EOL, $writer->render()); 114 | 115 | $writer->clear(); 116 | 117 | $writer->append(Opengraph\Writer::FB_ADMINS, '12345567657868,23334543656456'); 118 | $this->assertEquals("\t" . '' . PHP_EOL, $writer->render()); 119 | } 120 | } 121 | --------------------------------------------------------------------------------