From 4bbea502e9e5fdcd93bc59134d18ff9c99c64d34 Mon Sep 17 00:00:00 2001 From: Dominic Luidold Date: Thu, 28 Nov 2024 08:10:46 +0100 Subject: [PATCH 1/7] Update supported PHP, Symfony version constraints --- composer.json | 19 ++++++++++++------- phpunit.xml | 12 ------------ 2 files changed, 12 insertions(+), 19 deletions(-) delete mode 100644 phpunit.xml diff --git a/composer.json b/composer.json index b74deba..7776bbd 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,9 @@ { "name": "fusonic/opengraph", "description": "PHP library for consuming and publishing Open Graph resources.", - "keywords": ["opengraph"], + "keywords": [ + "opengraph" + ], "type": "library", "homepage": "https://github.com/fusonic/fusonic-opengraph", "license": "MIT", @@ -22,20 +24,23 @@ } }, "require": { - "php": "^7.4|^8.0", + "php": "^8.1", "ext-dom": "*", - "symfony/dom-crawler": "^3.0|^4.0|^5.0|^6.0", - "symfony/css-selector": "^3.0|^4.0|^5.0|^6.0", + "symfony/css-selector": "^5.4 || ^6.4 || ^7.1", + "symfony/dom-crawler": "^5.4 || ^6.4 || ^7.1", "psr/http-client": "^1.0", "psr/http-factory": "^1.0" }, "require-dev": { + "nyholm/psr7": "^1.2", "phpunit/phpunit": "^9.0", - "symfony/http-client": "^6.0", - "nyholm/psr7": "^1.2" + "symfony/http-client": "^5.4 || ^6.4 || ^7.1" }, "suggest": { - "symfony/http-client": "^5.0", + "symfony/http-client": "^5.4 || ^6.4 || ^7.1", "nyholm/psr7": "^1.2" + }, + "config": { + "sort-packages": true } } diff --git a/phpunit.xml b/phpunit.xml deleted file mode 100644 index 0d9aeef..0000000 --- a/phpunit.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - tests - - - - - src - - - From 1a3a5a50ab96a7d542f5add585f160b0b372cb32 Mon Sep 17 00:00:00 2001 From: Dominic Luidold Date: Thu, 28 Nov 2024 08:34:21 +0100 Subject: [PATCH 2/7] Upgrade to PHPUnit 11.4 --- .gitignore | 5 + composer.json | 5 +- phpunit.xml.dist | 43 +++++ tests/ConsumerTest.php | 224 ++++++++++++++++----------- tests/PublisherTest.php | 73 +++++---- tests/TestData/TestPublishObject.php | 11 +- 6 files changed, 231 insertions(+), 130 deletions(-) create mode 100644 phpunit.xml.dist diff --git a/.gitignore b/.gitignore index 2942141..d079bff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ .idea/ composer.lock vendor/ + +###> phpunit/phpunit ### +/phpunit.xml +/.phpunit.cache +###< phpunit/phpunit ### diff --git a/composer.json b/composer.json index 7776bbd..07812e2 100644 --- a/composer.json +++ b/composer.json @@ -33,13 +33,16 @@ }, "require-dev": { "nyholm/psr7": "^1.2", - "phpunit/phpunit": "^9.0", + "phpunit/phpunit": "^11.4", "symfony/http-client": "^5.4 || ^6.4 || ^7.1" }, "suggest": { "symfony/http-client": "^5.4 || ^6.4 || ^7.1", "nyholm/psr7": "^1.2" }, + "scripts": { + "test": "XDEBUG_MODE=off vendor/bin/phpunit --testdox" + }, "config": { "sort-packages": true } diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..56ab11f --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,43 @@ + + + + + + + + + + + ./tests/ + + + + + + src + + + diff --git a/tests/ConsumerTest.php b/tests/ConsumerTest.php index 4fa285c..7a133ab 100644 --- a/tests/ConsumerTest.php +++ b/tests/ConsumerTest.php @@ -5,13 +5,14 @@ use Fusonic\OpenGraph\Consumer; use PHPUnit\Framework\TestCase; -class ConsumerTest extends TestCase +final class ConsumerTest extends TestCase { /** * Checks crawler to read basic properties. */ - public function testLoadHtmlBasics() + public function testLoadHtmlBasics(): void { + // arrange $content = << @@ -34,27 +35,30 @@ public function testLoadHtmlBasics() $consumer = new Consumer(); - $res = $consumer->loadHtml($content, "about:blank"); - - $this->assertEquals("Description", $res->description); - $this->assertEquals("auto", $res->determiner); - $this->assertEquals("en_GB", $res->locale); - $this->assertContains("en_US", $res->localeAlternate); - $this->assertContains("de_AT", $res->localeAlternate); - $this->assertTrue($res->richAttachment); - $this->assertContains("https://github.com/fusonic/fusonic-linq", $res->seeAlso); - $this->assertContains("https://github.com/fusonic/fusonic-spreadsheetexport", $res->seeAlso); - $this->assertEquals("Site name", $res->siteName); - $this->assertEquals("Title", $res->title); - $this->assertTrue($res->updatedTime instanceof \DateTimeInterface); - $this->assertEquals("https://github.com/fusonic/fusonic-opengraph", $res->url); + // act + $result = $consumer->loadHtml($content, "about:blank"); + + // assert + self::assertSame('Description', $result->description); + self::assertSame('auto', $result->determiner); + self::assertSame('en_GB', $result->locale); + self::assertContains('en_US', $result->localeAlternate); + self::assertContains('de_AT', $result->localeAlternate); + self::assertTrue($result->richAttachment); + self::assertContains('https://github.com/fusonic/fusonic-linq', $result->seeAlso); + self::assertContains('https://github.com/fusonic/fusonic-spreadsheetexport', $result->seeAlso); + self::assertSame('Site name', $result->siteName); + self::assertSame('Title', $result->title); + self::assertInstanceOf(\DateTimeInterface::class, $result->updatedTime); + self::assertSame('https://github.com/fusonic/fusonic-opengraph', $result->url); } /** * Checks crawler not to use fallback if disabled even if no OG data is provided. */ - public function testLoadHtmlFallbacksOff() + public function testLoadHtmlFallbacksOff(): void { + // arrange $content = << @@ -67,18 +71,21 @@ public function testLoadHtmlFallbacksOff() $consumer = new Consumer(); - $res = $consumer->loadHtml($content, "about:blank"); + // act + $result = $consumer->loadHtml($content, "about:blank"); - $this->assertNull($res->description); - $this->assertNull($res->title); - $this->assertNull($res->url); + // assert + self::assertNull($result->description); + self::assertNull($result->title); + self::assertNull($result->url); } /** * Checks crawler to correctly use fallback elements when activated. */ - public function testLoadHtmlFallbacksOn() + public function testLoadHtmlFallbacksOn(): void { + // arrange $content = << @@ -92,18 +99,21 @@ public function testLoadHtmlFallbacksOn() $consumer = new Consumer(); $consumer->useFallbackMode = true; - $res = $consumer->loadHtml($content, "about:blank"); + // act + $result = $consumer->loadHtml($content, "about:blank"); - $this->assertEquals("Description", $res->description); - $this->assertEquals("Title", $res->title); - $this->assertEquals("about:blank", $res->url); + // assert + self::assertSame("Description", $result->description); + self::assertSame("Title", $result->title); + self::assertSame("about:blank", $result->url); } /** * Checks crawler to correctly use fallback elements when activated. */ - public function testLoadHtmlCanonicalLinkFallbacksOn() + public function testLoadHtmlCanonicalLinkFallbacksOn(): void { + // arrange $content = << @@ -118,19 +128,22 @@ public function testLoadHtmlCanonicalLinkFallbacksOn() $consumer = new Consumer(); $consumer->useFallbackMode = true; - $res = $consumer->loadHtml($content, "about:blank"); + // act + $result = $consumer->loadHtml($content, "about:blank"); - $this->assertEquals("Description", $res->description); - $this->assertEquals("Title", $res->title); - $this->assertEquals("https://github.com/fusonic/opengraph", $res->url); + // assert + self::assertSame("Description", $result->description); + self::assertSame("Title", $result->title); + self::assertSame("https://github.com/fusonic/opengraph", $result->url); } /** * Checks crawler to handle arrays of elements with child-properties like described in the * Open Graph documentation (http://ogp.me/#array). */ - public function testLoadHtmlArrayHandling() + public function testLoadHtmlArrayHandling(): void { + // arrange $content = << @@ -147,22 +160,25 @@ public function testLoadHtmlArrayHandling() $consumer = new Consumer(); - $res = $consumer->loadHtml($content); - - $this->assertEquals(3, count($res->images)); - $this->assertEquals("http://example.com/rock.jpg", $res->images[0]->url); - $this->assertEquals(300, $res->images[0]->width); - $this->assertEquals(300, $res->images[0]->height); - $this->assertEquals("http://example.com/rock2.jpg", $res->images[1]->url); - $this->assertNull($res->images[1]->width); - $this->assertNull($res->images[1]->height); - $this->assertEquals("http://example.com/rock3.jpg", $res->images[2]->url); - $this->assertNull($res->images[2]->width); - $this->assertEquals(1000, $res->images[2]->height); + // act + $result = $consumer->loadHtml($content); + + // assert + self::assertSame(3, count($result->images)); + self::assertSame("http://example.com/rock.jpg", $result->images[0]->url); + self::assertSame(300, $result->images[0]->width); + self::assertSame(300, $result->images[0]->height); + self::assertSame("http://example.com/rock2.jpg", $result->images[1]->url); + self::assertNull($result->images[1]->width); + self::assertNull($result->images[1]->height); + self::assertSame("http://example.com/rock3.jpg", $result->images[2]->url); + self::assertNull($result->images[2]->width); + self::assertSame(1000, $result->images[2]->height); } - public function testLoadHtmlImages() + public function testLoadHtmlImages(): void { + // arrange $content = << @@ -178,18 +194,21 @@ public function testLoadHtmlImages() $consumer = new Consumer(); - $res = $consumer->loadHtml($content); + // act + $result = $consumer->loadHtml($content); - $this->assertEquals(1, count($res->images)); - $this->assertEquals("http://example.com/rock.jpg", $res->images[0]->url); - $this->assertEquals("https://example.com/rock.jpg", $res->images[0]->secureUrl); - $this->assertEquals(300, $res->images[0]->width); - $this->assertEquals(300, $res->images[0]->height); - $this->assertEquals("image/jpg", $res->images[0]->type); + // assert + self::assertSame(1, count($result->images)); + self::assertSame("http://example.com/rock.jpg", $result->images[0]->url); + self::assertSame("https://example.com/rock.jpg", $result->images[0]->secureUrl); + self::assertSame(300, $result->images[0]->width); + self::assertSame(300, $result->images[0]->height); + self::assertSame("image/jpg", $result->images[0]->type); } - public function testLoadHtmlVideos() + public function testLoadHtmlVideos(): void { + // arrange $content = << @@ -205,18 +224,21 @@ public function testLoadHtmlVideos() $consumer = new Consumer(); - $res = $consumer->loadHtml($content); + // act + $result = $consumer->loadHtml($content); - $this->assertEquals(1, count($res->videos)); - $this->assertEquals("http://example.com/rock.ogv", $res->videos[0]->url); - $this->assertEquals("https://example.com/rock.ogv", $res->videos[0]->secureUrl); - $this->assertEquals(300, $res->videos[0]->width); - $this->assertEquals(300, $res->videos[0]->height); - $this->assertEquals("video/ogv", $res->videos[0]->type); + // assert + self::assertSame(1, count($result->videos)); + self::assertSame("http://example.com/rock.ogv", $result->videos[0]->url); + self::assertSame("https://example.com/rock.ogv", $result->videos[0]->secureUrl); + self::assertSame(300, $result->videos[0]->width); + self::assertSame(300, $result->videos[0]->height); + self::assertSame("video/ogv", $result->videos[0]->type); } - public function testLoadHtmlAudios() + public function testLoadHtmlAudios(): void { + // arrange $content = << @@ -230,16 +252,19 @@ public function testLoadHtmlAudios() $consumer = new Consumer(); - $res = $consumer->loadHtml($content); + // act + $result = $consumer->loadHtml($content); - $this->assertEquals(1, count($res->audios)); - $this->assertEquals("http://example.com/rock.mp3", $res->audios[0]->url); - $this->assertEquals("https://example.com/rock.mp3", $res->audios[0]->secureUrl); - $this->assertEquals("audio/mp3", $res->audios[0]->type); + // assert + self::assertSame(1, count($result->audios)); + self::assertSame("http://example.com/rock.mp3", $result->audios[0]->url); + self::assertSame("https://example.com/rock.mp3", $result->audios[0]->secureUrl); + self::assertSame("audio/mp3", $result->audios[0]->type); } - public function testCrawlHtmlImageExceptionDebugOff() + public function testCrawlHtmlImageExceptionDebugOff(): void { + // arrange $content = << @@ -251,15 +276,19 @@ public function testCrawlHtmlImageExceptionDebugOff() $consumer = new Consumer(); - $res = $consumer->loadHtml($content); + // act + $result = $consumer->loadHtml($content); - $this->assertEquals(0, count($res->images)); + // assert + self::assertSame(0, count($result->images)); } - public function testCrawlHtmlImageExceptionDebugOn() + public function testCrawlHtmlImageExceptionDebugOn(): void { + // assert $this->expectException(\UnexpectedValueException::class); + // arrange $content = << @@ -272,11 +301,13 @@ public function testCrawlHtmlImageExceptionDebugOn() $consumer = new Consumer(); $consumer->debug = true; - $res = $consumer->loadHtml($content); + // act + $consumer->loadHtml($content); } - public function testCrawlHtmlVideoExceptionDebugOff() + public function testCrawlHtmlVideoExceptionDebugOff(): void { + // arrange $content = << @@ -288,15 +319,19 @@ public function testCrawlHtmlVideoExceptionDebugOff() $consumer = new Consumer(); - $res = $consumer->loadHtml($content); + // act + $result = $consumer->loadHtml($content); - $this->assertEquals(0, count($res->videos)); + // assert + self::assertSame(0, count($result->videos)); } - public function testCrawlHtmlVideoExceptionDebugOn() + public function testCrawlHtmlVideoExceptionDebugOn(): void { + // assert $this->expectException(\UnexpectedValueException::class); + // arrange $content = << @@ -309,11 +344,13 @@ public function testCrawlHtmlVideoExceptionDebugOn() $consumer = new Consumer(); $consumer->debug = true; - $res = $consumer->loadHtml($content); + // act + $consumer->loadHtml($content); } - public function testCrawlHtmlAudioExceptionDebugOff() + public function testCrawlHtmlAudioExceptionDebugOff(): void { + // arrange $content = << @@ -325,15 +362,19 @@ public function testCrawlHtmlAudioExceptionDebugOff() $consumer = new Consumer(); - $res = $consumer->loadHtml($content); + // act + $result = $consumer->loadHtml($content); - $this->assertEquals(0, count($res->audios)); + // assert + self::assertSame(0, count($result->audios)); } - public function testCrawlHtmlAudioExceptionDebugOn() + public function testCrawlHtmlAudioExceptionDebugOn(): void { + // assert $this->expectException(\UnexpectedValueException::class); + // arrange $content = << @@ -346,11 +387,13 @@ public function testCrawlHtmlAudioExceptionDebugOn() $consumer = new Consumer(); $consumer->debug = true; - $res = $consumer->loadHtml($content); + // act + $consumer->loadHtml($content); } - public function testLoadHtmlSpecialCharacters() + public function testLoadHtmlSpecialCharacters(): void { + // arrange $content = << @@ -362,13 +405,16 @@ public function testLoadHtmlSpecialCharacters() $consumer = new Consumer(); - $res = $consumer->loadHtml($content); + // act + $result = $consumer->loadHtml($content); - $this->assertEquals("Apples & Bananas - just \"Fruits\"", $res->title); + // assert + self::assertSame("Apples & Bananas - just \"Fruits\"", $result->title); } - - public function testReadMetaName() + + public function testReadMetaName(): void { + // arrange $content = << @@ -380,8 +426,10 @@ public function testReadMetaName() $consumer = new Consumer(); - $res = $consumer->loadHtml($content); + // act + $result = $consumer->loadHtml($content); - $this->assertEquals("A 'name' attribute instead of 'property'", $res->title); + // assert + self::assertSame("A 'name' attribute instead of 'property'", $result->title); } } diff --git a/tests/PublisherTest.php b/tests/PublisherTest.php index 6cb0559..ebd2f79 100644 --- a/tests/PublisherTest.php +++ b/tests/PublisherTest.php @@ -5,34 +5,60 @@ use DateTime; use Fusonic\OpenGraph\Publisher; use Fusonic\OpenGraph\Test\TestData\TestPublishObject; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use stdClass; use UnexpectedValueException; -class PublisherTest extends TestCase +final class PublisherTest extends TestCase { - /** - * @var Publisher - */ - private $publisher; + public function testGenerateHtmlNull(): void + { + // arrange + $publisher = new Publisher(); + + $object = new TestPublishObject(null); + + // act + $result = $publisher->generateHtml($object); - protected function setUp(): void + // assert + self::assertSame('', $result); + } + + #[DataProvider('generateHtmlValuesProvider')] + public function testGenerateHtmlValues($value, $expectedContent) { - $this->publisher = new Publisher(); + // arrange + $publisher = new Publisher(); - parent::setUp(); + $object = new TestPublishObject($value); + + // act + $result = $publisher->generateHtml($object); + + // assert + self::assertSame( + expected: \sprintf('', TestPublishObject::KEY, $expectedContent), + actual: $result + ); } - public function testGenerateHtmlNull() + public function testGenerateHtmlUnsupportedObject(): void { - $object = new TestPublishObject(null); + // assert + $this->expectException(UnexpectedValueException::class); + + // arrange + $publisher = new Publisher(); - $result = $this->publisher->generateHtml($object); + $object = new TestPublishObject(new stdClass()); - $this->assertEquals("", $result); + // act + $publisher->generateHtml($object); } - public function generateHtmlValuesProvider() + public static function generateHtmlValuesProvider(): array { return [ "Boolean true" => [ true, "1" ], @@ -47,25 +73,4 @@ public function generateHtmlValuesProvider() "String with ampersands" => [ "some & ampersand", "some & ampersand" ], ]; } - - /** - * @dataProvider generateHtmlValuesProvider - */ - public function testGenerateHtmlValues($value, $expectedContent) - { - $object = new TestPublishObject($value); - - $result = $this->publisher->generateHtml($object); - - $this->assertEquals('', $result); - } - - public function testGenerateHtmlUnsupportedObject() - { - $this->expectException(UnexpectedValueException::class); - - $object = new TestPublishObject(new stdClass()); - - $this->publisher->generateHtml($object); - } } diff --git a/tests/TestData/TestPublishObject.php b/tests/TestData/TestPublishObject.php index dd1dcf0..665bb2c 100644 --- a/tests/TestData/TestPublishObject.php +++ b/tests/TestData/TestPublishObject.php @@ -5,17 +5,14 @@ use Fusonic\OpenGraph\Objects\ObjectBase; use Fusonic\OpenGraph\Property; -class TestPublishObject extends ObjectBase +final class TestPublishObject extends ObjectBase { const KEY = "og:title"; - private $value; - - public function __construct($value) - { + public function __construct( + private readonly mixed $value + ) { parent::__construct(); - - $this->value = $value; } public function getProperties(): array From dc9daa70fb6dd386317f24cfee5bb0819af95e35 Mon Sep 17 00:00:00 2001 From: Dominic Luidold Date: Thu, 28 Nov 2024 08:56:15 +0100 Subject: [PATCH 3/7] Install, apply PHP-CS-Fixer and type properties --- .gitignore | 4 + .php-cs-fixer.php | 35 +++ composer.json | 3 + examples/consume_website.php | 15 +- examples/publish_website.php | 32 ++- src/Consumer.php | 49 ++-- src/Elements/Audio.php | 35 ++- src/Elements/ElementBase.php | 19 +- src/Elements/Image.php | 53 ++-- src/Elements/Video.php | 47 ++-- src/Objects/ObjectBase.php | 117 +++++---- src/Objects/Website.php | 11 +- src/Property.php | 86 +++---- src/Publisher.php | 49 ++-- tests/ConsumerTest.php | 351 ++++++++++++++------------- tests/PublisherTest.php | 34 +-- tests/TestData/TestPublishObject.php | 12 +- 17 files changed, 513 insertions(+), 439 deletions(-) create mode 100644 .php-cs-fixer.php diff --git a/.gitignore b/.gitignore index d079bff..77387d8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,10 @@ composer.lock vendor/ +###> friendsofphp/php-cs-fixer ### +/.php-cs-fixer.cache +###< friendsofphp/php-cs-fixer ### + ###> phpunit/phpunit ### /phpunit.xml /.phpunit.cache diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 0000000..70b81c3 --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,35 @@ +in(__DIR__); + +return (new PhpCsFixer\Config()) + ->setParallelConfig(ParallelConfigFactory::detect()) + ->setRules([ + '@PHP81Migration' => true, + '@Symfony' => true, + '@Symfony:risky' => true, + 'declare_strict_types' => true, + 'header_comment' => [ + 'header' => 'Copyright (c) Fusonic GmbH. All rights reserved.'.\PHP_EOL.'Licensed under the MIT License. '. + 'See LICENSE file in the project root for license information.', + 'location' => 'after_open', + ], + 'no_useless_else' => true, + 'no_useless_return' => true, + 'php_unit_strict' => true, + 'single_line_throw' => false, + 'strict_comparison' => true, + 'strict_param' => true, + ]) + ->setFinder($finder) + ->setRiskyAllowed(true); diff --git a/composer.json b/composer.json index 07812e2..cae291f 100644 --- a/composer.json +++ b/composer.json @@ -32,6 +32,7 @@ "psr/http-factory": "^1.0" }, "require-dev": { + "friendsofphp/php-cs-fixer": "^3.65", "nyholm/psr7": "^1.2", "phpunit/phpunit": "^11.4", "symfony/http-client": "^5.4 || ^6.4 || ^7.1" @@ -41,6 +42,8 @@ "nyholm/psr7": "^1.2" }, "scripts": { + "phpcs:check": "XDEBUG_MODE=off vendor/bin/php-cs-fixer check -v --diff", + "phpcs:fix": "XDEBUG_MODE=off vendor/bin/php-cs-fixer fix -v", "test": "XDEBUG_MODE=off vendor/bin/phpunit --testdox" }, "config": { diff --git a/examples/consume_website.php b/examples/consume_website.php index 275cbfb..d46ce27 100644 --- a/examples/consume_website.php +++ b/examples/consume_website.php @@ -1,18 +1,25 @@ [ "User-Agent" => "facebookexternalhit/1.1" ] ])); +// package, but you can use any implementation provided by your framework of choice. +$client = new Psr18Client(new NativeHttpClient(['headers' => ['User-Agent' => 'facebookexternalhit/1.1']])); // Create a new crawler $crawler = new Fusonic\OpenGraph\Consumer($client, $client); // Crawl the desired URL and retrieve a Fusonic\OpenGraph\Object in response -$object = $crawler->loadUrl("https://github.com"); +$object = $crawler->loadUrl('https://github.com'); var_dump($object); diff --git a/examples/publish_website.php b/examples/publish_website.php index b419765..ceb68a8 100644 --- a/examples/publish_website.php +++ b/examples/publish_website.php @@ -1,43 +1,51 @@ url = "http://www.fusonic.net"; -$website->title = "Fusonic - Intranet & Mobile Applications from Austria"; -$website->description = "Creators of the awesome fusonic-opengraph library."; -$website->siteName = "Fusonic"; -$website->locale = "en_GB"; +$website->url = 'http://www.fusonic.net'; +$website->title = 'Fusonic - Intranet & Mobile Applications from Austria'; +$website->description = 'Creators of the awesome fusonic-opengraph library.'; +$website->siteName = 'Fusonic'; +$website->locale = 'en_GB'; // Attach an image -$image = new Image("http://www.fusonic.net/en/assets/images/logo.png"); +$image = new Image('http://www.fusonic.net/en/assets/images/logo.png'); $image->width = 140; $image->height = 41; -$image->type = "image/png"; +$image->type = 'image/png'; $website->images[] = $image; // Attach a video -$video = new Video("http://www.fusonic.net/en/we-dont-have-no-video.mp4"); +$video = new Video('http://www.fusonic.net/en/we-dont-have-no-video.mp4'); $video->width = 1920; $video->height = 1080; -$video->type = "video/mp4"; +$video->type = 'video/mp4'; $website->videos[] = $video; // Attach an audio -$audio = new Audio("http://www.fusonic.net/en/we-dont-have-no-audio.mp3"); -$audio->type = "audio/mp3"; +$audio = new Audio('http://www.fusonic.net/en/we-dont-have-no-audio.mp3'); +$audio->type = 'audio/mp3'; $website->audios[] = $audio; // Create Publisher object and echo HTML code $publisher = new Publisher(); $publisher->doctype = Publisher::DOCTYPE_HTML5; + echo $publisher->generateHtml($website); diff --git a/src/Consumer.php b/src/Consumer.php index 74aec6a..155ba34 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -1,10 +1,16 @@ client === null) { - throw new LogicException( - "To use loadUrl() you must provide \$client and \$requestFactory when instantiating the consumer." + if (null === $this->client) { + throw new \LogicException( + 'To use loadUrl() you must provide $client and $requestFactory when instantiating the consumer.' ); } - $request = $this->requestFactory->createRequest("GET", $url); + $request = $this->requestFactory->createRequest('GET', $url); $response = $this->client->sendRequest($request); return $this->loadHtml($response->getBody()->getContents(), $url); @@ -66,18 +70,16 @@ public function loadUrl(string $url): ObjectBase /** * Crawls the given HTML string for OpenGraph data. * - * @param string $html HTML string, usually whole content of crawled web resource. - * @param string $fallbackUrl URL to use when fallback mode is enabled. - * - * @return ObjectBase + * @param string $html HTML string, usually whole content of crawled web resource + * @param string|null $fallbackUrl URL to use when fallback mode is enabled */ - public function loadHtml(string $html, string $fallbackUrl = null): ObjectBase + public function loadHtml(string $html, ?string $fallbackUrl = null): ObjectBase { // Extract all data that can be found $page = $this->extractOpenGraphData($html); // Use the user's URL as fallback - if ($this->useFallbackMode && $page->url === null) { + if ($this->useFallbackMode && null === $page->url) { $page->url = $fallbackUrl; } @@ -87,12 +89,11 @@ public function loadHtml(string $html, string $fallbackUrl = null): ObjectBase private function extractOpenGraphData(string $content): ObjectBase { - $crawler = new Crawler; + $crawler = new Crawler(); $crawler->addHTMLContent($content, 'UTF-8'); $properties = []; - foreach(['name', 'property'] as $t) - { + foreach (['name', 'property'] as $t) { // Get all meta-tags starting with "og:" $ogMetaTags = $crawler->filter("meta[{$t}^='og:']"); @@ -100,13 +101,13 @@ private function extractOpenGraphData(string $content): ObjectBase $props = []; foreach ($ogMetaTags as $tag) { $name = strtolower(trim($tag->getAttribute($t))); - $value = trim($tag->getAttribute("content")); + $value = trim($tag->getAttribute('content')); $props[] = new Property($name, $value); } $properties = array_merge($properties, $props); } - + // Create new object $object = new Website(); @@ -117,13 +118,13 @@ private function extractOpenGraphData(string $content): ObjectBase if ($this->useFallbackMode && !$object->url) { $urlElement = $crawler->filter("link[rel='canonical']")->first(); if ($urlElement->count() > 0) { - $object->url = trim($urlElement->attr("href")); + $object->url = trim($urlElement->attr('href')); } } // Fallback for title if ($this->useFallbackMode && !$object->title) { - $titleElement = $crawler->filter("title")->first(); + $titleElement = $crawler->filter('title')->first(); if ($titleElement->count() > 0) { $object->title = trim($titleElement->text()); } @@ -133,7 +134,7 @@ private function extractOpenGraphData(string $content): ObjectBase if ($this->useFallbackMode && !$object->description) { $descriptionElement = $crawler->filter("meta[property='description']")->first(); if ($descriptionElement->count() > 0) { - $object->description = trim($descriptionElement->attr("content")); + $object->description = trim($descriptionElement->attr('content')); } } diff --git a/src/Elements/Audio.php b/src/Elements/Audio.php index 20df532..394348c 100644 --- a/src/Elements/Audio.php +++ b/src/Elements/Audio.php @@ -1,5 +1,12 @@ url = $url; } /** * Gets all properties set on this element. * - * @return array|Property[] + * @return Property[] */ - public function getProperties() + public function getProperties(): array { $properties = []; // URL must precede all other properties - if ($this->url !== null) { + if (null !== $this->url) { $properties[] = new Property(Property::AUDIO_URL, $this->url); } - if ($this->secureUrl !== null) { + if (null !== $this->secureUrl) { $properties[] = new Property(Property::AUDIO_SECURE_URL, $this->secureUrl); } - if ($this->type !== null) { + if (null !== $this->type) { $properties[] = new Property(Property::AUDIO_TYPE, $this->type); } diff --git a/src/Elements/ElementBase.php b/src/Elements/ElementBase.php index dd4139d..5660064 100644 --- a/src/Elements/ElementBase.php +++ b/src/Elements/ElementBase.php @@ -1,20 +1,25 @@ url = $url; } /** * Gets all properties set on this element. * - * @return array|Property[] + * @return Property[] */ - public function getProperties() + public function getProperties(): array { $properties = []; // URL must precede all other properties - if ($this->url !== null) { + if (null !== $this->url) { $properties[] = new Property(Property::IMAGE_URL, $this->url); } - if ($this->height !== null) { + if (null !== $this->height) { $properties[] = new Property(Property::IMAGE_HEIGHT, $this->height); } - if ($this->secureUrl !== null) { + if (null !== $this->secureUrl) { $properties[] = new Property(Property::IMAGE_SECURE_URL, $this->secureUrl); } - if ($this->type !== null) { + if (null !== $this->type) { $properties[] = new Property(Property::IMAGE_TYPE, $this->type); } - if ($this->width !== null) { + if (null !== $this->width) { $properties[] = new Property(Property::IMAGE_WIDTH, $this->width); } - if ($this->userGenerated !== null) { + if (null !== $this->userGenerated) { $properties[] = new Property(Property::IMAGE_USER_GENERATED, $this->userGenerated); } diff --git a/src/Elements/Video.php b/src/Elements/Video.php index ff5a330..c5062fe 100644 --- a/src/Elements/Video.php +++ b/src/Elements/Video.php @@ -1,5 +1,12 @@ url = $url; } /** * Gets all properties set on this element. * - * @return array|Property[] + * @return Property[] */ - public function getProperties() + public function getProperties(): array { $properties = []; // URL must precede all other properties - if ($this->url !== null) { + if (null !== $this->url) { $properties[] = new Property(Property::VIDEO_URL, $this->url); } - if ($this->height !== null) { + if (null !== $this->height) { $properties[] = new Property(Property::VIDEO_HEIGHT, $this->height); } - if ($this->secureUrl !== null) { + if (null !== $this->secureUrl) { $properties[] = new Property(Property::VIDEO_SECURE_URL, $this->secureUrl); } - if ($this->type !== null) { + if (null !== $this->type) { $properties[] = new Property(Property::VIDEO_TYPE, $this->type); } - if ($this->width !== null) { + if (null !== $this->width) { $properties[] = new Property(Property::VIDEO_WIDTH, $this->width); } diff --git a/src/Objects/ObjectBase.php b/src/Objects/ObjectBase.php index 33b9976..40e3bf9 100644 --- a/src/Objects/ObjectBase.php +++ b/src/Objects/ObjectBase.php @@ -1,16 +1,21 @@ key; $value = $property->value; - switch($name) { + switch ($name) { case Property::AUDIO: case Property::AUDIO_URL: $this->audios[] = new Audio($value); break; case Property::AUDIO_SECURE_URL: case Property::AUDIO_TYPE: - if (count($this->audios) > 0) { - $this->handleAudioAttribute($this->audios[count($this->audios) - 1], $name, $value); + if (\count($this->audios) > 0) { + $this->handleAudioAttribute($this->audios[\count($this->audios) - 1], $name, $value); } elseif ($debug) { - throw new UnexpectedValueException( - sprintf( + throw new \UnexpectedValueException( + \sprintf( "Found '%s' property but no audio was found before.", $name ) @@ -130,12 +131,12 @@ public function assignProperties(array $properties, $debug = false): void } break; case Property::DESCRIPTION: - if ($this->description === null) { + if (null === $this->description) { $this->description = $value; } break; case Property::DETERMINER: - if ($this->determiner === null) { + if (null === $this->determiner) { $this->determiner = $value; } break; @@ -148,11 +149,11 @@ public function assignProperties(array $properties, $debug = false): void case Property::IMAGE_TYPE: case Property::IMAGE_WIDTH: case Property::IMAGE_USER_GENERATED: - if (count($this->images) > 0) { - $this->handleImageAttribute($this->images[count($this->images) - 1], $name, $value); + if (\count($this->images) > 0) { + $this->handleImageAttribute($this->images[\count($this->images) - 1], $name, $value); } elseif ($debug) { - throw new UnexpectedValueException( - sprintf( + throw new \UnexpectedValueException( + \sprintf( "Found '%s' property but no image was found before.", $name ) @@ -160,7 +161,7 @@ public function assignProperties(array $properties, $debug = false): void } break; case Property::LOCALE: - if ($this->locale === null) { + if (null === $this->locale) { $this->locale = $value; } break; @@ -174,22 +175,22 @@ public function assignProperties(array $properties, $debug = false): void $this->seeAlso[] = $value; break; case Property::SITE_NAME: - if ($this->siteName === null) { + if (null === $this->siteName) { $this->siteName = $value; } break; case Property::TITLE: - if ($this->title === null) { + if (null === $this->title) { $this->title = $value; } break; case Property::UPDATED_TIME: - if ($this->updatedTime === null) { + if (null === $this->updatedTime) { $this->updatedTime = $this->convertToDateTime($value); } break; case Property::URL: - if ($this->url === null) { + if (null === $this->url) { $this->url = $value; } break; @@ -201,10 +202,10 @@ public function assignProperties(array $properties, $debug = false): void case Property::VIDEO_SECURE_URL: case Property::VIDEO_TYPE: case Property::VIDEO_WIDTH: - if (count($this->videos) > 0) { - $this->handleVideoAttribute($this->videos[count($this->videos) - 1], $name, $value); + if (\count($this->videos) > 0) { + $this->handleVideoAttribute($this->videos[\count($this->videos) - 1], $name, $value); } elseif ($debug) { - throw new UnexpectedValueException(sprintf( + throw new \UnexpectedValueException(\sprintf( "Found '%s' property but no video was found before.", $name )); @@ -215,13 +216,12 @@ public function assignProperties(array $properties, $debug = false): void private function handleImageAttribute(Image $element, string $name, string $value): void { - switch($name) - { + switch ($name) { case Property::IMAGE_HEIGHT: - $element->height = (int)$value; + $element->height = (int) $value; break; case Property::IMAGE_WIDTH: - $element->width = (int)$value; + $element->width = (int) $value; break; case Property::IMAGE_TYPE: $element->type = $value; @@ -237,13 +237,12 @@ private function handleImageAttribute(Image $element, string $name, string $valu private function handleVideoAttribute(Video $element, string $name, string $value): void { - switch($name) - { + switch ($name) { case Property::VIDEO_HEIGHT: - $element->height = (int)$value; + $element->height = (int) $value; break; case Property::VIDEO_WIDTH: - $element->width = (int)$value; + $element->width = (int) $value; break; case Property::VIDEO_TYPE: $element->type = $value; @@ -256,8 +255,7 @@ private function handleVideoAttribute(Video $element, string $name, string $valu private function handleAudioAttribute(Audio $element, string $name, string $value): void { - switch($name) - { + switch ($name) { case Property::AUDIO_TYPE: $element->type = $value; break; @@ -267,10 +265,10 @@ private function handleAudioAttribute(Audio $element, string $name, string $valu } } - protected function convertToDateTime(string $value): ?DateTimeImmutable + protected function convertToDateTime(string $value): ?\DateTimeImmutable { try { - return new DateTimeImmutable($value); + return new \DateTimeImmutable($value); } catch (\Exception $e) { return null; } @@ -278,10 +276,9 @@ protected function convertToDateTime(string $value): ?DateTimeImmutable protected function convertToBoolean(string $value): bool { - switch(strtolower($value)) - { - case "1": - case "true": + switch (strtolower($value)) { + case '1': + case 'true': return true; default: return false; @@ -291,7 +288,7 @@ protected function convertToBoolean(string $value): bool /** * Gets all properties set on this object. * - * @return Property[] + * @return Property[] */ public function getProperties(): array { @@ -300,16 +297,16 @@ public function getProperties(): array foreach ($this->audios as $audio) { $properties = array_merge($properties, $audio->getProperties()); } - - if ($this->title !== null) { + + if (null !== $this->title) { $properties[] = new Property(Property::TITLE, $this->title); } - if ($this->description !== null) { + if (null !== $this->description) { $properties[] = new Property(Property::DESCRIPTION, $this->description); } - if ($this->determiner !== null) { + if (null !== $this->determiner) { $properties[] = new Property(Property::DETERMINER, $this->determiner); } @@ -317,7 +314,7 @@ public function getProperties(): array $properties = array_merge($properties, $image->getProperties()); } - if ($this->locale !== null) { + if (null !== $this->locale) { $properties[] = new Property(Property::LOCALE, $this->locale); } @@ -325,27 +322,27 @@ public function getProperties(): array $properties[] = new Property(Property::LOCALE_ALTERNATE, $locale); } - if ($this->richAttachment !== null) { - $properties[] = new Property(Property::RICH_ATTACHMENT, (int)$this->richAttachment); + if (null !== $this->richAttachment) { + $properties[] = new Property(Property::RICH_ATTACHMENT, (int) $this->richAttachment); } foreach ($this->seeAlso as $seeAlso) { $properties[] = new Property(Property::SEE_ALSO, $seeAlso); } - if ($this->siteName !== null) { + if (null !== $this->siteName) { $properties[] = new Property(Property::SITE_NAME, $this->siteName); } - if ($this->type !== null) { + if (null !== $this->type) { $properties[] = new Property(Property::TYPE, $this->type); } - if ($this->updatedTime !== null) { - $properties[] = new Property(Property::UPDATED_TIME, $this->updatedTime->format("c")); + if (null !== $this->updatedTime) { + $properties[] = new Property(Property::UPDATED_TIME, $this->updatedTime->format('c')); } - if ($this->url !== null) { + if (null !== $this->url) { $properties[] = new Property(Property::URL, $this->url); } diff --git a/src/Objects/Website.php b/src/Objects/Website.php index f66428c..a973c77 100644 --- a/src/Objects/Website.php +++ b/src/Objects/Website.php @@ -1,5 +1,12 @@ type = self::TYPE; } } diff --git a/src/Property.php b/src/Property.php index e94593b..00fe7be 100644 --- a/src/Property.php +++ b/src/Property.php @@ -1,5 +1,12 @@ key = $key; - $this->value = $value; + /** + * Value of the property. + */ + public mixed $value, + ) { } } diff --git a/src/Publisher.php b/src/Publisher.php index 059363a..7a6eb66 100644 --- a/src/Publisher.php +++ b/src/Publisher.php @@ -1,18 +1,23 @@ doctype == self::DOCTYPE_XHTML ? " />" : ">"); + $html = ''; + $format = 'doctype ? ' />' : '>'); foreach ($object->getProperties() as $property) { - if ($html !== "") { + if ('' !== $html) { $html .= "\n"; } - if ($property->value === null) { + if (null === $property->value) { continue; - } elseif ($property->value instanceof DateTimeInterface) { - $value = $property->value->format("c"); - } elseif (is_object($property->value)) { - throw new UnexpectedValueException( - sprintf( + } elseif ($property->value instanceof \DateTimeInterface) { + $value = $property->value->format('c'); + } elseif (\is_object($property->value)) { + throw new \UnexpectedValueException( + \sprintf( "Cannot handle value of type '%s' for property '%s'.", - get_class($property->value), + \get_class($property->value), $property->key ) ); - } elseif ($property->value === true) { - $value = "1"; - } elseif ($property->value === false) { - $value = "0"; + } elseif (true === $property->value) { + $value = '1'; + } elseif (false === $property->value) { + $value = '0'; } else { - $value = (string)$property->value; + $value = (string) $property->value; } - $html .= sprintf($format, $property->key, htmlspecialchars($value)); + $html .= \sprintf($format, $property->key, htmlspecialchars($value)); } return $html; diff --git a/tests/ConsumerTest.php b/tests/ConsumerTest.php index 7a133ab..36ac662 100644 --- a/tests/ConsumerTest.php +++ b/tests/ConsumerTest.php @@ -1,5 +1,12 @@ - - - - - - - - - - - - - - - - -LONG; + + + + + + + + + + + + + + + + + + LONG; $consumer = new Consumer(); // act - $result = $consumer->loadHtml($content, "about:blank"); + $result = $consumer->loadHtml($content, 'about:blank'); // assert self::assertSame('Description', $result->description); @@ -60,19 +67,19 @@ public function testLoadHtmlFallbacksOff(): void { // arrange $content = << - -Title - - - - -LONG; + + + Title + + + + + LONG; $consumer = new Consumer(); // act - $result = $consumer->loadHtml($content, "about:blank"); + $result = $consumer->loadHtml($content, 'about:blank'); // assert self::assertNull($result->description); @@ -87,25 +94,25 @@ public function testLoadHtmlFallbacksOn(): void { // arrange $content = << - -Title - - - - -LONG; + + + Title + + + + + LONG; $consumer = new Consumer(); $consumer->useFallbackMode = true; // act - $result = $consumer->loadHtml($content, "about:blank"); + $result = $consumer->loadHtml($content, 'about:blank'); // assert - self::assertSame("Description", $result->description); - self::assertSame("Title", $result->title); - self::assertSame("about:blank", $result->url); + self::assertSame('Description', $result->description); + self::assertSame('Title', $result->title); + self::assertSame('about:blank', $result->url); } /** @@ -115,26 +122,26 @@ public function testLoadHtmlCanonicalLinkFallbacksOn(): void { // arrange $content = << - -Title - - - - - -LONG; + + + Title + + + + + + LONG; $consumer = new Consumer(); $consumer->useFallbackMode = true; // act - $result = $consumer->loadHtml($content, "about:blank"); + $result = $consumer->loadHtml($content, 'about:blank'); // assert - self::assertSame("Description", $result->description); - self::assertSame("Title", $result->title); - self::assertSame("https://github.com/fusonic/opengraph", $result->url); + self::assertSame('Description', $result->description); + self::assertSame('Title', $result->title); + self::assertSame('https://github.com/fusonic/opengraph', $result->url); } /** @@ -145,18 +152,18 @@ public function testLoadHtmlArrayHandling(): void { // arrange $content = << - - - - - - - - - - -LONG; + + + + + + + + + + + + LONG; $consumer = new Consumer(); @@ -164,14 +171,14 @@ public function testLoadHtmlArrayHandling(): void $result = $consumer->loadHtml($content); // assert - self::assertSame(3, count($result->images)); - self::assertSame("http://example.com/rock.jpg", $result->images[0]->url); + self::assertSame(3, \count($result->images)); + self::assertSame('http://example.com/rock.jpg', $result->images[0]->url); self::assertSame(300, $result->images[0]->width); self::assertSame(300, $result->images[0]->height); - self::assertSame("http://example.com/rock2.jpg", $result->images[1]->url); + self::assertSame('http://example.com/rock2.jpg', $result->images[1]->url); self::assertNull($result->images[1]->width); self::assertNull($result->images[1]->height); - self::assertSame("http://example.com/rock3.jpg", $result->images[2]->url); + self::assertSame('http://example.com/rock3.jpg', $result->images[2]->url); self::assertNull($result->images[2]->width); self::assertSame(1000, $result->images[2]->height); } @@ -180,17 +187,17 @@ public function testLoadHtmlImages(): void { // arrange $content = << - - - - - - - - - -LONG; + + + + + + + + + + + LONG; $consumer = new Consumer(); @@ -198,29 +205,29 @@ public function testLoadHtmlImages(): void $result = $consumer->loadHtml($content); // assert - self::assertSame(1, count($result->images)); - self::assertSame("http://example.com/rock.jpg", $result->images[0]->url); - self::assertSame("https://example.com/rock.jpg", $result->images[0]->secureUrl); + self::assertSame(1, \count($result->images)); + self::assertSame('http://example.com/rock.jpg', $result->images[0]->url); + self::assertSame('https://example.com/rock.jpg', $result->images[0]->secureUrl); self::assertSame(300, $result->images[0]->width); self::assertSame(300, $result->images[0]->height); - self::assertSame("image/jpg", $result->images[0]->type); + self::assertSame('image/jpg', $result->images[0]->type); } public function testLoadHtmlVideos(): void { // arrange $content = << - - - - - - - - - -LONG; + + + + + + + + + + + LONG; $consumer = new Consumer(); @@ -228,27 +235,27 @@ public function testLoadHtmlVideos(): void $result = $consumer->loadHtml($content); // assert - self::assertSame(1, count($result->videos)); - self::assertSame("http://example.com/rock.ogv", $result->videos[0]->url); - self::assertSame("https://example.com/rock.ogv", $result->videos[0]->secureUrl); + self::assertSame(1, \count($result->videos)); + self::assertSame('http://example.com/rock.ogv', $result->videos[0]->url); + self::assertSame('https://example.com/rock.ogv', $result->videos[0]->secureUrl); self::assertSame(300, $result->videos[0]->width); self::assertSame(300, $result->videos[0]->height); - self::assertSame("video/ogv", $result->videos[0]->type); + self::assertSame('video/ogv', $result->videos[0]->type); } public function testLoadHtmlAudios(): void { // arrange $content = << - - - - - - - -LONG; + + + + + + + + + LONG; $consumer = new Consumer(); @@ -256,23 +263,23 @@ public function testLoadHtmlAudios(): void $result = $consumer->loadHtml($content); // assert - self::assertSame(1, count($result->audios)); - self::assertSame("http://example.com/rock.mp3", $result->audios[0]->url); - self::assertSame("https://example.com/rock.mp3", $result->audios[0]->secureUrl); - self::assertSame("audio/mp3", $result->audios[0]->type); + self::assertSame(1, \count($result->audios)); + self::assertSame('http://example.com/rock.mp3', $result->audios[0]->url); + self::assertSame('https://example.com/rock.mp3', $result->audios[0]->secureUrl); + self::assertSame('audio/mp3', $result->audios[0]->type); } public function testCrawlHtmlImageExceptionDebugOff(): void { // arrange $content = << - - - - - -LONG; + + + + + + + LONG; $consumer = new Consumer(); @@ -280,7 +287,7 @@ public function testCrawlHtmlImageExceptionDebugOff(): void $result = $consumer->loadHtml($content); // assert - self::assertSame(0, count($result->images)); + self::assertSame(0, \count($result->images)); } public function testCrawlHtmlImageExceptionDebugOn(): void @@ -290,13 +297,13 @@ public function testCrawlHtmlImageExceptionDebugOn(): void // arrange $content = << - - - - - -LONG; + + + + + + + LONG; $consumer = new Consumer(); $consumer->debug = true; @@ -309,13 +316,13 @@ public function testCrawlHtmlVideoExceptionDebugOff(): void { // arrange $content = << - - - - - -LONG; + + + + + + + LONG; $consumer = new Consumer(); @@ -323,7 +330,7 @@ public function testCrawlHtmlVideoExceptionDebugOff(): void $result = $consumer->loadHtml($content); // assert - self::assertSame(0, count($result->videos)); + self::assertSame(0, \count($result->videos)); } public function testCrawlHtmlVideoExceptionDebugOn(): void @@ -333,13 +340,13 @@ public function testCrawlHtmlVideoExceptionDebugOn(): void // arrange $content = << - - - - - -LONG; + + + + + + + LONG; $consumer = new Consumer(); $consumer->debug = true; @@ -352,13 +359,13 @@ public function testCrawlHtmlAudioExceptionDebugOff(): void { // arrange $content = << - - - - - -LONG; + + + + + + + LONG; $consumer = new Consumer(); @@ -366,7 +373,7 @@ public function testCrawlHtmlAudioExceptionDebugOff(): void $result = $consumer->loadHtml($content); // assert - self::assertSame(0, count($result->audios)); + self::assertSame(0, \count($result->audios)); } public function testCrawlHtmlAudioExceptionDebugOn(): void @@ -376,13 +383,13 @@ public function testCrawlHtmlAudioExceptionDebugOn(): void // arrange $content = << - - - - - -LONG; + + + + + + + LONG; $consumer = new Consumer(); $consumer->debug = true; @@ -395,13 +402,13 @@ public function testLoadHtmlSpecialCharacters(): void { // arrange $content = << - - - - - -LONG; + + + + + + + LONG; $consumer = new Consumer(); @@ -409,20 +416,20 @@ public function testLoadHtmlSpecialCharacters(): void $result = $consumer->loadHtml($content); // assert - self::assertSame("Apples & Bananas - just \"Fruits\"", $result->title); + self::assertSame('Apples & Bananas - just "Fruits"', $result->title); } public function testReadMetaName(): void { // arrange $content = << - - - - - -LONG; + + + + + + + LONG; $consumer = new Consumer(); diff --git a/tests/PublisherTest.php b/tests/PublisherTest.php index ebd2f79..a63df8f 100644 --- a/tests/PublisherTest.php +++ b/tests/PublisherTest.php @@ -1,14 +1,18 @@ expectException(UnexpectedValueException::class); + $this->expectException(\UnexpectedValueException::class); // arrange $publisher = new Publisher(); - $object = new TestPublishObject(new stdClass()); + $object = new TestPublishObject(new \stdClass()); // act $publisher->generateHtml($object); @@ -61,16 +65,16 @@ public function testGenerateHtmlUnsupportedObject(): void public static function generateHtmlValuesProvider(): array { return [ - "Boolean true" => [ true, "1" ], - "Boolean false" => [ false, "0" ], - "Integer 1" => [ 1, "1" ], - "Integer -1" => [ -1, "-1" ], - "Float 1.11111" => [ 1.11111, "1.11111" ], - "Float -1.11111" => [ -1.11111, "-1.11111" ], - "DateTime" => [ new DateTime("2014-07-21T20:14:00+02:00"), "2014-07-21T20:14:00+02:00" ], - "String" => [ "string", "string" ], - "String with quotes" => [ "some \" quotes", "some " quotes" ], - "String with ampersands" => [ "some & ampersand", "some & ampersand" ], + 'Boolean true' => [true, '1'], + 'Boolean false' => [false, '0'], + 'Integer 1' => [1, '1'], + 'Integer -1' => [-1, '-1'], + 'Float 1.11111' => [1.11111, '1.11111'], + 'Float -1.11111' => [-1.11111, '-1.11111'], + 'DateTime' => [new \DateTime('2014-07-21T20:14:00+02:00'), '2014-07-21T20:14:00+02:00'], + 'String' => ['string', 'string'], + 'String with quotes' => ['some " quotes', 'some " quotes'], + 'String with ampersands' => ['some & ampersand', 'some & ampersand'], ]; } } diff --git a/tests/TestData/TestPublishObject.php b/tests/TestData/TestPublishObject.php index 665bb2c..438fbbe 100644 --- a/tests/TestData/TestPublishObject.php +++ b/tests/TestData/TestPublishObject.php @@ -1,5 +1,12 @@ Date: Thu, 28 Nov 2024 09:14:27 +0100 Subject: [PATCH 4/7] Install PHPStan and fix code --- composer.json | 5 +++++ phpstan.neon | 11 +++++++++++ src/Consumer.php | 29 ++++++++++++++--------------- tests/ConsumerTest.php | 14 +++++++------- tests/PublisherTest.php | 10 +++++----- 5 files changed, 42 insertions(+), 27 deletions(-) create mode 100644 phpstan.neon diff --git a/composer.json b/composer.json index cae291f..350521a 100644 --- a/composer.json +++ b/composer.json @@ -34,6 +34,10 @@ "require-dev": { "friendsofphp/php-cs-fixer": "^3.65", "nyholm/psr7": "^1.2", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^11.4", "symfony/http-client": "^5.4 || ^6.4 || ^7.1" }, @@ -44,6 +48,7 @@ "scripts": { "phpcs:check": "XDEBUG_MODE=off vendor/bin/php-cs-fixer check -v --diff", "phpcs:fix": "XDEBUG_MODE=off vendor/bin/php-cs-fixer fix -v", + "phpstan": "XDEBUG_MODE=off php -d memory_limit=2048M vendor/bin/phpstan analyse", "test": "XDEBUG_MODE=off vendor/bin/phpunit --testdox" }, "config": { diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..558a859 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,11 @@ +parameters: + level: 8 + paths: + - src + - tests + +includes: + - vendor/phpstan/phpstan-deprecation-rules/rules.neon + - vendor/phpstan/phpstan-phpunit/extension.neon + - vendor/phpstan/phpstan-phpunit/rules.neon + - vendor/phpstan/phpstan-strict-rules/rules.neon diff --git a/src/Consumer.php b/src/Consumer.php index 155ba34..7dc884a 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -17,13 +17,10 @@ use Symfony\Component\DomCrawler\Crawler; /** - * Consumer that extracts Open Graph data from either a URL or a HTML string. + * Consumer that extracts Open Graph data from either a URL or an HTML string. */ class Consumer { - private ?ClientInterface $client; - private ?RequestFactoryInterface $requestFactory; - /** * When enabled, crawler will read content of title and meta description if no * Open Graph data is provided by target page. @@ -40,10 +37,10 @@ class Consumer * @param ClientInterface|null $client a PSR-18 ClientInterface implementation * @param RequestFactoryInterface|null $requestFactory a PSR-17 RequestFactoryInterface implementation */ - public function __construct(?ClientInterface $client = null, ?RequestFactoryInterface $requestFactory = null) - { - $this->client = $client; - $this->requestFactory = $requestFactory; + public function __construct( + private ?ClientInterface $client = null, + private ?RequestFactoryInterface $requestFactory = null, + ) { } /** @@ -55,7 +52,7 @@ public function __construct(?ClientInterface $client = null, ?RequestFactoryInte */ public function loadUrl(string $url): ObjectBase { - if (null === $this->client) { + if (null === $this->client || null === $this->requestFactory) { throw new \LogicException( 'To use loadUrl() you must provide $client and $requestFactory when instantiating the consumer.' ); @@ -90,7 +87,7 @@ public function loadHtml(string $html, ?string $fallbackUrl = null): ObjectBase private function extractOpenGraphData(string $content): ObjectBase { $crawler = new Crawler(); - $crawler->addHTMLContent($content, 'UTF-8'); + $crawler->addHtmlContent(content: $content); $properties = []; foreach (['name', 'property'] as $t) { @@ -99,6 +96,8 @@ private function extractOpenGraphData(string $content): ObjectBase // Create clean property array $props = []; + + /** @var \DOMElement $tag */ foreach ($ogMetaTags as $tag) { $name = strtolower(trim($tag->getAttribute($t))); $value = trim($tag->getAttribute('content')); @@ -115,15 +114,15 @@ private function extractOpenGraphData(string $content): ObjectBase $object->assignProperties($properties, $this->debug); // Fallback for url - if ($this->useFallbackMode && !$object->url) { + if ($this->useFallbackMode && null === $object->url) { $urlElement = $crawler->filter("link[rel='canonical']")->first(); if ($urlElement->count() > 0) { - $object->url = trim($urlElement->attr('href')); + $object->url = trim($urlElement->attr('href') ?? ''); } } // Fallback for title - if ($this->useFallbackMode && !$object->title) { + if ($this->useFallbackMode && null === $object->title) { $titleElement = $crawler->filter('title')->first(); if ($titleElement->count() > 0) { $object->title = trim($titleElement->text()); @@ -131,10 +130,10 @@ private function extractOpenGraphData(string $content): ObjectBase } // Fallback for description - if ($this->useFallbackMode && !$object->description) { + if ($this->useFallbackMode && null === $object->description) { $descriptionElement = $crawler->filter("meta[property='description']")->first(); if ($descriptionElement->count() > 0) { - $object->description = trim($descriptionElement->attr('content')); + $object->description = trim($descriptionElement->attr('content') ?? ''); } } diff --git a/tests/ConsumerTest.php b/tests/ConsumerTest.php index 36ac662..652d124 100644 --- a/tests/ConsumerTest.php +++ b/tests/ConsumerTest.php @@ -171,7 +171,7 @@ public function testLoadHtmlArrayHandling(): void $result = $consumer->loadHtml($content); // assert - self::assertSame(3, \count($result->images)); + self::assertCount(3, $result->images); self::assertSame('http://example.com/rock.jpg', $result->images[0]->url); self::assertSame(300, $result->images[0]->width); self::assertSame(300, $result->images[0]->height); @@ -205,7 +205,7 @@ public function testLoadHtmlImages(): void $result = $consumer->loadHtml($content); // assert - self::assertSame(1, \count($result->images)); + self::assertCount(1, $result->images); self::assertSame('http://example.com/rock.jpg', $result->images[0]->url); self::assertSame('https://example.com/rock.jpg', $result->images[0]->secureUrl); self::assertSame(300, $result->images[0]->width); @@ -235,7 +235,7 @@ public function testLoadHtmlVideos(): void $result = $consumer->loadHtml($content); // assert - self::assertSame(1, \count($result->videos)); + self::assertCount(1, $result->videos); self::assertSame('http://example.com/rock.ogv', $result->videos[0]->url); self::assertSame('https://example.com/rock.ogv', $result->videos[0]->secureUrl); self::assertSame(300, $result->videos[0]->width); @@ -263,7 +263,7 @@ public function testLoadHtmlAudios(): void $result = $consumer->loadHtml($content); // assert - self::assertSame(1, \count($result->audios)); + self::assertCount(1, $result->audios); self::assertSame('http://example.com/rock.mp3', $result->audios[0]->url); self::assertSame('https://example.com/rock.mp3', $result->audios[0]->secureUrl); self::assertSame('audio/mp3', $result->audios[0]->type); @@ -287,7 +287,7 @@ public function testCrawlHtmlImageExceptionDebugOff(): void $result = $consumer->loadHtml($content); // assert - self::assertSame(0, \count($result->images)); + self::assertCount(0, $result->images); } public function testCrawlHtmlImageExceptionDebugOn(): void @@ -330,7 +330,7 @@ public function testCrawlHtmlVideoExceptionDebugOff(): void $result = $consumer->loadHtml($content); // assert - self::assertSame(0, \count($result->videos)); + self::assertCount(0, $result->videos); } public function testCrawlHtmlVideoExceptionDebugOn(): void @@ -373,7 +373,7 @@ public function testCrawlHtmlAudioExceptionDebugOff(): void $result = $consumer->loadHtml($content); // assert - self::assertSame(0, \count($result->audios)); + self::assertCount(0, $result->audios); } public function testCrawlHtmlAudioExceptionDebugOn(): void diff --git a/tests/PublisherTest.php b/tests/PublisherTest.php index a63df8f..869c4d2 100644 --- a/tests/PublisherTest.php +++ b/tests/PublisherTest.php @@ -31,7 +31,7 @@ public function testGenerateHtmlNull(): void } #[DataProvider('generateHtmlValuesProvider')] - public function testGenerateHtmlValues($value, $expectedContent) + public function testGenerateHtmlValues(int|float|bool|string|\DateTime $value, string $expectedContent): void { // arrange $publisher = new Publisher(); @@ -42,10 +42,7 @@ public function testGenerateHtmlValues($value, $expectedContent) $result = $publisher->generateHtml($object); // assert - self::assertSame( - expected: \sprintf('', TestPublishObject::KEY, $expectedContent), - actual: $result - ); + self::assertSame(\sprintf('', TestPublishObject::KEY, $expectedContent), $result); } public function testGenerateHtmlUnsupportedObject(): void @@ -62,6 +59,9 @@ public function testGenerateHtmlUnsupportedObject(): void $publisher->generateHtml($object); } + /** + * @return array> + */ public static function generateHtmlValuesProvider(): array { return [ From 8e4f9cff32da9632a31694c8d013aaf9202e8cc8 Mon Sep 17 00:00:00 2001 From: Dominic Luidold Date: Thu, 28 Nov 2024 09:47:29 +0100 Subject: [PATCH 5/7] Update version constraints for nyholm/psr7 --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 350521a..0f5705f 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.65", - "nyholm/psr7": "^1.2", + "nyholm/psr7": "^1.8", "phpstan/phpstan": "^2.0", "phpstan/phpstan-deprecation-rules": "^2.0", "phpstan/phpstan-phpunit": "^2.0", @@ -43,7 +43,7 @@ }, "suggest": { "symfony/http-client": "^5.4 || ^6.4 || ^7.1", - "nyholm/psr7": "^1.2" + "nyholm/psr7": "^1.8" }, "scripts": { "phpcs:check": "XDEBUG_MODE=off vendor/bin/php-cs-fixer check -v --diff", From d39c0eff9dcba7d6e9ef8212db11bfdffce78d08 Mon Sep 17 00:00:00 2001 From: Dominic Luidold Date: Thu, 28 Nov 2024 10:26:44 +0100 Subject: [PATCH 6/7] Update documentation --- CHANGELOG.md => CHANGELOG-2.x.md | 0 CHANGELOG-3.x.md | 15 ++++++++ LICENSE | 2 +- README.md | 64 ++++++++++++++++++-------------- UPGRADE-3.0.md | 5 +++ composer.json | 2 +- examples/publish_website.php | 8 ++-- 7 files changed, 62 insertions(+), 34 deletions(-) rename CHANGELOG.md => CHANGELOG-2.x.md (100%) create mode 100644 CHANGELOG-3.x.md create mode 100644 UPGRADE-3.0.md diff --git a/CHANGELOG.md b/CHANGELOG-2.x.md similarity index 100% rename from CHANGELOG.md rename to CHANGELOG-2.x.md diff --git a/CHANGELOG-3.x.md b/CHANGELOG-3.x.md new file mode 100644 index 0000000..959a7f2 --- /dev/null +++ b/CHANGELOG-3.x.md @@ -0,0 +1,15 @@ +# CHANGELOG for 3.x + +## 3.0.0 + +- Bumped the required PHP version from `^7.4 || ^8.0` to `^8.1` +- Bumped the compatible Symfony version from `^3.0 || ^4.0 || ^5.0 || ^6.0` to `^5.4 || ^6.4 || ^7.1` +- Upgraded PHPUnit from `^9.0` to `^11.4` and updated tests accordingly + - Added `composer test` script for running tests +- Installed `friendsofphp/php-cs-fixer` and applied Fusonic's code style + - Added `composer phpcs:check` script for validating code style + - Added `composer phpcs:fix` script for fixing code style violations +- Installed `phpstan/phpstan`, `phpstan/phpstan-deprecation-rules`, `phpstan/phpstan-phpunit` and + `phpstan/phpstan-strict-rules` and fixed reported errors + - Added `composer phpstan` script for validating code +- Updated documentation diff --git a/LICENSE b/LICENSE index 46c173a..bb8f68c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014 Fusonic GmbH (http://www.fusonic.net) +Copyright (c) 2014-2024 Fusonic GmbH (https://www.fusonic.net) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index b256cac..f9fe60f 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,63 @@ # fusonic/opengraph -[![Latest Stable Version](https://poser.pugx.org/fusonic/opengraph/v/stable)](https://packagist.org/packages/fusonic/opengraph) -[![Total Downloads](https://poser.pugx.org/fusonic/opengraph/downloads)](https://packagist.org/packages/fusonic/opengraph) -[![Build Status](https://api.travis-ci.org/fusonic/opengraph.svg)](https://travis-ci.org/fusonic/opengraph) -[![License](https://poser.pugx.org/fusonic/opengraph/license)](https://packagist.org/packages/fusonic/opengraph) +[![GitHub Release](https://img.shields.io/github/v/release/fusonic/opengraph)](https://github.com/fusonic/opengraph/releases/latest) +[![Packagist Downloads](https://img.shields.io/packagist/dt/fusonic/opengraph?color=blue)](https://packagist.org/packages/fusonic/opengraph) +[![Packagist License](https://img.shields.io/packagist/l/fusonic/opengraph)](https://github.com/fusonic/opengraph/blob/master/LICENSE) -A simple library to read Open Graph data from the web and generate HTML code to publish your own Open Graph objects. A fallback mode enables you to read data from websites that do not implement the Open Graph protocol. +A simple library to read Open Graph data from the web and generate HTML code to publish your own Open Graph objects. A +fallback mode enables you to read data from websites that do not implement the Open Graph protocol. -Using this library you can easily retrieve stuff like meta data, video information from YouTube or Vimeo or image information from Flickr without using site-specific APIs since they all implement the Open Graph protocol. +Using this library you can easily retrieve stuff like metadata, video information from YouTube or Vimeo or image +information from Flickr without using site-specific APIs since they all implement the Open Graph protocol. -See [ogp.me](http://ogp.me) for information on the Open Graph protocol. +See [ogp.me](https://ogp.me) for information on the Open Graph protocol. ## Requirements -* PHP 7.4+ -* [fusonic/linq](https://github.com/fusonic/linq) -* [symfony/css-selector](https://github.com/symfony/CssSelector) -* [symfony/dom-crawler](https://github.com/symfony/DomCrawler) -* [psr/http-client](https://github.com/php-fig/http-client), [psr/http-factory](https://github.com/php-fig/http-factory) and compatible implementation such as [guzzle/guzzle](https://github.com/guzzle/guzzle) +* PHP 8.1+ +* [symfony/css-selector](https://github.com/symfony/css-selector) +* [symfony/dom-crawler](https://github.com/symfony/dom-crawler) +* [psr/http-client](https://github.com/php-fig/http-client) +* [psr/http-factory](https://github.com/php-fig/http-factory) +* and compatible implementation such as [symfony/http-client](https://github.com/symfony/http-client) ## Installation The most flexible installation method is using Composer: -``` bash +```bash composer require fusonic/opengraph ``` -Install composer and run install command: -``` bash +Install Composer and run the `install` command: +```bash curl -s http://getcomposer.org/installer | php php composer.phar install ``` -Once installed, include vendor/autoload.php in your script. +Once installed, include `vendor/autoload.php` in your script. ``` php -require "vendor/autoload.php"; +require 'vendor/autoload.php'; ``` ## Usage ### Retrieve Open Graph data from a URL -``` php +```php +loadUrl("http://www.youtube.com/watch?v=P422jZg50X4"); +$object = $consumer->loadUrl('https://www.youtube.com/watch?v=P422jZg50X4'); // Basic information of the object echo "Title: " . $object->title; // Getting started with Facebook Open Graph echo "Site name: " . $object->siteName; // YouTube echo "Description: " . $object->description; // Originally recorded at the Facebook World ... -echo "Canonical URL: " . $object->url; // http://www.youtube.com/watch?v=P422jZg50X4 +echo "Canonical URL: " . $object->url; // https://www.youtube.com/watch?v=P422jZg50X4 // Images $image = $object->images[0]; @@ -63,7 +67,7 @@ echo "Image[0] width: " . $image->width; // null (May return width in pi // Videos $video = $object->videos[0]; -echo "Video URL: " . $video->url; // http://www.youtube.com/v/P422jZg50X4?version=3&autohide=1 +echo "Video URL: " . $video->url; // https://www.youtube.com/v/P422jZg50X4?version=3&autohide=1 echo "Video height: " . $video->height; // 1080 echo "Video width: " . $video->width; // 1920 echo "Video type: " . $video->type; // application/x-shockwave-flash @@ -73,7 +77,9 @@ _There are some more properties but these are the basic and most commonly used o ### Publish own Open Graph data -``` php +```php +title = "Getting started with Facebook Open Graph"; $object->siteName = "YouTube"; $object->description = "Originally recorded at the Facebook World ..." -$object->url = "http://www.youtube.com/watch?v=P422jZg50X4"; +$object->url = "https://www.youtube.com/watch?v=P422jZg50X4"; // Images $image = new Image("https://i1.ytimg.com/vi/P422jZg50X4/maxresdefault.jpg"); $object->images[] = $image; // Videos -$video = new Video("http://www.youtube.com/v/P422jZg50X4?version=3&autohide=1"); +$video = new Video("https://www.youtube.com/v/P422jZg50X4?version=3&autohide=1"); $video->height = 1080; $video->width = 1920; $video->type = "application/x-shockwave-flash"; @@ -125,18 +131,20 @@ _HTML code is formatted just for displaying purposes. You may choose between HTM ## Running tests -You can run the test suite by running `phpunit` from the command line. +You can run the test suite by running `composer test` from the command line. ## FAQ **I don't get any information from a webpage, but Facebook shows information for the same URL. What do I do wrong?** -It seems that some pages (like Twitter) only publish OpenGraph information if Facebook's user agent string `facebookexternalhit/1.1` is used (see #28). So you should configure your PSR-18 client to use this user agent string: +It seems that some pages (like Twitter/X) only publish Open Graph information if Facebook's user agent string +`facebookexternalhit/1.1` is used (see [#28](https://github.com/fusonic/opengraph/issues/28)). So you should configure +your PSR-18 client to use this user agent string: ```php -$client = new Psr18Client(new NativeHttpClient([ "headers" => [ "User-Agent" => "facebookexternalhit/1.1" ] ])); +$client = new Psr18Client(new NativeHttpClient(['headers' => ['User-Agent' => 'facebookexternalhit/1.1']])); ``` ## License -This library is licensed under the MIT license. +fusonic/opengraph is licensed under the MIT license. See [LICENSE](LICENSE) for more information. diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md new file mode 100644 index 0000000..8bb9fef --- /dev/null +++ b/UPGRADE-3.0.md @@ -0,0 +1,5 @@ +# UPGRADE FROM 2.2 TO 3.0 + +## Requirements +- Bumped the required PHP version from `^7.4 || ^8.0` to `^8.1` +- Bumped the compatible Symfony version from `^3.0 || ^4.0 || ^5.0 || ^6.0` to `^5.4 || ^6.4 || ^7.1` diff --git a/composer.json b/composer.json index 0f5705f..079ee5e 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "opengraph" ], "type": "library", - "homepage": "https://github.com/fusonic/fusonic-opengraph", + "homepage": "https://github.com/fusonic/opengraph", "license": "MIT", "authors": [ { diff --git a/examples/publish_website.php b/examples/publish_website.php index ceb68a8..71d9653 100644 --- a/examples/publish_website.php +++ b/examples/publish_website.php @@ -19,28 +19,28 @@ $website = new Website(); // Set some basic properties -$website->url = 'http://www.fusonic.net'; +$website->url = 'https://www.fusonic.net'; $website->title = 'Fusonic - Intranet & Mobile Applications from Austria'; $website->description = 'Creators of the awesome fusonic-opengraph library.'; $website->siteName = 'Fusonic'; $website->locale = 'en_GB'; // Attach an image -$image = new Image('http://www.fusonic.net/en/assets/images/logo.png'); +$image = new Image('https://www.fusonic.net/en/assets/images/logo.png'); $image->width = 140; $image->height = 41; $image->type = 'image/png'; $website->images[] = $image; // Attach a video -$video = new Video('http://www.fusonic.net/en/we-dont-have-no-video.mp4'); +$video = new Video('https://www.fusonic.net/en/we-dont-have-no-video.mp4'); $video->width = 1920; $video->height = 1080; $video->type = 'video/mp4'; $website->videos[] = $video; // Attach an audio -$audio = new Audio('http://www.fusonic.net/en/we-dont-have-no-audio.mp3'); +$audio = new Audio('https://www.fusonic.net/en/we-dont-have-no-audio.mp3'); $audio->type = 'audio/mp3'; $website->audios[] = $audio; From 9656d1fbb565801a2e0629ccc8a1f2558804f613 Mon Sep 17 00:00:00 2001 From: Dominic Luidold Date: Thu, 28 Nov 2024 10:43:17 +0100 Subject: [PATCH 7/7] Switch to GitHub Actions for testing --- .github/workflows/test.yml | 42 ++++++++++++++++++++++++++++++++++++++ .scrutinizer.yml | 17 --------------- .travis.yml | 6 ------ CHANGELOG-3.x.md | 1 + README.md | 1 + composer.json | 2 +- phpunit.xml.dist | 9 ++++---- 7 files changed, 50 insertions(+), 28 deletions(-) create mode 100644 .github/workflows/test.yml delete mode 100644 .scrutinizer.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..3199cf1 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,42 @@ +name: Test + +on: + push: + branches: + - master + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + test: + name: Test with PHP ${{ matrix.php-versions }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + php-versions: [8.1, 8.2, 8.3] # TODO - Add 8.4 once all tools/dependencies support it + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + + - name: Install dependencies + uses: ramsey/composer-install@v3 + + - name: Run PHP-CS-Fixer + run: composer phpcs:check + + - name: Run PHPStan + run: composer phpstan + + - name: Run PHPUnit + run: composer test diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index 30a9fcf..0000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,17 +0,0 @@ -build: - environment: - php: - version: 7.4 - tests: - override: - - - command: 'vendor/bin/phpunit --coverage-clover coverage-report' - coverage: - file: 'coverage-report' - format: 'clover' - -tools: - php_code_sniffer: - enabled: true - config: - standard: PSR2 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6dcf4bd..0000000 --- a/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: php -php: - - 7.4 - - 8.0 -install: composer install -script: vendor/bin/phpunit diff --git a/CHANGELOG-3.x.md b/CHANGELOG-3.x.md index 959a7f2..dd7cff8 100644 --- a/CHANGELOG-3.x.md +++ b/CHANGELOG-3.x.md @@ -13,3 +13,4 @@ `phpstan/phpstan-strict-rules` and fixed reported errors - Added `composer phpstan` script for validating code - Updated documentation +- Switched to GitHub actions for automated testing diff --git a/README.md b/README.md index f9fe60f..11e8acf 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![GitHub Release](https://img.shields.io/github/v/release/fusonic/opengraph)](https://github.com/fusonic/opengraph/releases/latest) [![Packagist Downloads](https://img.shields.io/packagist/dt/fusonic/opengraph?color=blue)](https://packagist.org/packages/fusonic/opengraph) [![Packagist License](https://img.shields.io/packagist/l/fusonic/opengraph)](https://github.com/fusonic/opengraph/blob/master/LICENSE) +[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/fusonic/opengraph/test)](https://github.com/fusonic/opengraph/actions/workflows/test.yml) A simple library to read Open Graph data from the web and generate HTML code to publish your own Open Graph objects. A fallback mode enables you to read data from websites that do not implement the Open Graph protocol. diff --git a/composer.json b/composer.json index 079ee5e..b5e19e1 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "phpstan/phpstan-deprecation-rules": "^2.0", "phpstan/phpstan-phpunit": "^2.0", "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^11.4", + "phpunit/phpunit": "^10.5 || ^11.4", "symfony/http-client": "^5.4 || ^6.4 || ^7.1" }, "suggest": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 56ab11f..f6aa547 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -26,16 +26,17 @@ - + + + + src