From 4788c385f387f838e5b6bee361b8478075aa6719 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Thu, 2 Mar 2023 20:10:04 +0100 Subject: [PATCH 01/17] Use native types, finalize classes --- .github/workflows/psalm.yml | 4 +- composer.json | 8 +- psalm-baseline.xml | 36 +-------- psalm.xml | 2 + src/Exceptions/InvalidLink.php | 10 +-- src/Generator.php | 7 +- src/Generators/BaseOutlook.php | 20 +++-- src/Generators/Google.php | 19 ++--- src/Generators/Ics.php | 18 +++-- src/Generators/WebOffice.php | 3 +- src/Generators/WebOutlook.php | 3 +- src/Generators/Yahoo.php | 16 ++-- src/Link.php | 85 +++++--------------- tests/Generators/GeneratorTestContract.php | 2 +- tests/Generators/GoogleGeneratorTest.php | 4 +- tests/Generators/IcsGeneratorTest.php | 10 ++- tests/Generators/WebOfficeGeneratorTest.php | 4 +- tests/Generators/WebOutlookGeneratorTest.php | 4 +- tests/Generators/YahooGeneratorTest.php | 4 +- tests/LinkTest.php | 2 +- tests/TestCase.php | 2 +- 21 files changed, 95 insertions(+), 168 deletions(-) diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml index 613fa3f..fe1f0da 100644 --- a/.github/workflows/psalm.yml +++ b/.github/workflows/psalm.yml @@ -4,8 +4,7 @@ on: push: paths: - '**.php' - - 'psalm.xml' - - 'psalm.yml' + - 'psalm*' jobs: psalm: @@ -18,7 +17,6 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: '8.2' - extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick coverage: none - name: Cache composer dependencies diff --git a/composer.json b/composer.json index c7390c5..ba745ff 100644 --- a/composer.json +++ b/composer.json @@ -16,13 +16,13 @@ } ], "require": { - "php": "^8.0" + "php": "^8.1" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.14", - "phpunit/phpunit": "^9.6 || ^10.0", - "spatie/phpunit-snapshot-assertions": "^4.2 || ^5.0", - "vimeo/psalm": "^5.6" + "phpunit/phpunit": "^10.0", + "spatie/phpunit-snapshot-assertions": "^5.0", + "vimeo/psalm": "^5.7.7" }, "autoload": { "psr-4": { diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 793cfb5..9efde3f 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,36 +1,2 @@ - - - - setTimezone - setTimezone - - - - - setTimezone - setTimezone - - - - - setTimezone - setTimezone - - - - - $property - - - modify - - - clone $from - clone $to - - - new static($title, $from, $to, $allDay) - - - + diff --git a/psalm.xml b/psalm.xml index cb3e980..998b1c7 100644 --- a/psalm.xml +++ b/psalm.xml @@ -4,6 +4,8 @@ xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" errorLevel="2" + findUnusedBaselineEntry="false" + findUnusedCode="false" findUnusedVariablesAndParams="true" resolveFromConfigFile="true" useDocblockPropertyTypes="true" diff --git a/src/Exceptions/InvalidLink.php b/src/Exceptions/InvalidLink.php index 5eac383..caff971 100644 --- a/src/Exceptions/InvalidLink.php +++ b/src/Exceptions/InvalidLink.php @@ -1,4 +1,4 @@ -format(self::DATETIME_FORMAT)}`) must be greater than FROM time (`{$from->format(self::DATETIME_FORMAT)}`)"); - } - public static function negativeDateRange(DateTimeInterface $from, DateTimeInterface $to): self { return new self("TO time (`{$to->format(self::DATETIME_FORMAT)}`) must be greater than FROM time (`{$from->format(self::DATETIME_FORMAT)}`)"); diff --git a/src/Generator.php b/src/Generator.php index 3ee0294..6b8d981 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -1,13 +1,12 @@ -baseUrl(); $dateTimeFormat = $link->allDay ? $this->dateFormat : $this->dateTimeFormat; - $utcStartDateTime = (clone $link->from)->setTimezone(new DateTimeZone('UTC')); - $utcEndDateTime = (clone $link->to)->setTimezone(new DateTimeZone('UTC')); + $utcStartDateTime = $link->from->setTimezone(new DateTimeZone('UTC')); + $utcEndDateTime = $link->to->setTimezone(new DateTimeZone('UTC')); $url .= '&startdt='.$utcStartDateTime->format($dateTimeFormat); $url .= '&enddt='.$utcEndDateTime->format($dateTimeFormat); diff --git a/src/Generators/Google.php b/src/Generators/Google.php index 5ff4fe6..bd0ab1d 100644 --- a/src/Generators/Google.php +++ b/src/Generators/Google.php @@ -11,26 +11,23 @@ */ class Google implements Generator { - /** @var string {@see https://www.php.net/manual/en/function.date.php} */ - protected $dateFormat = 'Ymd'; - /** @var string */ - protected $dateTimeFormat = 'Ymd\THis\Z'; + /** @see https://www.php.net/manual/en/function.date.php */ + protected string $dateFormat = 'Ymd'; - /** {@inheritDoc} */ + protected string $dateTimeFormat = 'Ymd\THis\Z'; + + /** @inheritDoc */ public function generate(Link $link): string { $url = 'https://calendar.google.com/calendar/render?action=TEMPLATE'; - $utcStartDateTime = (clone $link->from)->setTimezone(new DateTimeZone('UTC')); - $utcEndDateTime = (clone $link->to)->setTimezone(new DateTimeZone('UTC')); + $utcStartDateTime = $link->from->setTimezone(new DateTimeZone('UTC')); + $utcEndDateTime = $link->to->setTimezone(new DateTimeZone('UTC')); $dateTimeFormat = $link->allDay ? $this->dateFormat : $this->dateTimeFormat; $url .= '&dates='.$utcStartDateTime->format($dateTimeFormat).'/'.$utcEndDateTime->format($dateTimeFormat); // Add timezone name if it is specified in both from and to dates and is the same for both - if ( - $link->from->getTimezone() && $link->to->getTimezone() - && $link->from->getTimezone()->getName() === $link->to->getTimezone()->getName() - ) { + if ($link->from->getTimezone()->getName() === $link->to->getTimezone()->getName()) { $url .= '&ctz=' . $link->from->getTimezone()->getName(); } diff --git a/src/Generators/Ics.php b/src/Generators/Ics.php index b738974..11b5f48 100644 --- a/src/Generators/Ics.php +++ b/src/Generators/Ics.php @@ -1,4 +1,4 @@ - */ - protected $options = []; + protected array $options = []; /** * @param array $options @@ -26,7 +26,7 @@ public function __construct(array $options = []) $this->options = $options; } - /** {@inheritDoc} */ + /** @inheritDoc */ public function generate(Link $link): string { $url = [ @@ -67,6 +67,10 @@ public function generate(Link $link): string return $this->buildLink($url); } + /** + * @param non-empty-list $propertiesAndComponents + * @return non-empty-string + */ protected function buildLink(array $propertiesAndComponents): string { return 'data:text/calendar;charset=utf8;base64,'.base64_encode(implode("\r\n", $propertiesAndComponents)); diff --git a/src/Generators/WebOffice.php b/src/Generators/WebOffice.php index b52a7f6..1462368 100644 --- a/src/Generators/WebOffice.php +++ b/src/Generators/WebOffice.php @@ -1,9 +1,10 @@ -to->format($dateTimeFormat); } else { - $utcStartDateTime = (clone $link->from)->setTimezone(new DateTimeZone('UTC')); - $utcEndDateTime = (clone $link->to)->setTimezone(new DateTimeZone('UTC')); + $utcStartDateTime = $link->from->setTimezone(new DateTimeZone('UTC')); + $utcEndDateTime = $link->to->setTimezone(new DateTimeZone('UTC')); $url .= '&ST='.$utcStartDateTime->format($dateTimeFormat); $url .= '&ET='.$utcEndDateTime->format($dateTimeFormat); } diff --git a/src/Link.php b/src/Link.php index 56ab3c3..23d2f59 100644 --- a/src/Link.php +++ b/src/Link.php @@ -1,4 +1,4 @@ -title = $title; $this->allDay = $allDay; @@ -46,61 +32,42 @@ public function __construct(string $title, \DateTimeInterface $from, \DateTimeIn throw InvalidLink::negativeDateRange($from, $to); } - $this->from = clone $from; - $this->to = clone $to; + $this->from = \DateTimeImmutable::createFromInterface($from); + $this->to = \DateTimeImmutable::createFromInterface($to); } /** - * @param string $title - * @param \DateTimeInterface $from - * @param \DateTimeInterface $to - * @param bool $allDay - * - * @return static - * @throws InvalidLink + * @throws \Spatie\CalendarLinks\Exceptions\InvalidLink When date range is invalid. */ - public static function create(string $title, \DateTimeInterface $from, \DateTimeInterface $to, bool $allDay = false) + public static function create(string $title, \DateTimeInterface $from, \DateTimeInterface $to, bool $allDay = false): static { return new static($title, $from, $to, $allDay); } /** - * @param string $title - * @param \DateTimeInterface|\DateTime|\DateTimeImmutable $fromDate - * @param int $numberOfDays - * - * @return Link - * @throws InvalidLink + * @param positive-int $numberOfDays + * @throws \Spatie\CalendarLinks\Exceptions\InvalidLink When date range is invalid. */ public static function createAllDay(string $title, \DateTimeInterface $fromDate, int $numberOfDays = 1): self { - $from = (clone $fromDate)->modify('midnight'); - $to = (clone $from)->modify("+$numberOfDays days"); + $from = \DateTimeImmutable::createFromInterface($fromDate)->modify('midnight'); + $to = $from->modify("+$numberOfDays days"); + assert($to instanceof \DateTimeImmutable); return new self($title, $from, $to, true); } - /** - * @param string $description - * - * @return $this - */ - public function description(string $description) + /** Set description of the Event. */ + public function description(string $description): static { $this->description = $description; - return $this; } - /** - * @param string $address - * - * @return $this - */ - public function address(string $address) + /** Set address of the Event. */ + public function address(string $address): static { $this->address = $address; - return $this; } @@ -114,10 +81,7 @@ public function google(): string return $this->formatWith(new Google()); } - /** - * @param array $options - * @return string - */ + /** @param array $options */ public function ics(array $options = []): string { return $this->formatWith(new Ics($options)); @@ -137,9 +101,4 @@ public function webOffice(): string { return $this->formatWith(new WebOffice()); } - - public function __get($property) - { - return $this->$property; - } } diff --git a/tests/Generators/GeneratorTestContract.php b/tests/Generators/GeneratorTestContract.php index 69fdf1b..11e6034 100644 --- a/tests/Generators/GeneratorTestContract.php +++ b/tests/Generators/GeneratorTestContract.php @@ -1,4 +1,4 @@ - $options {@see \Spatie\CalendarLinks\Generators\Ics::__construct} * @return \Spatie\CalendarLinks\Generator */ protected function generator(array $options = []): Generator { // extend base class just to make output more readable and simplify reviewing of the snapshot diff return new class($options) extends Ics { + /** + * @param non-empty-list $propertiesAndComponents + * @return non-empty-string + */ protected function buildLink(array $propertiesAndComponents): string { return implode("\r\n", $propertiesAndComponents); diff --git a/tests/Generators/WebOfficeGeneratorTest.php b/tests/Generators/WebOfficeGeneratorTest.php index 9a6ec3d..a4dcb21 100644 --- a/tests/Generators/WebOfficeGeneratorTest.php +++ b/tests/Generators/WebOfficeGeneratorTest.php @@ -1,4 +1,4 @@ - Date: Thu, 2 Mar 2023 19:10:47 +0000 Subject: [PATCH 02/17] Fix styling --- src/Link.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Link.php b/src/Link.php index 23d2f59..8589f28 100644 --- a/src/Link.php +++ b/src/Link.php @@ -61,6 +61,7 @@ public static function createAllDay(string $title, \DateTimeInterface $fromDate, public function description(string $description): static { $this->description = $description; + return $this; } @@ -68,6 +69,7 @@ public function description(string $description): static public function address(string $address): static { $this->address = $address; + return $this; } From edabd2898fafd962d0ba055f261d0ffed2e32fdf Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Thu, 2 Mar 2023 20:14:07 +0100 Subject: [PATCH 03/17] Do not use PHP8.0 on CI --- .github/workflows/run-tests.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index b558a9e..baeb5d9 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -24,7 +24,7 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest] - php: [8.0, 8.1, 8.2] + php: [8.1, 8.2] dependency-version: [prefer-lowest, prefer-stable] name: P${{ matrix.php }} - ${{ matrix.dependency-version }} - ${{ matrix.os }} @@ -37,7 +37,6 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - extensions: dom, json, libxml, mbstring, pcre, reflection, spl, xml, xmlwriter coverage: none ini-values: assert.exception=1, zend.assertions=1 From 18b27befa52ee6144b26f4ec9bd5a33ef9ab0ce7 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Sun, 28 Jan 2024 11:58:20 +0530 Subject: [PATCH 04/17] Use DateTimeImmutable --- src/Generators/Ics.php | 1 - src/Link.php | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Generators/Ics.php b/src/Generators/Ics.php index e9f63d6..f58ef4a 100644 --- a/src/Generators/Ics.php +++ b/src/Generators/Ics.php @@ -17,7 +17,6 @@ class Ics implements Generator /** @see https://www.php.net/manual/en/function.date.php */ protected string $dateFormat = 'Ymd'; - protected string $dateTimeFormat = 'Ymd\THis\Z'; /** @var IcsOptions */ diff --git a/src/Link.php b/src/Link.php index 2ff3783..8bf6e18 100644 --- a/src/Link.php +++ b/src/Link.php @@ -52,7 +52,7 @@ public static function create(string $title, \DateTimeInterface $from, \DateTime { // When creating all day events, we need to be in the UTC timezone as all day events are "floating" based on the user's timezone if ($allDay) { - $startDate = new \DateTime($from->format('Y-m-d'), new \DateTimeZone('UTC')); + $startDate = new \DateTimeImmutable($from->format('Y-m-d'), new \DateTimeZone('UTC')); $numberOfDays = $from->diff($to)->days + 1; return self::createAllDay($title, $startDate, $numberOfDays); @@ -69,7 +69,7 @@ public static function createAllDay(string $title, \DateTimeInterface $fromDate, { // In cases where the from date is not UTC, make sure it's UTC, size all day events are floating and non UTC dates cause bugs in the generators if ($fromDate->getTimezone() !== new \DateTimeZone('UTC')) { - $fromDate = \DateTime::createFromFormat('Y-m-d', $fromDate->format('Y-m-d')); + $fromDate = \DateTimeImmutable::createFromFormat('Y-m-d', $fromDate->format('Y-m-d')); } $from = \DateTimeImmutable::createFromInterface($fromDate)->modify('midnight'); From 464378f16a92a5ba3f05688a908d129ca3a04600 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Sun, 28 Jan 2024 12:19:17 +0530 Subject: [PATCH 05/17] ICS: Allow to customize PRODID --- src/Generators/Ics.php | 2 +- tests/Generators/IcsGeneratorTest.php | 4 ++-- ...=> IcsGeneratorTest__it_supports_custom_product_id__1.txt} | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename tests/Generators/__snapshots__/{IcsGeneratorTest__it_has_a_product_dtstamp__1.txt => IcsGeneratorTest__it_supports_custom_product_id__1.txt} (91%) diff --git a/src/Generators/Ics.php b/src/Generators/Ics.php index f58ef4a..21273b3 100644 --- a/src/Generators/Ics.php +++ b/src/Generators/Ics.php @@ -41,7 +41,7 @@ public function generate(Link $link): string $url = [ 'BEGIN:VCALENDAR', 'VERSION:2.0', // @see https://datatracker.ietf.org/doc/html/rfc5545#section-3.7.4 - 'PRODID:Spatie calendar-links', // @see https://datatracker.ietf.org/doc/html/rfc5545#section-3.7.3 + 'PRODID:'.($this->options['PRODID'] ?? 'Spatie calendar-links'), // @see https://datatracker.ietf.org/doc/html/rfc5545#section-3.7.3 'BEGIN:VEVENT', 'UID:'.($this->options['UID'] ?? $this->generateEventUid($link)), 'SUMMARY:'.$this->escapeString($link->title), diff --git a/tests/Generators/IcsGeneratorTest.php b/tests/Generators/IcsGeneratorTest.php index b39aa57..d1eb992 100644 --- a/tests/Generators/IcsGeneratorTest.php +++ b/tests/Generators/IcsGeneratorTest.php @@ -37,10 +37,10 @@ public function it_can_generate_an_ics_link_with_custom_uid(): void } /** @test */ - public function it_has_a_product_id(): void + public function it_supports_custom_product_id(): void { $this->assertMatchesSnapshot( - $this->generator(['PRODID' => 'Spatie calendar-links'], ['format' => Ics::FORMAT_FILE])->generate($this->createShortEventLink()) + $this->generator(['PRODID' => 'My Product'])->generate($this->createShortEventLink()) ); } diff --git a/tests/Generators/__snapshots__/IcsGeneratorTest__it_has_a_product_dtstamp__1.txt b/tests/Generators/__snapshots__/IcsGeneratorTest__it_supports_custom_product_id__1.txt similarity index 91% rename from tests/Generators/__snapshots__/IcsGeneratorTest__it_has_a_product_dtstamp__1.txt rename to tests/Generators/__snapshots__/IcsGeneratorTest__it_supports_custom_product_id__1.txt index e0bd621..cfba018 100644 --- a/tests/Generators/__snapshots__/IcsGeneratorTest__it_has_a_product_dtstamp__1.txt +++ b/tests/Generators/__snapshots__/IcsGeneratorTest__it_supports_custom_product_id__1.txt @@ -1,6 +1,6 @@ BEGIN:VCALENDAR VERSION:2.0 -PRODID:Spatie calendar-links +PRODID:My Product BEGIN:VEVENT UID:94ab75add84a67c019eae57539658036 SUMMARY:Birthday From 5f02ce13a84d3b0cd6e217490a22d367cdf0e801 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Sun, 28 Jan 2024 12:22:00 +0530 Subject: [PATCH 06/17] ICS: Remove outdated test --- tests/Generators/IcsGeneratorTest.php | 15 +++++---------- .../IcsGeneratorTest__it_has_a_product_id__1.txt | 13 ------------- 2 files changed, 5 insertions(+), 23 deletions(-) delete mode 100644 tests/Generators/__snapshots__/IcsGeneratorTest__it_has_a_product_id__1.txt diff --git a/tests/Generators/IcsGeneratorTest.php b/tests/Generators/IcsGeneratorTest.php index d1eb992..2c18ba3 100644 --- a/tests/Generators/IcsGeneratorTest.php +++ b/tests/Generators/IcsGeneratorTest.php @@ -8,12 +8,15 @@ use Spatie\CalendarLinks\Generators\Ics; use Spatie\CalendarLinks\Tests\TestCase; +/** @psalm-import-type IcsOptions from \Spatie\CalendarLinks\Generators\Ics */ final class IcsGeneratorTest extends TestCase { use GeneratorTestContract; /** - * @param array $options {@see \Spatie\CalendarLinks\Generators\Ics::__construct} + * @psalm-param IcsOptions $options ICS specific properties and components + * @param array $options ICS specific properties and components + * @param array{format?: \Spatie\CalendarLinks\Generators\Ics::FORMAT_*} $presentationOptions * @return \Spatie\CalendarLinks\Generator */ protected function generator(array $options = [], array $presentationOptions = []): Generator @@ -32,7 +35,7 @@ protected function linkMethodName(): string public function it_can_generate_an_ics_link_with_custom_uid(): void { $this->assertMatchesSnapshot( - $this->generator(['UID' => 'random-uid', ['format' => Ics::FORMAT_FILE]])->generate($this->createShortEventLink()) + $this->generator(['UID' => 'random-uid'])->generate($this->createShortEventLink()) ); } @@ -44,14 +47,6 @@ public function it_supports_custom_product_id(): void ); } - /** @test */ - public function it_has_a_product_dtstamp(): void - { - $this->assertMatchesSnapshot( - $this->generator(['DTSTAMP' => '20180201T090000Z'], ['format' => Ics::FORMAT_FILE])->generate($this->createShortEventLink()) - ); - } - /** @test */ public function it_generates_base64_encoded_link_for_html(): void { diff --git a/tests/Generators/__snapshots__/IcsGeneratorTest__it_has_a_product_id__1.txt b/tests/Generators/__snapshots__/IcsGeneratorTest__it_has_a_product_id__1.txt deleted file mode 100644 index e0bd621..0000000 --- a/tests/Generators/__snapshots__/IcsGeneratorTest__it_has_a_product_id__1.txt +++ /dev/null @@ -1,13 +0,0 @@ -BEGIN:VCALENDAR -VERSION:2.0 -PRODID:Spatie calendar-links -BEGIN:VEVENT -UID:94ab75add84a67c019eae57539658036 -SUMMARY:Birthday -DTSTAMP:20180201T090000Z -DTSTART:20180201T090000Z -DTEND:20180201T180000Z -DESCRIPTION:With balloons\, clowns and stuff\nBring a dog\, bring a frog -LOCATION:Party Lane 1A\, 1337 Funtown -END:VEVENT -END:VCALENDAR \ No newline at end of file From c489027e63db99fdf0620a5d331f33f884cf41c2 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Sun, 28 Jan 2024 12:23:16 +0530 Subject: [PATCH 07/17] Use late static binding to allow to extending Link class --- src/Link.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Link.php b/src/Link.php index 8bf6e18..9219f5d 100644 --- a/src/Link.php +++ b/src/Link.php @@ -55,7 +55,7 @@ public static function create(string $title, \DateTimeInterface $from, \DateTime $startDate = new \DateTimeImmutable($from->format('Y-m-d'), new \DateTimeZone('UTC')); $numberOfDays = $from->diff($to)->days + 1; - return self::createAllDay($title, $startDate, $numberOfDays); + return static::createAllDay($title, $startDate, $numberOfDays); } return new static($title, $from, $to, $allDay); @@ -65,7 +65,7 @@ public static function create(string $title, \DateTimeInterface $from, \DateTime * @param positive-int $numberOfDays * @throws \Spatie\CalendarLinks\Exceptions\InvalidLink When date range is invalid. */ - public static function createAllDay(string $title, \DateTimeInterface $fromDate, int $numberOfDays = 1): self + public static function createAllDay(string $title, \DateTimeInterface $fromDate, int $numberOfDays = 1): static { // In cases where the from date is not UTC, make sure it's UTC, size all day events are floating and non UTC dates cause bugs in the generators if ($fromDate->getTimezone() !== new \DateTimeZone('UTC')) { @@ -73,10 +73,16 @@ public static function createAllDay(string $title, \DateTimeInterface $fromDate, } $from = \DateTimeImmutable::createFromInterface($fromDate)->modify('midnight'); + if (! $from instanceof \DateTimeImmutable) { + throw new InvalidLink('Could not modify $fromDate while building all day link.'); + } + $to = $from->modify("+$numberOfDays days"); - assert($to instanceof \DateTimeImmutable); + if (! $to instanceof \DateTimeImmutable) { + throw new InvalidLink('Could not modify $fromDate while calculating $to date.'); + } - return new self($title, $from, $to, true); + return new static($title, $from, $to, true); } /** Set description of the Event. */ From 70ffd79282ca0dba8ab168239558bcbe92f94eba Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Sun, 28 Jan 2024 12:23:29 +0530 Subject: [PATCH 08/17] Fix PHPDoc --- src/Generators/Ics.php | 8 ++++++-- src/Link.php | 6 ------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Generators/Ics.php b/src/Generators/Ics.php index 21273b3..dd325a0 100644 --- a/src/Generators/Ics.php +++ b/src/Generators/Ics.php @@ -7,7 +7,7 @@ /** * @see https://icalendar.org/RFC-Specifications/iCalendar-RFC-5545/ - * @psalm-type IcsOptions = array{UID?: string, URL?: string, REMINDER?: array{DESCRIPTION?: string, TIME?: \DateTimeInterface}} + * @psalm-type IcsOptions = array{UID?: string, URL?: string, PRODID?: string, REMINDER?: array{DESCRIPTION?: string, TIME?: \DateTimeInterface}} */ class Ics implements Generator { @@ -86,7 +86,7 @@ public function generate(Link $link): string } /** - * @param non-empty-list $propertiesAndComponents + * @param non-empty-list $propertiesAndComponents * @return non-empty-string */ protected function buildLink(array $propertiesAndComponents): string @@ -94,6 +94,10 @@ protected function buildLink(array $propertiesAndComponents): string return 'data:text/calendar;charset=utf8;base64,'.base64_encode(implode("\r\n", $propertiesAndComponents)); } + /** + * @param non-empty-list $propertiesAndComponents + * @return non-empty-string + */ protected function buildFile(array $propertiesAndComponents): string { return implode("\r\n", $propertiesAndComponents); diff --git a/src/Link.php b/src/Link.php index 9219f5d..efb3905 100644 --- a/src/Link.php +++ b/src/Link.php @@ -10,12 +10,6 @@ use Spatie\CalendarLinks\Generators\Yahoo; /** - * @property-read string $title - * @property-read \DateTimeInterface|\DateTime|\DateTimeImmutable $from - * @property-read \DateTimeInterface|\DateTime|\DateTimeImmutable $to - * @property-read string $description - * @property-read string $address - * @property-read bool $allDay * @psalm-import-type IcsOptions from \Spatie\CalendarLinks\Generators\Ics */ class Link From 181f6bed73620c1b8e089b064768d6da73a56ae1 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Sun, 28 Jan 2024 12:36:38 +0530 Subject: [PATCH 09/17] Remove `$allDay` argument from `Link::create` (use createAllDay instead) --- src/Link.php | 12 +---- tests/Generators/GeneratorTestContract.php | 6 +-- tests/Generators/IcsGeneratorTest.php | 16 +++---- tests/TestCase.php | 53 ++++++++-------------- 4 files changed, 32 insertions(+), 55 deletions(-) diff --git a/src/Link.php b/src/Link.php index efb3905..6143a3a 100644 --- a/src/Link.php +++ b/src/Link.php @@ -42,17 +42,9 @@ final public function __construct(string $title, \DateTimeInterface $from, \Date /** * @throws \Spatie\CalendarLinks\Exceptions\InvalidLink When date range is invalid. */ - public static function create(string $title, \DateTimeInterface $from, \DateTimeInterface $to, bool $allDay = false): static + public static function create(string $title, \DateTimeInterface $from, \DateTimeInterface $to): static { - // When creating all day events, we need to be in the UTC timezone as all day events are "floating" based on the user's timezone - if ($allDay) { - $startDate = new \DateTimeImmutable($from->format('Y-m-d'), new \DateTimeZone('UTC')); - $numberOfDays = $from->diff($to)->days + 1; - - return static::createAllDay($title, $startDate, $numberOfDays); - } - - return new static($title, $from, $to, $allDay); + return new static($title, $from, $to); } /** diff --git a/tests/Generators/GeneratorTestContract.php b/tests/Generators/GeneratorTestContract.php index 11e6034..94f29c4 100644 --- a/tests/Generators/GeneratorTestContract.php +++ b/tests/Generators/GeneratorTestContract.php @@ -67,12 +67,12 @@ public function it_can_generate_a_multiple_days_event_link_with_allday_flag(): v public function it_can_generate_a_description_is_html_code_event_link_with_allday_flag(): void { $this->assertMatchesSnapshot( - $this->generator()->generate($this->createDescriptionIsHTMLcodeEventLink()) + $this->generator()->generate($this->createDescriptionIsHtmlCodeEventLink()) ); $this->assertSame( - $this->generator()->generate($this->createDescriptionIsHTMLcodeEventLink(false)), - $this->generator()->generate($this->createDescriptionIsHTMLcodeEventLink(true)) + $this->generator()->generate($this->createDescriptionIsHtmlCodeEventLink(false)), + $this->generator()->generate($this->createDescriptionIsHtmlCodeEventLink(true)) ); } } diff --git a/tests/Generators/IcsGeneratorTest.php b/tests/Generators/IcsGeneratorTest.php index 2c18ba3..53e44ee 100644 --- a/tests/Generators/IcsGeneratorTest.php +++ b/tests/Generators/IcsGeneratorTest.php @@ -32,18 +32,18 @@ protected function linkMethodName(): string } /** @test */ - public function it_can_generate_an_ics_link_with_custom_uid(): void + public function it_correctly_generates_all_day_events_by_days(): void { $this->assertMatchesSnapshot( - $this->generator(['UID' => 'random-uid'])->generate($this->createShortEventLink()) + $this->generator()->generate($this->createAllDayEventMultipleDaysWithTimezoneLink()) ); } /** @test */ - public function it_supports_custom_product_id(): void + public function it_correctly_generates_all_day_events_by_dates(): void { $this->assertMatchesSnapshot( - $this->generator(['PRODID' => 'My Product'])->generate($this->createShortEventLink()) + $this->generator()->generate($this->createEventMultipleDaysViaStartEndWithTimezoneLink()) ); } @@ -56,18 +56,18 @@ public function it_generates_base64_encoded_link_for_html(): void } /** @test */ - public function it_correctly_generates_all_day_events_by_days(): void + public function it_can_generate_an_ics_link_with_custom_uid(): void { $this->assertMatchesSnapshot( - $this->generator()->generate($this->createAllDayEventMultipleDaysWithTimezoneLink()) + $this->generator(['UID' => 'random-uid'])->generate($this->createShortEventLink()) ); } /** @test */ - public function it_correctly_generates_all_day_events_by_dates(): void + public function it_supports_custom_product_id(): void { $this->assertMatchesSnapshot( - $this->generator()->generate($this->createEventMultipleDaysViaStartEndWithTimezoneLink()) + $this->generator(['PRODID' => 'My Product'])->generate($this->createShortEventLink()) ); } diff --git a/tests/TestCase.php b/tests/TestCase.php index c33c6ce..602a41c 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,7 +3,6 @@ namespace Spatie\CalendarLinks\Tests; use DateTime; -use DateTimeImmutable; use DateTimeZone; use PHPUnit\Framework\TestCase as BaseTestCase; use Spatie\CalendarLinks\Link; @@ -13,89 +12,77 @@ abstract class TestCase extends BaseTestCase { use MatchesSnapshots; - protected function createShortEventLink(bool $immutable = false): Link + protected function createShortEventLink(): Link { $description = 'With balloons, clowns and stuff Bring a dog, bring a frog'; - $dateTimeClass = $immutable ? DateTimeImmutable::class : DateTime::class; - return Link::create( 'Birthday', - $dateTimeClass::createFromFormat('Y-m-d H:i', '2018-02-01 09:00', new DateTimeZone('UTC')), - $dateTimeClass::createFromFormat('Y-m-d H:i', '2018-02-01 18:00', new DateTimeZone('UTC')) + DateTime::class::createFromFormat('Y-m-d H:i', '2018-02-01 09:00', new DateTimeZone('UTC')), + DateTime::createFromFormat('Y-m-d H:i', '2018-02-01 18:00', new DateTimeZone('UTC')) )->description($description)->address('Party Lane 1A, 1337 Funtown'); } - protected function createMultipleDaysEventLink(bool $immutable = false): Link + protected function createMultipleDaysEventLink(): Link { $description = 'With balloons, clowns and stuff Bring a dog, bring a frog'; - $dateTimeClass = $immutable ? DateTimeImmutable::class : DateTime::class; - return Link::create( 'New Year', - $dateTimeClass::createFromFormat('Y-m-d H:i', '2019-12-31 23:00', new DateTimeZone('UTC')), - $dateTimeClass::createFromFormat('Y-m-d H:i', '2020-01-01 1:00', new DateTimeZone('UTC')) + DateTime::createFromFormat('Y-m-d H:i', '2019-12-31 23:00', new DateTimeZone('UTC')), + DateTime::createFromFormat('Y-m-d H:i', '2020-01-01 1:00', new DateTimeZone('UTC')) )->description($description)->address('Party Lane 1A, 1337 Funtown'); } - protected function createSingleDayAllDayEventLink(bool $immutable = false): Link + protected function createSingleDayAllDayEventLink(): Link { $description = 'With balloons, clowns and stuff Bring a dog, bring a frog'; - $dateTimeClass = $immutable ? DateTimeImmutable::class : DateTime::class; - return Link::createAllDay( 'Birthday', - $dateTimeClass::createFromFormat('Y-m-d', '2018-02-01', new DateTimeZone('UTC')) + DateTime::createFromFormat('Y-m-d', '2018-02-01', new DateTimeZone('UTC')) )->description($description)->address('Party Lane 1A, 1337 Funtown'); } - protected function createMultipleDaysAllDayEventLink(bool $immutable = false): Link + protected function createMultipleDaysAllDayEventLink(): Link { $description = 'With balloons, clowns and stuff Bring a dog, bring a frog'; - $dateTimeClass = $immutable ? DateTimeImmutable::class : DateTime::class; return Link::createAllDay( 'Birthday', - $dateTimeClass::createFromFormat('Y-m-d', '2018-02-01', new DateTimeZone('UTC')), + DateTime::createFromFormat('Y-m-d', '2018-02-01', new DateTimeZone('UTC')), 5 )->description($description)->address('Party Lane 1A, 1337 Funtown'); } - protected function createAllDayEventMultipleDaysWithTimezoneLink(bool $immutable = false): Link + protected function createAllDayEventMultipleDaysWithTimezoneLink(): Link { $description = 'Testing all day'; - $dateTimeClass = $immutable ? DateTimeImmutable::class : DateTime::class; - return Link::createAllDay( 'All day bugs', - $dateTimeClass::createFromFormat('Y-m-d', '2024-01-25', new DateTimeZone('Pacific/Wake'))->setTime(0, 0), + DateTime::createFromFormat('Y-m-d', '2024-01-25', new DateTimeZone('Pacific/Wake'))->setTime(0, 0), 5 )->description($description); } - protected function createEventMultipleDaysViaStartEndWithTimezoneLink(bool $immutable = false): Link + protected function createEventMultipleDaysViaStartEndWithTimezoneLink(): Link { $description = 'Testing all day'; - $dateTimeClass = $immutable ? DateTimeImmutable::class : DateTime::class; - - return Link::create( + return Link::createAllDay( 'All day bugs', - $dateTimeClass::createFromFormat('Y-m-d', '2024-01-25', new DateTimeZone('Pacific/Wake'))->setTime(0, 0), - $dateTimeClass::createFromFormat('Y-m-d', '2024-01-30', new DateTimeZone('Pacific/Wake'))->setTime(0, 0), - true, + \DateTime::createFromFormat('Y-m-d', '2024-01-25', new DateTimeZone('Pacific/Wake'))->setTime(0, 0), + 6, )->description($description); } - protected function createDescriptionIsHTMLcodeEventLink(bool $immutable = false): Link + protected function createDescriptionIsHtmlCodeEventLink(): Link { $description = 'With balloons, clowns and stuff Bring a dog, bring a frog. @@ -106,12 +93,10 @@ protected function createDescriptionIsHTMLcodeEventLink(bool $immutable = false) Thank you. '; - $dateTimeClass = $immutable ? DateTimeImmutable::class : DateTime::class; - return Link::create( 'Birthday Party +1', - $dateTimeClass::createFromFormat('Y-m-d H:i', '2018-02-01 09:00', new DateTimeZone('UTC')), - $dateTimeClass::createFromFormat('Y-m-d H:i', '2018-02-01 18:00', new DateTimeZone('UTC')) + DateTime::createFromFormat('Y-m-d H:i', '2018-02-01 09:00', new DateTimeZone('UTC')), + DateTime::createFromFormat('Y-m-d H:i', '2018-02-01 18:00', new DateTimeZone('UTC')) )->description($description)->address('Party Lane 1A, 1337 Funtown'); } } From 44fb0c0eb2587775c2e0a44628d4a9c25a5c1233 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Sun, 28 Jan 2024 12:46:00 +0530 Subject: [PATCH 10/17] Simplify type check --- src/Generators/Ics.php | 7 +++--- src/Link.php | 14 +++++------- tests/Generators/GeneratorTestContract.php | 25 ---------------------- tests/Generators/IcsGeneratorTest.php | 9 +++++--- 4 files changed, 15 insertions(+), 40 deletions(-) diff --git a/src/Generators/Ics.php b/src/Generators/Ics.php index dd325a0..5dbe572 100644 --- a/src/Generators/Ics.php +++ b/src/Generators/Ics.php @@ -8,6 +8,7 @@ /** * @see https://icalendar.org/RFC-Specifications/iCalendar-RFC-5545/ * @psalm-type IcsOptions = array{UID?: string, URL?: string, PRODID?: string, REMINDER?: array{DESCRIPTION?: string, TIME?: \DateTimeInterface}} + * @psalm-type IcsPresentationOptions = array{format?: self::FORMAT_*} */ class Ics implements Generator { @@ -19,15 +20,15 @@ class Ics implements Generator protected string $dateTimeFormat = 'Ymd\THis\Z'; - /** @var IcsOptions */ + /** @psalm-var IcsOptions */ protected array $options = []; - /** @var array{format?: self::FORMAT_*} */ + /** @psalm-var IcsPresentationOptions */ protected $presentationOptions = []; /** * @param IcsOptions $options Optional ICS properties and components - * @param array{format?: self::FORMAT_*} $presentationOptions + * @param IcsPresentationOptions $presentationOptions */ public function __construct(array $options = [], array $presentationOptions = []) { diff --git a/src/Link.php b/src/Link.php index 6143a3a..40c2d53 100644 --- a/src/Link.php +++ b/src/Link.php @@ -11,6 +11,7 @@ /** * @psalm-import-type IcsOptions from \Spatie\CalendarLinks\Generators\Ics + * @psalm-import-type IcsPresentationOptions from \Spatie\CalendarLinks\Generators\Ics */ class Link { @@ -56,17 +57,13 @@ public static function createAllDay(string $title, \DateTimeInterface $fromDate, // In cases where the from date is not UTC, make sure it's UTC, size all day events are floating and non UTC dates cause bugs in the generators if ($fromDate->getTimezone() !== new \DateTimeZone('UTC')) { $fromDate = \DateTimeImmutable::createFromFormat('Y-m-d', $fromDate->format('Y-m-d')); + assert($fromDate instanceof \DateTimeImmutable); } $from = \DateTimeImmutable::createFromInterface($fromDate)->modify('midnight'); - if (! $from instanceof \DateTimeImmutable) { - throw new InvalidLink('Could not modify $fromDate while building all day link.'); - } $to = $from->modify("+$numberOfDays days"); - if (! $to instanceof \DateTimeImmutable) { - throw new InvalidLink('Could not modify $fromDate while calculating $to date.'); - } + assert($to instanceof \DateTimeImmutable); return new static($title, $from, $to, true); } @@ -79,7 +76,7 @@ public function description(string $description): static return $this; } - /** Set address of the Event. */ + /** Set the address of the Event. */ public function address(string $address): static { $this->address = $address; @@ -99,8 +96,7 @@ public function google(): string /** * @psalm-param IcsOptions $options ICS specific properties and components - * @param array $options ICS specific properties and components - * @param array{format?: \Spatie\CalendarLinks\Generators\Ics::FORMAT_*} $presentationOptions + * @psalm-param IcsPresentationOptions $presentationOptions * @return string */ public function ics(array $options = [], array $presentationOptions = []): string diff --git a/tests/Generators/GeneratorTestContract.php b/tests/Generators/GeneratorTestContract.php index 94f29c4..c6a684d 100644 --- a/tests/Generators/GeneratorTestContract.php +++ b/tests/Generators/GeneratorTestContract.php @@ -17,11 +17,6 @@ public function it_can_generate_a_short_event_link(): void $this->assertMatchesSnapshot( $this->generator()->generate($this->createShortEventLink()) ); - - $this->assertSame( - $this->generator()->generate($this->createShortEventLink(false)), - $this->generator()->generate($this->createShortEventLink(true)) - ); } /** @test */ @@ -30,11 +25,6 @@ public function it_can_generate_a_single_day_allday_event_link(): void $this->assertMatchesSnapshot( $this->generator()->generate($this->createSingleDayAllDayEventLink()) ); - - $this->assertSame( - $this->generator()->generate($this->createSingleDayAllDayEventLink(false)), - $this->generator()->generate($this->createSingleDayAllDayEventLink(true)) - ); } /** @test */ @@ -43,11 +33,6 @@ public function it_can_generate_a_multiple_days_event_link(): void $this->assertMatchesSnapshot( $this->generator()->generate($this->createMultipleDaysEventLink()) ); - - $this->assertSame( - $this->generator()->generate($this->createMultipleDaysEventLink(false)), - $this->generator()->generate($this->createMultipleDaysEventLink(true)) - ); } /** @test */ @@ -56,11 +41,6 @@ public function it_can_generate_a_multiple_days_event_link_with_allday_flag(): v $this->assertMatchesSnapshot( $this->generator()->generate($this->createMultipleDaysAllDayEventLink()) ); - - $this->assertSame( - $this->generator()->generate($this->createMultipleDaysAllDayEventLink(false)), - $this->generator()->generate($this->createMultipleDaysAllDayEventLink(true)) - ); } /** @test */ @@ -69,10 +49,5 @@ public function it_can_generate_a_description_is_html_code_event_link_with_allda $this->assertMatchesSnapshot( $this->generator()->generate($this->createDescriptionIsHtmlCodeEventLink()) ); - - $this->assertSame( - $this->generator()->generate($this->createDescriptionIsHtmlCodeEventLink(false)), - $this->generator()->generate($this->createDescriptionIsHtmlCodeEventLink(true)) - ); } } diff --git a/tests/Generators/IcsGeneratorTest.php b/tests/Generators/IcsGeneratorTest.php index 53e44ee..2f2ee6f 100644 --- a/tests/Generators/IcsGeneratorTest.php +++ b/tests/Generators/IcsGeneratorTest.php @@ -8,15 +8,18 @@ use Spatie\CalendarLinks\Generators\Ics; use Spatie\CalendarLinks\Tests\TestCase; -/** @psalm-import-type IcsOptions from \Spatie\CalendarLinks\Generators\Ics */ +/** + * @psalm-import-type IcsOptions from \Spatie\CalendarLinks\Generators\Ics + * @psalm-import-type IcsPresentationOptions from \Spatie\CalendarLinks\Generators\Ics + */ final class IcsGeneratorTest extends TestCase { use GeneratorTestContract; /** * @psalm-param IcsOptions $options ICS specific properties and components - * @param array $options ICS specific properties and components - * @param array{format?: \Spatie\CalendarLinks\Generators\Ics::FORMAT_*} $presentationOptions + * @param IcsOptions $options ICS specific properties and components + * @param IcsPresentationOptions $presentationOptions * @return \Spatie\CalendarLinks\Generator */ protected function generator(array $options = [], array $presentationOptions = []): Generator From 1d7d300c0e38405aebb65299401570837638d9bb Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Sun, 28 Jan 2024 13:02:52 +0530 Subject: [PATCH 11/17] Privatize more methods --- src/Generators/BaseOutlook.php | 8 ++++---- src/Generators/Google.php | 12 ++++++++---- src/Generators/WebOffice.php | 6 +++--- src/Generators/WebOutlook.php | 6 +++--- src/Generators/Yahoo.php | 12 ++++++++---- 5 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/Generators/BaseOutlook.php b/src/Generators/BaseOutlook.php index 69f5f2f..48de855 100644 --- a/src/Generators/BaseOutlook.php +++ b/src/Generators/BaseOutlook.php @@ -12,23 +12,23 @@ abstract class BaseOutlook implements Generator { /** @see https://www.php.net/manual/en/function.date.php */ - protected string $dateFormat = 'Y-m-d'; + private const DATE_FORMAT = 'Y-m-d'; /** @see https://www.php.net/manual/en/function.date.php */ - protected string $dateTimeFormat = 'Y-m-d\TH:i:s\Z'; + private const DATETIME_FORMAT = 'Y-m-d\TH:i:s\Z'; /** * Get base URL for links. * @return non-empty-string */ - abstract public function baseUrl(): string; + abstract protected function baseUrl(): string; /** @inheritDoc */ public function generate(Link $link): string { $url = $this->baseUrl(); - $dateTimeFormat = $link->allDay ? $this->dateFormat : $this->dateTimeFormat; + $dateTimeFormat = $link->allDay ? self::DATE_FORMAT : self::DATETIME_FORMAT; $utcStartDateTime = $link->from->setTimezone(new DateTimeZone('UTC')); $utcEndDateTime = $link->to->setTimezone(new DateTimeZone('UTC')); diff --git a/src/Generators/Google.php b/src/Generators/Google.php index bd0ab1d..1d82c44 100644 --- a/src/Generators/Google.php +++ b/src/Generators/Google.php @@ -12,18 +12,22 @@ class Google implements Generator { /** @see https://www.php.net/manual/en/function.date.php */ - protected string $dateFormat = 'Ymd'; + private const DATE_FORMAT = 'Ymd'; - protected string $dateTimeFormat = 'Ymd\THis\Z'; + /** @see https://www.php.net/manual/en/function.date.php */ + private const DATETIME_FORMAT = 'Ymd\THis\Z'; + + /** @var non-empty-string */ + protected const BASE_URL = 'https://calendar.google.com/calendar/render?action=TEMPLATE'; /** @inheritDoc */ public function generate(Link $link): string { - $url = 'https://calendar.google.com/calendar/render?action=TEMPLATE'; + $url = self::BASE_URL; $utcStartDateTime = $link->from->setTimezone(new DateTimeZone('UTC')); $utcEndDateTime = $link->to->setTimezone(new DateTimeZone('UTC')); - $dateTimeFormat = $link->allDay ? $this->dateFormat : $this->dateTimeFormat; + $dateTimeFormat = $link->allDay ? self::DATE_FORMAT : self::DATETIME_FORMAT; $url .= '&dates='.$utcStartDateTime->format($dateTimeFormat).'/'.$utcEndDateTime->format($dateTimeFormat); // Add timezone name if it is specified in both from and to dates and is the same for both diff --git a/src/Generators/WebOffice.php b/src/Generators/WebOffice.php index 1462368..14f8783 100644 --- a/src/Generators/WebOffice.php +++ b/src/Generators/WebOffice.php @@ -2,13 +2,13 @@ namespace Spatie\CalendarLinks\Generators; -class WebOffice extends BaseOutlook +final class WebOffice extends BaseOutlook { /** @var non-empty-string */ - protected const BASE_URL = 'https://outlook.office.com/calendar/deeplink/compose?path=/calendar/action/compose&rru=addevent'; + private const BASE_URL = 'https://outlook.office.com/calendar/deeplink/compose?path=/calendar/action/compose&rru=addevent'; /** @inheritDoc */ - public function baseUrl(): string + protected function baseUrl(): string { return static::BASE_URL; } diff --git a/src/Generators/WebOutlook.php b/src/Generators/WebOutlook.php index 8316dae..c1ebb33 100644 --- a/src/Generators/WebOutlook.php +++ b/src/Generators/WebOutlook.php @@ -2,13 +2,13 @@ namespace Spatie\CalendarLinks\Generators; -class WebOutlook extends BaseOutlook +final class WebOutlook extends BaseOutlook { /** @var non-empty-string */ - protected const BASE_URL = 'https://outlook.live.com/calendar/action/compose?path=/calendar/action/compose&rru=addevent'; + private const BASE_URL = 'https://outlook.live.com/calendar/action/compose?path=/calendar/action/compose&rru=addevent'; /** @inheritDoc */ - public function baseUrl(): string + protected function baseUrl(): string { return static::BASE_URL; } diff --git a/src/Generators/Yahoo.php b/src/Generators/Yahoo.php index e11f041..8531c8c 100644 --- a/src/Generators/Yahoo.php +++ b/src/Generators/Yahoo.php @@ -12,16 +12,20 @@ class Yahoo implements Generator { /** @see https://www.php.net/manual/en/function.date.php */ - protected string $dateFormat = 'Ymd'; + private const DATE_FORMAT = 'Ymd'; - protected string $dateTimeFormat = 'Ymd\THis\Z'; + /** @see https://www.php.net/manual/en/function.date.php */ + private const DATETIME_FORMAT = 'Ymd\THis\Z'; + + /** @var non-empty-string */ + protected const BASE_URL = 'https://calendar.yahoo.com/?v=60&view=d&type=20'; /** @inheritDoc */ public function generate(Link $link): string { - $url = 'https://calendar.yahoo.com/?v=60&view=d&type=20'; + $url = self::BASE_URL; - $dateTimeFormat = $link->allDay ? $this->dateFormat : $this->dateTimeFormat; + $dateTimeFormat = $link->allDay ? self::DATE_FORMAT : self::DATETIME_FORMAT; if ($link->allDay) { $url .= '&ST='.$link->from->format($dateTimeFormat); From 3edd49532029606f847e033274e06cf7747b4fcb Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Sun, 28 Jan 2024 13:04:22 +0530 Subject: [PATCH 12/17] Add missing strict_types --- src/Generators/Google.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Generators/Google.php b/src/Generators/Google.php index bd0ab1d..7f716da 100644 --- a/src/Generators/Google.php +++ b/src/Generators/Google.php @@ -1,4 +1,4 @@ - Date: Sun, 28 Jan 2024 13:11:24 +0530 Subject: [PATCH 13/17] Fix links to docs --- src/Generators/BaseOutlook.php | 2 +- src/Generators/Google.php | 2 +- src/Generators/Yahoo.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Generators/BaseOutlook.php b/src/Generators/BaseOutlook.php index 48de855..d7a035e 100644 --- a/src/Generators/BaseOutlook.php +++ b/src/Generators/BaseOutlook.php @@ -7,7 +7,7 @@ use Spatie\CalendarLinks\Link; /** - * @see https://github.com/InteractionDesignFoundation/add-event-to-calendar-docs/blob/master/services/outlook-web.md + * @see https://github.com/InteractionDesignFoundation/add-event-to-calendar-docs/blob/main/services/outlook-web.md */ abstract class BaseOutlook implements Generator { diff --git a/src/Generators/Google.php b/src/Generators/Google.php index f79df36..c2c31af 100644 --- a/src/Generators/Google.php +++ b/src/Generators/Google.php @@ -7,7 +7,7 @@ use Spatie\CalendarLinks\Link; /** - * @see https://github.com/InteractionDesignFoundation/add-event-to-calendar-docs/blob/master/services/google.md + * @see https://github.com/InteractionDesignFoundation/add-event-to-calendar-docs/blob/main/services/google.md */ class Google implements Generator { diff --git a/src/Generators/Yahoo.php b/src/Generators/Yahoo.php index 8531c8c..697d4cd 100644 --- a/src/Generators/Yahoo.php +++ b/src/Generators/Yahoo.php @@ -7,7 +7,7 @@ use Spatie\CalendarLinks\Link; /** - * @see https://github.com/InteractionDesignFoundation/add-event-to-calendar-docs/blob/master/services/yahoo.md + * @see https://github.com/InteractionDesignFoundation/add-event-to-calendar-docs/blob/main/services/yahoo.md */ class Yahoo implements Generator { From 14bc6d5d432281f9b0097849999fd8197e99cd0e Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Tue, 20 Feb 2024 00:17:28 +0530 Subject: [PATCH 14/17] Use PSR12 --- .php-cs-fixer.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 5b9766d..276c398 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -6,13 +6,12 @@ __DIR__ . '/tests', ]) ->name('*.php') - ->notName('*.blade.php') ->ignoreDotFiles(true) ->ignoreVCS(true); return (new PhpCsFixer\Config()) ->setRules([ - '@PSR2' => true, + '@PSR12' => true, 'array_syntax' => ['syntax' => 'short'], 'ordered_imports' => ['sort_algorithm' => 'alpha'], 'no_unused_imports' => true, From 95245443de8be7915527f8adf80ca00395c1a6f3 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Tue, 20 Feb 2024 00:23:29 +0530 Subject: [PATCH 15/17] Move test cases to GeneratorTestContract --- tests/Generators/GeneratorTestContract.php | 24 +++++++++++++++++++ tests/Generators/GoogleGeneratorTest.php | 24 ------------------- ...tes_all_day_events_by_dates_diff_tz__1.txt | 11 +++++++++ ...y_generates_all_day_events_by_dates__1.txt | 1 + ...tes_all_day_events_by_dates_diff_tz__1.txt | 1 + ...ly_generates_all_day_events_by_days__1.txt | 1 + ...y_generates_all_day_events_by_dates__1.txt | 1 + ...tes_all_day_events_by_dates_diff_tz__1.txt | 1 + ...ly_generates_all_day_events_by_days__1.txt | 1 + ...y_generates_all_day_events_by_dates__1.txt | 1 + ...tes_all_day_events_by_dates_diff_tz__1.txt | 1 + ...ly_generates_all_day_events_by_days__1.txt | 1 + 12 files changed, 44 insertions(+), 24 deletions(-) create mode 100644 tests/Generators/__snapshots__/IcsGeneratorTest__it_correctly_generates_all_day_events_by_dates_diff_tz__1.txt create mode 100644 tests/Generators/__snapshots__/WebOfficeGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt create mode 100644 tests/Generators/__snapshots__/WebOfficeGeneratorTest__it_correctly_generates_all_day_events_by_dates_diff_tz__1.txt create mode 100644 tests/Generators/__snapshots__/WebOfficeGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt create mode 100644 tests/Generators/__snapshots__/WebOutlookGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt create mode 100644 tests/Generators/__snapshots__/WebOutlookGeneratorTest__it_correctly_generates_all_day_events_by_dates_diff_tz__1.txt create mode 100644 tests/Generators/__snapshots__/WebOutlookGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt create mode 100644 tests/Generators/__snapshots__/YahooGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt create mode 100644 tests/Generators/__snapshots__/YahooGeneratorTest__it_correctly_generates_all_day_events_by_dates_diff_tz__1.txt create mode 100644 tests/Generators/__snapshots__/YahooGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt diff --git a/tests/Generators/GeneratorTestContract.php b/tests/Generators/GeneratorTestContract.php index c6a684d..f0a252e 100644 --- a/tests/Generators/GeneratorTestContract.php +++ b/tests/Generators/GeneratorTestContract.php @@ -50,4 +50,28 @@ public function it_can_generate_a_description_is_html_code_event_link_with_allda $this->generator()->generate($this->createDescriptionIsHtmlCodeEventLink()) ); } + + /** @test */ + public function it_correctly_generates_all_day_events_by_days(): void + { + $this->assertMatchesSnapshot( + $this->generator()->generate($this->createAllDayEventMultipleDaysWithTimezoneLink()) + ); + } + + /** @test */ + public function it_correctly_generates_all_day_events_by_dates(): void + { + $this->assertMatchesSnapshot( + $this->generator()->generate($this->createEventMultipleDaysViaStartEndWithTimezoneLink()) + ); + } + + /** @test */ + public function it_correctly_generates_all_day_events_by_dates_diff_tz(): void + { + $this->assertMatchesSnapshot( + $this->generator()->generate($this->createEventMultipleDaysViaStartEndWithDiffTimezoneLink()) + ); + } } diff --git a/tests/Generators/GoogleGeneratorTest.php b/tests/Generators/GoogleGeneratorTest.php index cf2b1a7..f564a5f 100644 --- a/tests/Generators/GoogleGeneratorTest.php +++ b/tests/Generators/GoogleGeneratorTest.php @@ -20,30 +20,6 @@ protected function linkMethodName(): string return 'google'; } - /** @test */ - public function it_correctly_generates_all_day_events_by_days(): void - { - $this->assertMatchesSnapshot( - $this->generator()->generate($this->createAllDayEventMultipleDaysWithTimezoneLink()) - ); - } - - /** @test */ - public function it_correctly_generates_all_day_events_by_dates(): void - { - $this->assertMatchesSnapshot( - $this->generator()->generate($this->createEventMultipleDaysViaStartEndWithTimezoneLink()) - ); - } - - /** @test */ - public function it_correctly_generates_all_day_events_by_dates_diff_tz(): void - { - $this->assertMatchesSnapshot( - $this->generator()->generate($this->createEventMultipleDaysViaStartEndWithDiffTimezoneLink()) - ); - } - /** @test */ public function it_can_generate_an_url_with_custom_parameters(): void { diff --git a/tests/Generators/__snapshots__/IcsGeneratorTest__it_correctly_generates_all_day_events_by_dates_diff_tz__1.txt b/tests/Generators/__snapshots__/IcsGeneratorTest__it_correctly_generates_all_day_events_by_dates_diff_tz__1.txt new file mode 100644 index 0000000..983dc31 --- /dev/null +++ b/tests/Generators/__snapshots__/IcsGeneratorTest__it_correctly_generates_all_day_events_by_dates_diff_tz__1.txt @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:Spatie calendar-links +BEGIN:VEVENT +UID:cb8469bae9cf10a9ee8f1179c6a932f5 +SUMMARY:All day bugs +DTSTAMP:20240125 +DTSTART:20240125 +DURATION:P6D +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/tests/Generators/__snapshots__/WebOfficeGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt b/tests/Generators/__snapshots__/WebOfficeGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt new file mode 100644 index 0000000..fb16376 --- /dev/null +++ b/tests/Generators/__snapshots__/WebOfficeGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt @@ -0,0 +1 @@ +https://outlook.office.com/calendar/deeplink/compose?path=/calendar/action/compose&rru=addevent&startdt=2024-01-25&enddt=2024-01-31&allday=true&subject=All%20day%20bugs&body=Testing%20all%20day \ No newline at end of file diff --git a/tests/Generators/__snapshots__/WebOfficeGeneratorTest__it_correctly_generates_all_day_events_by_dates_diff_tz__1.txt b/tests/Generators/__snapshots__/WebOfficeGeneratorTest__it_correctly_generates_all_day_events_by_dates_diff_tz__1.txt new file mode 100644 index 0000000..5c6dc80 --- /dev/null +++ b/tests/Generators/__snapshots__/WebOfficeGeneratorTest__it_correctly_generates_all_day_events_by_dates_diff_tz__1.txt @@ -0,0 +1 @@ +https://outlook.office.com/calendar/deeplink/compose?path=/calendar/action/compose&rru=addevent&startdt=2024-01-25&enddt=2024-01-31&allday=true&subject=All%20day%20bugs \ No newline at end of file diff --git a/tests/Generators/__snapshots__/WebOfficeGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt b/tests/Generators/__snapshots__/WebOfficeGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt new file mode 100644 index 0000000..a7b3a6e --- /dev/null +++ b/tests/Generators/__snapshots__/WebOfficeGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt @@ -0,0 +1 @@ +https://outlook.office.com/calendar/deeplink/compose?path=/calendar/action/compose&rru=addevent&startdt=2024-01-25&enddt=2024-01-30&allday=true&subject=All%20day%20bugs&body=Testing%20all%20day \ No newline at end of file diff --git a/tests/Generators/__snapshots__/WebOutlookGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt b/tests/Generators/__snapshots__/WebOutlookGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt new file mode 100644 index 0000000..b1cbca2 --- /dev/null +++ b/tests/Generators/__snapshots__/WebOutlookGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt @@ -0,0 +1 @@ +https://outlook.live.com/calendar/action/compose?path=/calendar/action/compose&rru=addevent&startdt=2024-01-25&enddt=2024-01-31&allday=true&subject=All%20day%20bugs&body=Testing%20all%20day \ No newline at end of file diff --git a/tests/Generators/__snapshots__/WebOutlookGeneratorTest__it_correctly_generates_all_day_events_by_dates_diff_tz__1.txt b/tests/Generators/__snapshots__/WebOutlookGeneratorTest__it_correctly_generates_all_day_events_by_dates_diff_tz__1.txt new file mode 100644 index 0000000..c2e2621 --- /dev/null +++ b/tests/Generators/__snapshots__/WebOutlookGeneratorTest__it_correctly_generates_all_day_events_by_dates_diff_tz__1.txt @@ -0,0 +1 @@ +https://outlook.live.com/calendar/action/compose?path=/calendar/action/compose&rru=addevent&startdt=2024-01-25&enddt=2024-01-31&allday=true&subject=All%20day%20bugs \ No newline at end of file diff --git a/tests/Generators/__snapshots__/WebOutlookGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt b/tests/Generators/__snapshots__/WebOutlookGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt new file mode 100644 index 0000000..8234d67 --- /dev/null +++ b/tests/Generators/__snapshots__/WebOutlookGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt @@ -0,0 +1 @@ +https://outlook.live.com/calendar/action/compose?path=/calendar/action/compose&rru=addevent&startdt=2024-01-25&enddt=2024-01-30&allday=true&subject=All%20day%20bugs&body=Testing%20all%20day \ No newline at end of file diff --git a/tests/Generators/__snapshots__/YahooGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt b/tests/Generators/__snapshots__/YahooGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt new file mode 100644 index 0000000..acab65c --- /dev/null +++ b/tests/Generators/__snapshots__/YahooGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt @@ -0,0 +1 @@ +https://calendar.yahoo.com/?v=60&view=d&type=20&ST=20240125&DUR=allday&ET=20240131&TITLE=All%20day%20bugs&DESC=Testing%20all%20day \ No newline at end of file diff --git a/tests/Generators/__snapshots__/YahooGeneratorTest__it_correctly_generates_all_day_events_by_dates_diff_tz__1.txt b/tests/Generators/__snapshots__/YahooGeneratorTest__it_correctly_generates_all_day_events_by_dates_diff_tz__1.txt new file mode 100644 index 0000000..212ac82 --- /dev/null +++ b/tests/Generators/__snapshots__/YahooGeneratorTest__it_correctly_generates_all_day_events_by_dates_diff_tz__1.txt @@ -0,0 +1 @@ +https://calendar.yahoo.com/?v=60&view=d&type=20&ST=20240125&DUR=allday&ET=20240131&TITLE=All%20day%20bugs \ No newline at end of file diff --git a/tests/Generators/__snapshots__/YahooGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt b/tests/Generators/__snapshots__/YahooGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt new file mode 100644 index 0000000..f798add --- /dev/null +++ b/tests/Generators/__snapshots__/YahooGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt @@ -0,0 +1 @@ +https://calendar.yahoo.com/?v=60&view=d&type=20&ST=20240125&DUR=allday&ET=20240130&TITLE=All%20day%20bugs&DESC=Testing%20all%20day \ No newline at end of file From 72b8b855cc39a8830ba164c5e49a06f36b804993 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Tue, 20 Feb 2024 00:24:16 +0530 Subject: [PATCH 16/17] Apply coding style fixes --- src/Exceptions/InvalidLink.php | 4 +++- src/Generator.php | 4 +++- src/Generators/BaseOutlook.php | 4 +++- src/Generators/Google.php | 4 +++- src/Generators/Ics.php | 4 +++- src/Generators/WebOffice.php | 4 +++- src/Generators/WebOutlook.php | 4 +++- src/Generators/Yahoo.php | 4 +++- src/Link.php | 6 +++++- tests/Generators/GeneratorTestContract.php | 4 +++- tests/Generators/GoogleGeneratorTest.php | 4 +++- tests/Generators/IcsGeneratorTest.php | 4 +++- tests/Generators/WebOfficeGeneratorTest.php | 4 +++- tests/Generators/WebOutlookGeneratorTest.php | 4 +++- tests/Generators/YahooGeneratorTest.php | 4 +++- tests/LinkTest.php | 4 +++- tests/TestCase.php | 4 +++- 17 files changed, 53 insertions(+), 17 deletions(-) diff --git a/src/Exceptions/InvalidLink.php b/src/Exceptions/InvalidLink.php index caff971..0fe01de 100644 --- a/src/Exceptions/InvalidLink.php +++ b/src/Exceptions/InvalidLink.php @@ -1,4 +1,6 @@ -description = $description; + return $this; } @@ -80,6 +83,7 @@ public function description(string $description): static public function address(string $address): static { $this->address = $address; + return $this; } diff --git a/tests/Generators/GeneratorTestContract.php b/tests/Generators/GeneratorTestContract.php index f0a252e..4b8eb70 100644 --- a/tests/Generators/GeneratorTestContract.php +++ b/tests/Generators/GeneratorTestContract.php @@ -1,4 +1,6 @@ - Date: Tue, 20 Feb 2024 00:38:34 +0530 Subject: [PATCH 17/17] Suppress Psalm's false positives --- psalm-baseline.xml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index c537d28..b629420 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,2 +1,9 @@ - + + + + + + + +