From a97cb341e5eabe2c4711c381d21b8d4d5c450154 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 1 Jun 2026 10:17:41 +0930 Subject: [PATCH 01/23] chore: support PHP 8.2 and up --- .github/workflows/ci.yml | 11 +- .gitignore | 1 + composer.json | 28 +- lib/BirthdayCalendarGenerator.php | 2 +- lib/Cli.php | 74 +--- lib/Component.php | 12 +- lib/Component/VAlarm.php | 2 +- lib/Component/VAvailability.php | 2 +- lib/Component/VCalendar.php | 2 +- lib/Component/VCard.php | 6 +- lib/Component/VEvent.php | 2 +- lib/Component/VFreeBusy.php | 2 +- lib/DateTimeParser.php | 4 +- lib/Document.php | 4 +- lib/FreeBusyData.php | 20 +- lib/FreeBusyGenerator.php | 18 +- lib/ITip/Broker.php | 12 +- lib/ITip/Message.php | 10 +- lib/Node.php | 4 +- lib/PHPUnitAssertions.php | 4 +- lib/Parameter.php | 87 +---- lib/Parser/Json.php | 33 +- lib/Parser/MimeDir.php | 33 +- lib/Parser/Parser.php | 8 +- lib/Parser/XML.php | 8 +- lib/Property.php | 45 +-- lib/Property/Binary.php | 6 +- lib/Property/Boolean.php | 4 +- lib/Property/FloatValue.php | 6 +- lib/Property/ICalendar/CalAddress.php | 4 +- lib/Property/ICalendar/DateTime.php | 10 +- lib/Property/ICalendar/Period.php | 6 +- lib/Property/ICalendar/Recur.php | 24 +- lib/Property/IntegerValue.php | 2 +- lib/Property/Text.php | 8 +- lib/Property/Time.php | 6 +- lib/Property/Uri.php | 12 +- lib/Property/UtcOffset.php | 10 +- lib/Property/VCard/DateAndOrTime.php | 10 +- lib/Recur/RDateIterator.php | 15 +- lib/Recur/RRuleIterator.php | 31 +- lib/Splitter/VCard.php | 17 +- .../FindFromTimezoneIdentifier.php | 2 +- lib/TimezoneGuesser/FindFromTimezoneMap.php | 4 +- lib/TimezoneGuesser/GuessFromLicEntry.php | 2 +- lib/TimezoneGuesser/GuessFromMsTzId.php | 2 +- lib/VCardConverter.php | 24 +- rector.php | 16 + tests/VObject/CliTest.php | 90 ++--- tests/VObject/Component/AvailableTest.php | 44 +-- tests/VObject/Component/VAvailabilityTest.php | 366 +++++++++--------- tests/VObject/ITip/BrokerTester.php | 2 +- ...ezoneInParseEventInfoWithoutMasterTest.php | 1 - tests/VObject/Property/TextTest.php | 16 +- tests/VObject/ReaderTest.php | 4 +- .../EventIterator/HandleRDateExpandTest.php | 2 +- tests/VObject/Splitter/ICalendarTest.php | 2 +- tests/VObject/Splitter/VCardTest.php | 2 +- tests/VObject/TimeZoneUtilTest.php | 14 +- tests/VObject/VCardConverterTest.php | 264 ++++++------- tests/phpunit.xml | 24 +- 61 files changed, 664 insertions(+), 822 deletions(-) create mode 100644 rector.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 08c0b0a2d..2a7e69fb7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,19 +12,22 @@ jobs: strategy: fail-fast: false matrix: - php-versions: ['8.0', '8.1', '8.2', '8.3', '8.4'] + php-versions: ['8.3', '8.4'] coverage: ['pcov'] code-style: ['no'] code-analysis: ['no'] + rector-check: ['no'] include: - - php-versions: '7.4' + - php-versions: '8.2' coverage: 'pcov' code-style: 'yes' code-analysis: 'yes' + rector-check: 'no' - php-versions: '8.5' coverage: 'pcov' code-style: 'no' code-analysis: 'yes' + rector-check: 'yes' steps: - name: Checkout uses: actions/checkout@v6 @@ -61,6 +64,10 @@ jobs: if: matrix.code-analysis == 'yes' run: composer phpstan + - name: Code Refactoring (rector) + if: matrix.rector-check == 'yes' + run: composer rector-check + - name: Test with phpunit run: vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-clover clover.xml diff --git a/.gitignore b/.gitignore index d0e5169ac..f38607123 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ vendor/ composer.lock tests/cov/ tests/temp +tests/.phpunit.cache tests/.phpunit.result.cache # Development stuff diff --git a/composer.json b/composer.json index 561906176..8ae9f914a 100644 --- a/composer.json +++ b/composer.json @@ -32,16 +32,20 @@ "homepage" : "http://sabre.io/vobject/", "license" : "BSD-3-Clause", "require" : { - "php" : "^7.4 || ^8.0", + "php" : "^8.2", "ext-mbstring" : "*", "ext-json" : "*", "sabre/xml" : "^3.0 || ^4.0" }, "require-dev" : { - "friendsofphp/php-cs-fixer": "^3.94", - "phpunit/phpunit" : "^9.6", - "phpunit/php-invoker" : "^2.0 || ^3.1", - "phpstan/phpstan": "^2.1" + "friendsofphp/php-cs-fixer": "^3.95", + "phpstan/phpstan": "^2.2", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpstan/extension-installer": "^1.4", + "phpunit/phpunit": "^11.5", + "phpunit/php-invoker" : "^5.0", + "rector/rector": "^2.4" }, "suggest" : { "hoa/bench" : "If you would like to run the benchmark scripts" @@ -96,6 +100,12 @@ "cs-fixer": [ "PHP_CS_FIXER_IGNORE_ENV=true php-cs-fixer fix" ], + "rector-check": [ + "rector process --dry-run" + ], + "rector-fix": [ + "rector process" + ], "phpunit": [ "phpunit --configuration tests/phpunit.xml" ], @@ -104,5 +114,13 @@ "composer cs-fixer", "composer phpunit" ] + }, + "config": { + "allow-plugins": { + "phpstan/extension-installer": true + }, + "platform": { + "php": "8.2" + } } } diff --git a/lib/BirthdayCalendarGenerator.php b/lib/BirthdayCalendarGenerator.php index 49793b0d4..6fb21f4f2 100644 --- a/lib/BirthdayCalendarGenerator.php +++ b/lib/BirthdayCalendarGenerator.php @@ -116,7 +116,7 @@ public function getResult(): VCalendar // Skip if we can't parse the BDAY value. try { $dateParts = DateTimeParser::parseVCardDateTime($object->BDAY->getValue()); - } catch (InvalidDataException $e) { + } catch (InvalidDataException) { continue; } diff --git a/lib/Cli.php b/lib/Cli.php index 69af6f470..38db47334 100644 --- a/lib/Cli.php +++ b/lib/Cli.php @@ -92,7 +92,7 @@ public function main(array $argv): int // @codeCoverageIgnoreEnd try { - list($options, $positional) = $this->parseArguments($argv); + [$options, $positional] = $this->parseArguments($argv); if (isset($options['q'])) { $this->quiet = true; @@ -110,27 +110,10 @@ public function main(array $argv): int return 0; case 'format': - switch ($value) { - // jcard/jcal documents - case 'jcard': - case 'jcal': - // specific document versions - case 'vcard21': - case 'vcard30': - case 'vcard40': - case 'icalendar20': - // specific formats - case 'json': - case 'mimedir': - // icalendar/vcard - case 'icalendar': - case 'vcard': - $this->format = $value; - break; - - default: - throw new \InvalidArgumentException('Unknown format: '.$value); - } + $this->format = match ($value) { + 'jcard', 'jcal', 'vcard21', 'vcard30', 'vcard40', 'icalendar20', 'json', 'mimedir', 'icalendar', 'vcard' => $value, + default => throw new \InvalidArgumentException('Unknown format: '.$value), + }; break; case 'pretty': $this->pretty = true; @@ -139,28 +122,11 @@ public function main(array $argv): int $this->forgiving = true; break; case 'inputformat': - switch ($value) { - // json formats - case 'jcard': - case 'jcal': - case 'json': - $this->inputFormat = 'json'; - break; - - // mimedir formats - case 'mimedir': - case 'icalendar': - case 'vcard': - case 'vcard21': - case 'vcard30': - case 'vcard40': - case 'icalendar20': - $this->inputFormat = 'mimedir'; - break; - - default: - throw new \InvalidArgumentException('Unknown format: '.$value); - } + $this->inputFormat = match ($value) { + 'jcard', 'jcal', 'json' => 'json', + 'mimedir', 'icalendar', 'vcard', 'vcard21', 'vcard30', 'vcard40', 'icalendar20' => 'mimedir', + default => throw new \InvalidArgumentException('Unknown format: '.$value), + }; break; default: throw new \InvalidArgumentException('Unknown option: '.$name); @@ -201,14 +167,14 @@ public function main(array $argv): int } if (null === $this->inputFormat) { - if ('.json' === substr($this->inputPath, -5)) { + if (str_ends_with($this->inputPath, '.json')) { $this->inputFormat = 'json'; } else { $this->inputFormat = 'mimedir'; } } if (null === $this->format) { - if ('.json' === substr($this->outputPath, -5)) { + if (str_ends_with($this->outputPath, '.json')) { $this->format = 'json'; } else { $this->format = 'mimedir'; @@ -224,7 +190,7 @@ public function main(array $argv): int $realCode = $returnCode; } } - } catch (EofException $e) { + } catch (EofException) { // end of file } catch (\Exception $e) { $this->log('Error: '.$e->getMessage(), 'red'); @@ -408,7 +374,7 @@ protected function convert(Component $vObj): int } fwrite($this->stdout, json_encode($vObj->jsonSerialize(), $jsonOptions)); } else { - fwrite($this->stdout, $vObj->serialize()); + fwrite($this->stdout, (string) $vObj->serialize()); } return 0; @@ -538,7 +504,7 @@ protected function serializeProperty(Property $property): void $this->cWrite('red', ':'); if ($property instanceof Property\Binary) { - $this->cWrite('default', 'embedded binary stripped. ('.strlen($property->getValue()).' bytes)'); + $this->cWrite('default', 'embedded binary stripped. ('.strlen((string) $property->getValue()).' bytes)'); } else { $parts = $property->getParts(); $first1 = true; @@ -593,17 +559,17 @@ protected function parseArguments(array $argv): array $v = $argv[$ii]; - if ('--' === substr($v, 0, 2)) { + if (str_starts_with((string) $v, '--')) { // This is a long-form option. - $optionName = substr($v, 2); + $optionName = substr((string) $v, 2); $optionValue = true; if (strpos($optionName, '=')) { - list($optionName, $optionValue) = explode('=', $optionName); + [$optionName, $optionValue] = explode('=', $optionName); } $options[$optionName] = $optionValue; - } elseif ('-' === substr($v, 0, 1) && strlen($v) > 1) { + } elseif (str_starts_with((string) $v, '-') && strlen((string) $v) > 1) { // This is a short-form option. - foreach (str_split(substr($v, 1)) as $option) { + foreach (str_split(substr((string) $v, 1)) as $option) { $options[$option] = true; } } else { diff --git a/lib/Component.php b/lib/Component.php index 2a08a082e..b1a05d9aa 100644 --- a/lib/Component.php +++ b/lib/Component.php @@ -140,7 +140,7 @@ public function remove($item): void // If there's no dot in the name, it's an exact property name, // we can just wipe out all those properties. // - if (false === strpos($item, '.')) { + if (!str_contains($item, '.')) { unset($this->children[strtoupper($item)]); return; @@ -210,8 +210,8 @@ public function select(string $name): array { $group = null; $name = strtoupper($name); - if (false !== strpos($name, '.')) { - list($group, $name) = explode('.', $name, 2); + if (str_contains($name, '.')) { + [$group, $name] = explode('.', $name, 2); } if ('' === $name) { $name = null; @@ -228,9 +228,7 @@ public function select(string $name): array // more. return array_filter( $result, - function ($child) use ($group) { - return $child instanceof Property && (null !== $child->group ? strtoupper($child->group) : '') === $group; - } + fn ($child) => $child instanceof Property && (null !== $child->group ? strtoupper($child->group) : '') === $group ); } @@ -541,7 +539,7 @@ public function validate(int $options = 0): array $messages = []; foreach ($this->children() as $child) { - $name = strtoupper($child->name); + $name = strtoupper((string) $child->name); if (!isset($propertyCounters[$name])) { $propertyCounters[$name] = 1; } else { diff --git a/lib/Component/VAlarm.php b/lib/Component/VAlarm.php index 0cb6ab1de..0e7f05ce0 100644 --- a/lib/Component/VAlarm.php +++ b/lib/Component/VAlarm.php @@ -32,7 +32,7 @@ class VAlarm extends VObject\Component public function getEffectiveTriggerTime(): \DateTimeImmutable { $trigger = $this->TRIGGER; - if (!isset($trigger['VALUE']) || ($trigger['VALUE'] && 'DURATION' === strtoupper($trigger['VALUE']))) { + if (!isset($trigger['VALUE']) || ($trigger['VALUE'] && 'DURATION' === strtoupper((string) $trigger['VALUE']))) { $triggerDuration = VObject\DateTimeParser::parseDuration($this->TRIGGER); $related = (isset($trigger['RELATED']) && 'END' == strtoupper($trigger['RELATED'])) ? 'END' : 'START'; diff --git a/lib/Component/VAvailability.php b/lib/Component/VAvailability.php index ff6e9bd65..a4db47642 100644 --- a/lib/Component/VAvailability.php +++ b/lib/Component/VAvailability.php @@ -33,7 +33,7 @@ class VAvailability extends VObject\Component */ public function isInTimeRange(\DateTimeInterface $start, \DateTimeInterface $end): bool { - list($effectiveStart, $effectiveEnd) = $this->getEffectiveStartEnd(); + [$effectiveStart, $effectiveEnd] = $this->getEffectiveStartEnd(); return (is_null($effectiveStart) || $start < $effectiveEnd) diff --git a/lib/Component/VCalendar.php b/lib/Component/VCalendar.php index 5163c042a..a4dff82c9 100644 --- a/lib/Component/VCalendar.php +++ b/lib/Component/VCalendar.php @@ -327,7 +327,7 @@ public function expand(\DateTimeInterface $start, \DateTimeInterface $end, ?\Dat foreach ($recurringEvents as $events) { try { $it = new EventIterator($events, null, $timeZone); - } catch (NoInstancesException $e) { + } catch (NoInstancesException) { // This event is recurring, but it doesn't have a single // instance. We are skipping this event from the output // entirely. diff --git a/lib/Component/VCard.php b/lib/Component/VCard.php index 0dafb674b..1fe654712 100644 --- a/lib/Component/VCard.php +++ b/lib/Component/VCard.php @@ -423,10 +423,10 @@ public function getByType(string $propertyName, string $type) */ public function getByTypes(string $propertyName, array $types) { - $types = array_map('strtolower', $types); + $types = array_map(strtolower(...), $types); foreach ($this->select($propertyName) as $field) { if (isset($field['TYPE'])) { - $parts = array_map('strtolower', $field['TYPE']->getParts()); + $parts = array_map(strtolower(...), $field['TYPE']->getParts()); if (!array_diff($types, $parts) && !array_diff($parts, $types)) { return $field; @@ -493,7 +493,7 @@ public function xmlSerialize(Xml\Writer $writer): void foreach ($propertiesByGroup as $group => $properties) { if (!empty($group)) { $writer->startElement('group'); - $writer->writeAttribute('name', strtolower($group)); + $writer->writeAttribute('name', strtolower((string) $group)); } foreach ($properties as $property) { diff --git a/lib/Component/VEvent.php b/lib/Component/VEvent.php index 6ca196d66..c13081884 100644 --- a/lib/Component/VEvent.php +++ b/lib/Component/VEvent.php @@ -44,7 +44,7 @@ public function isInTimeRange(\DateTimeInterface $start, \DateTimeInterface $end if ($this->RRULE || $this->RDATE) { try { $it = new EventIterator($this, null, $start->getTimezone()); - } catch (NoInstancesException $e) { + } catch (NoInstancesException) { // If we've caught this exception, there are no instances // for the event that fall into the specified time-range. return false; diff --git a/lib/Component/VFreeBusy.php b/lib/Component/VFreeBusy.php index a9891c92c..623652ff9 100644 --- a/lib/Component/VFreeBusy.php +++ b/lib/Component/VFreeBusy.php @@ -41,7 +41,7 @@ public function isFree(\DateTimeInterface $start, \DatetimeInterface $end): bool // Every period is formatted as [start]/[end]. The start is an // absolute UTC time, the end may be an absolute UTC time, or // duration (relative) value. - list($busyStart, $busyEnd) = explode('/', $period); + [$busyStart, $busyEnd] = explode('/', $period); $busyStart = VObject\DateTimeParser::parse($busyStart); $busyEnd = VObject\DateTimeParser::parse($busyEnd); diff --git a/lib/DateTimeParser.php b/lib/DateTimeParser.php index 712a40faf..44e27c6fa 100644 --- a/lib/DateTimeParser.php +++ b/lib/DateTimeParser.php @@ -42,7 +42,7 @@ public static function parseDateTime(string $dt, ?\DateTimeZone $tz = null): \Da try { $date = new \DateTimeImmutable($matches[1].'-'.$matches[2].'-'.$matches[3].' '.$matches[4].':'.$matches[5].':'.$matches[6], $tz); - } catch (\Exception $e) { + } catch (\Exception) { throw new InvalidDataException('The supplied iCalendar datetime value is incorrect: '.$dt); } @@ -69,7 +69,7 @@ public static function parseDate(string $date, ?\DateTimeZone $tz = null): \Date try { $date = new \DateTimeImmutable($matches[1].'-'.$matches[2].'-'.$matches[3], $tz); - } catch (\Exception $e) { + } catch (\Exception) { throw new InvalidDataException('The supplied iCalendar date value is incorrect: '.$date); } diff --git a/lib/Document.php b/lib/Document.php index aa3a404a7..c8fd99054 100644 --- a/lib/Document.php +++ b/lib/Document.php @@ -118,10 +118,10 @@ public function getDocumentType(): int public function create(string $name) { if (isset(static::$componentMap[strtoupper($name)])) { - return call_user_func_array([$this, 'createComponent'], func_get_args()); + return call_user_func_array($this->createComponent(...), func_get_args()); } - return call_user_func_array([$this, 'createProperty'], func_get_args()); + return call_user_func_array($this->createProperty(...), func_get_args()); } /** diff --git a/lib/FreeBusyData.php b/lib/FreeBusyData.php index 405a5886d..6cdf2b937 100644 --- a/lib/FreeBusyData.php +++ b/lib/FreeBusyData.php @@ -11,25 +11,19 @@ */ class FreeBusyData { - /** - * Start timestamp. - */ - protected int $start; - - /** - * End timestamp. - */ - protected int $end; - /** * A list of free-busy times. */ protected array $data; - public function __construct(int $start, int $end) + public function __construct(/** + * Start timestamp. + */ + protected int $start, /** + * End timestamp. + */ + protected int $end) { - $this->start = $start; - $this->end = $end; $this->data = []; $this->data[] = [ diff --git a/lib/FreeBusyGenerator.php b/lib/FreeBusyGenerator.php index 424826034..3bcc842e3 100644 --- a/lib/FreeBusyGenerator.php +++ b/lib/FreeBusyGenerator.php @@ -217,7 +217,7 @@ function ($a, $b) { $new = []; foreach ($old as $vavail) { - list($compStart, $compEnd) = $vavail->getEffectiveStartEnd(); + [$compStart, $compEnd] = $vavail->getEffectiveStartEnd(); // We don't care about date-times that are earlier or later than the // start and end of the freebusy report, so this gets normalized @@ -237,7 +237,7 @@ function ($a, $b) { // Going through our existing list of components to see if there's // a higher priority component that already fully covers this one. foreach ($new as $higherVavail) { - list($higherStart, $higherEnd) = $higherVavail->getEffectiveStartEnd(); + [$higherStart, $higherEnd] = $higherVavail->getEffectiveStartEnd(); if ( (is_null($higherStart) || $higherStart < $compStart) && (is_null($higherEnd) || $higherEnd > $compEnd) @@ -259,7 +259,7 @@ function ($a, $b) { // priority components to override the lower ones. foreach (array_reverse($new) as $vavail) { $busyType = isset($vavail->BUSYTYPE) ? strtoupper($vavail->BUSYTYPE) : 'BUSY-UNAVAILABLE'; - list($vavailStart, $vavailEnd) = $vavail->getEffectiveStartEnd(); + [$vavailStart, $vavailEnd] = $vavail->getEffectiveStartEnd(); // Making the component size no larger than the requested free-busy // report range. @@ -281,7 +281,7 @@ function ($a, $b) { // Looping over the AVAILABLE components. if (isset($vavail->AVAILABLE)) { foreach ($vavail->AVAILABLE as $available) { - list($availStart, $availEnd) = $available->getEffectiveStartEnd(); + [$availStart, $availEnd] = $available->getEffectiveStartEnd(); $fbData->add( $availStart->getTimeStamp(), $availEnd->getTimeStamp(), @@ -362,7 +362,7 @@ protected function calculateBusy(FreeBusyData $fbData, array $objects): void if ($component->RRULE) { try { $iterator = new EventIterator($object, (string) $component->UID, $this->timeZone); - } catch (NoInstancesException $e) { + } catch (NoInstancesException) { // This event is recurring, but it doesn't have a single // instance. We are skipping this event from the output // entirely. @@ -437,12 +437,12 @@ protected function calculateBusy(FreeBusyData $fbData, array $objects): void continue; } - $values = explode(',', $freebusy); + $values = explode(',', (string) $freebusy); foreach ($values as $value) { - list($startTime, $endTime) = explode('/', $value); + [$startTime, $endTime] = explode('/', $value); $startTime = DateTimeParser::parseDateTime($startTime); - if ('P' === substr($endTime, 0, 1) || '-P' === substr($endTime, 0, 2)) { + if (str_starts_with($endTime, 'P') || str_starts_with($endTime, '-P')) { $duration = DateTimeParser::parseDuration($endTime); $endTime = clone $startTime; $endTime = $endTime->add($duration); @@ -507,7 +507,7 @@ protected function generateFreeBusyCalendar(FreeBusyData $fbData): VCalendar $vfreebusy->add($dtstamp); foreach ($fbData->getData() as $busyTime) { - $busyType = strtoupper($busyTime['type']); + $busyType = strtoupper((string) $busyTime['type']); // Ignoring all the FREE parts, because those are already assumed. if ('FREE' === $busyType) { diff --git a/lib/ITip/Broker.php b/lib/ITip/Broker.php index 9bcdcc2a2..f36b32787 100644 --- a/lib/ITip/Broker.php +++ b/lib/ITip/Broker.php @@ -354,7 +354,7 @@ protected function processMessageReply(Message $itipMessage, ?VCalendar $existin $instances[$recurId] = $attendee['PARTSTAT']->getValue(); if (isset($vevent->{'REQUEST-STATUS'})) { $requestStatus = $vevent->{'REQUEST-STATUS'}->getValue(); - list($requestStatus) = explode(';', $requestStatus); + [$requestStatus] = explode(';', $requestStatus); } } @@ -754,7 +754,7 @@ protected function parseEventForAttendee(VCalendar $calendar, array $eventInfo, // EXDATE $dt = DateTimeParser::parse($instance['id'], $eventInfo['timezone']); // Treat is as a DATE field - if (strlen($instance['id']) <= 8) { + if (strlen((string) $instance['id']) <= 8) { $event->add('DTSTART', $dt, ['VALUE' => 'DATE']); } else { $event->add('DTSTART', $dt); @@ -766,7 +766,7 @@ protected function parseEventForAttendee(VCalendar $calendar, array $eventInfo, if ('master' !== $instance['id']) { $dt = DateTimeParser::parse($instance['id'], $eventInfo['timezone']); // Treat is as a DATE field - if (strlen($instance['id']) <= 8) { + if (strlen((string) $instance['id']) <= 8) { $event->add('RECURRENCE-ID', $dt, ['VALUE' => 'DATE']); } else { $event->add('RECURRENCE-ID', $dt); @@ -858,7 +858,7 @@ protected function parseEventInfo(VCalendar $calendar): array $organizer = $vevent->ORGANIZER->getNormalizedValue(); $organizerName = $vevent->ORGANIZER['CN'] ?? null; } else { - if (strtoupper($organizer) !== strtoupper($vevent->ORGANIZER->getNormalizedValue())) { + if (strtoupper($organizer) !== strtoupper((string) $vevent->ORGANIZER->getNormalizedValue())) { throw new SameOrganizerForAllComponentsException('Every instance of the event must have the same organizer.'); } } @@ -896,7 +896,7 @@ protected function parseEventInfo(VCalendar $calendar): array sort($rrule); } if (isset($vevent->STATUS)) { - $status = strtoupper($vevent->STATUS->getValue()); + $status = strtoupper((string) $vevent->STATUS->getValue()); } $recurId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->getValue() : 'master'; @@ -914,7 +914,7 @@ protected function parseEventInfo(VCalendar $calendar): array foreach ($vevent->ATTENDEE as $attendee) { if ($this->scheduleAgentServerRules && isset($attendee['SCHEDULE-AGENT']) - && 'CLIENT' === strtoupper($attendee['SCHEDULE-AGENT']->getValue()) + && 'CLIENT' === strtoupper((string) $attendee['SCHEDULE-AGENT']->getValue()) ) { continue; } diff --git a/lib/ITip/Message.php b/lib/ITip/Message.php index 6f59eebc2..505e07f25 100644 --- a/lib/ITip/Message.php +++ b/lib/ITip/Message.php @@ -32,12 +32,12 @@ class Message * Contains the ITip method, which is something like REQUEST, REPLY or * CANCEL. */ - public ?string $method; + public ?string $method = null; /** * The current sequence number for the event. */ - public ?int $sequence; + public ?int $sequence = null; /** * The senders' email address. @@ -52,7 +52,7 @@ class Message * The name of the sender. This is often populated from a CN parameter from * either the ORGANIZER or ATTENDEE, depending on the message. */ - public ?string $senderName; + public ?string $senderName = null; /** * The recipient's email address. @@ -63,7 +63,7 @@ class Message * The name of the recipient. This is usually populated with the CN * parameter from the ATTENDEE or ORGANIZER property, if it's available. */ - public ?string $recipientName; + public ?string $recipientName = null; /** * After the message has been delivered, this should contain a string such @@ -108,7 +108,7 @@ public function getScheduleStatus() if (!$this->scheduleStatus) { return false; } - list($scheduleStatus) = explode(';', $this->scheduleStatus); + [$scheduleStatus] = explode(';', $this->scheduleStatus); return $scheduleStatus; } diff --git a/lib/Node.php b/lib/Node.php index 0dbe7cfad..47889a2ab 100644 --- a/lib/Node.php +++ b/lib/Node.php @@ -42,7 +42,7 @@ abstract class Node implements \IteratorAggregate, \ArrayAccess, \Countable, \Js /** * Reference to the parent object, if this is not the top object. */ - public ?Node $parent; + public ?Node $parent = null; /** * Iterator override. @@ -52,7 +52,7 @@ abstract class Node implements \IteratorAggregate, \ArrayAccess, \Countable, \Js /** * The root document. */ - protected ?Component $root; + protected ?Component $root = null; /** * Serializes the node into a mimedir format. diff --git a/lib/PHPUnitAssertions.php b/lib/PHPUnitAssertions.php index 1976c3afd..a201379be 100644 --- a/lib/PHPUnitAssertions.php +++ b/lib/PHPUnitAssertions.php @@ -55,13 +55,13 @@ public function assertVObjectEqualsVObject($expected, $actual, string $message = $actual = $getObj($actual)->serialize(); // Finding wildcards in expected. - preg_match_all('|^([A-Z]+):\\*\\*ANY\\*\\*\r$|m', $expected, $matches, PREG_SET_ORDER); + preg_match_all('|^([A-Z]+):\\*\\*ANY\\*\\*\r$|m', (string) $expected, $matches, PREG_SET_ORDER); foreach ($matches as $match) { $actual = preg_replace( '|^'.preg_quote($match[1], '|').':(.*)\r$|m', $match[1].':**ANY**'."\r", - $actual + (string) $actual ); } diff --git a/lib/Parameter.php b/lib/Parameter.php index 9952d3d2b..5b0804ec5 100644 --- a/lib/Parameter.php +++ b/lib/Parameter.php @@ -16,7 +16,7 @@ * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ -class Parameter extends Node +class Parameter extends Node implements \Stringable { /** * Parameter name. @@ -74,83 +74,12 @@ public function __construct(Document $root, ?string $name, $value = null) */ public static function guessParameterNameByValue(string $value): string { - switch (strtoupper($value)) { - // Encodings - case '7-BIT': - case 'QUOTED-PRINTABLE': - case 'BASE64': - $name = 'ENCODING'; - break; - - // Common types - case 'WORK': - case 'HOME': - case 'PREF': - // Delivery Label Type - case 'DOM': - case 'INTL': - case 'POSTAL': - case 'PARCEL': - // Telephone types - case 'VOICE': - case 'FAX': - case 'MSG': - case 'CELL': - case 'PAGER': - case 'BBS': - case 'MODEM': - case 'CAR': - case 'ISDN': - case 'VIDEO': - // EMAIL types (lol) - case 'AOL': - case 'APPLELINK': - case 'ATTMAIL': - case 'CIS': - case 'EWORLD': - case 'INTERNET': - case 'IBMMAIL': - case 'MCIMAIL': - case 'POWERSHARE': - case 'PRODIGY': - case 'TLX': - case 'X400': - // Photo / Logo format types - case 'GIF': - case 'CGM': - case 'WMF': - case 'BMP': - case 'DIB': - case 'PICT': - case 'TIFF': - case 'PDF': - case 'PS': - case 'JPEG': - case 'MPEG': - case 'MPEG2': - case 'AVI': - case 'QTIME': - // Sound Digital Audio Type - case 'WAVE': - case 'PCM': - case 'AIFF': - // Key types - case 'X509': - case 'PGP': - $name = 'TYPE'; - break; - - // Value types - case 'INLINE': - case 'URL': - case 'CONTENT-ID': - case 'CID': - $name = 'VALUE'; - break; - - default: - $name = ''; - } + $name = match (strtoupper($value)) { + '7-BIT', 'QUOTED-PRINTABLE', 'BASE64' => 'ENCODING', + 'WORK', 'HOME', 'PREF', 'DOM', 'INTL', 'POSTAL', 'PARCEL', 'VOICE', 'FAX', 'MSG', 'CELL', 'PAGER', 'BBS', 'MODEM', 'CAR', 'ISDN', 'VIDEO', 'AOL', 'APPLELINK', 'ATTMAIL', 'CIS', 'EWORLD', 'INTERNET', 'IBMMAIL', 'MCIMAIL', 'POWERSHARE', 'PRODIGY', 'TLX', 'X400', 'GIF', 'CGM', 'WMF', 'BMP', 'DIB', 'PICT', 'TIFF', 'PDF', 'PS', 'JPEG', 'MPEG', 'MPEG2', 'AVI', 'QTIME', 'WAVE', 'PCM', 'AIFF', 'X509', 'PGP' => 'TYPE', + 'INLINE', 'URL', 'CONTENT-ID', 'CID' => 'VALUE', + default => '', + }; return $name; } @@ -234,7 +163,7 @@ public function has(string $value): bool { return in_array( strtolower($value), - array_map('strtolower', (array) $this->value) + array_map(strtolower(...), (array) $this->value) ); } diff --git a/lib/Parser/Json.php b/lib/Parser/Json.php index 6403af4e9..45f985f68 100644 --- a/lib/Parser/Json.php +++ b/lib/Parser/Json.php @@ -27,12 +27,12 @@ class Json extends Parser /** * The input data. */ - protected ?array $input; + protected ?array $input = null; /** * Root component. */ - protected ?Document $root; + protected ?Document $root = null; /** * This method starts the parsing process. @@ -61,16 +61,11 @@ public function parse($input = null, int $options = 0): ?Document $this->options = $options; } - switch ($this->input[0]) { - case 'vcalendar': - $this->root = new VCalendar([], false); - break; - case 'vcard': - $this->root = new VCard([], false); - break; - default: - throw new ParseException('The root component must either be a vcalendar, or a vcard'); - } + $this->root = match ($this->input[0]) { + 'vcalendar' => new VCalendar([], false), + 'vcard' => new VCard([], false), + default => throw new ParseException('The root component must either be a vcalendar, or a vcard'), + }; foreach ($this->input[1] as $prop) { $this->root->add($this->parseProperty($prop)); } @@ -97,17 +92,13 @@ public function parseComponent(array $jComp): Component $self = $this; $properties = array_map( - function ($jProp) use ($self) { - return $self->parseProperty($jProp); - }, + $self->parseProperty(...), $jComp[1] ); if (isset($jComp[2])) { $components = array_map( - function ($jComp) use ($self) { - return $self->parseComponent($jComp); - }, + $self->parseComponent(...), $jComp[2] ); } else { @@ -134,7 +125,7 @@ public function parseProperty(array $jProp): Property $valueType, ) = $jProp; - $propertyName = strtoupper($propertyName); + $propertyName = strtoupper((string) $propertyName); // This is the default class we would be using if we didn't know the // value type. We're using this value later in this function. @@ -144,7 +135,7 @@ public function parseProperty(array $jProp): Property $value = array_slice($jProp, 3); - $valueType = strtoupper($valueType); + $valueType = strtoupper((string) $valueType); if (isset($parameters['group'])) { $propertyName = $parameters['group'].'.'.$propertyName; @@ -165,7 +156,7 @@ public function parseProperty(array $jProp): Property // If the value type we received (e.g.: TEXT) was not the default value // type for the given property (e.g.: BDAY), we need to add a VALUE= // parameter. - if ($defaultPropertyClass !== get_class($prop)) { + if ($defaultPropertyClass !== $prop::class) { $prop['VALUE'] = $valueType; } diff --git a/lib/Parser/MimeDir.php b/lib/Parser/MimeDir.php index 7718dba04..b71ae2634 100644 --- a/lib/Parser/MimeDir.php +++ b/lib/Parser/MimeDir.php @@ -41,7 +41,7 @@ class MimeDir extends Parser /** * Root component. */ - protected ?Document $root; + protected ?Document $root = null; /** * By default, all input will be assumed to be UTF-8. @@ -155,23 +155,18 @@ protected function parseDocument(): void // BOM is ZERO WIDTH NO-BREAK SPACE (U+FEFF). // It's 0xEF 0xBB 0xBF in UTF-8 hex. - if (3 <= strlen($line) + if (3 <= strlen((string) $line) && 0xEF === ord($line[0]) && 0xBB === ord($line[1]) && 0xBF === ord($line[2])) { - $line = \substr($line, 3); + $line = \substr((string) $line, 3); } - switch (strtoupper($line)) { - case 'BEGIN:VCALENDAR': - $class = VCalendar::$componentMap['VCALENDAR']; - break; - case 'BEGIN:VCARD': - $class = VCard::$componentMap['VCARD']; - break; - default: - throw new ParseException('This parser only supports VCARD and VCALENDAR files'); - } + $class = match (strtoupper((string) $line)) { + 'BEGIN:VCALENDAR' => VCalendar::$componentMap['VCALENDAR'], + 'BEGIN:VCARD' => VCard::$componentMap['VCARD'], + default => throw new ParseException('This parser only supports VCARD and VCALENDAR files'), + }; $this->root = new $class([], false); @@ -179,10 +174,10 @@ protected function parseDocument(): void // Reading until we hit END: try { $line = $this->readLine(); - } catch (EofException $oEx) { + } catch (EofException) { $line = 'END:'.$this->root->name; } - if ('END:' === strtoupper(\substr($line, 0, 4))) { + if ('END:' === strtoupper(\substr((string) $line, 0, 4))) { break; } $result = $this->parseLine($line); @@ -191,7 +186,7 @@ protected function parseDocument(): void } } - $name = strtoupper(\substr($line, 4)); + $name = strtoupper(\substr((string) $line, 4)); if ($name !== $this->root->name) { throw new ParseException('Invalid MimeDir file. expected: "END:'.$this->root->name.'" got: "END:'.$name.'"'); } @@ -220,7 +215,7 @@ protected function parseLine(string $line) while (true) { // Reading until we hit END: $line = $this->readLine(); - if ('END:' === strtoupper(\substr($line, 0, 4))) { + if ('END:' === strtoupper(\substr((string) $line, 0, 4))) { break; } $result = $this->parseLine($line); @@ -229,7 +224,7 @@ protected function parseLine(string $line) } } - $name = strtoupper(\substr($line, 4)); + $name = strtoupper(\substr((string) $line, 4)); if ($name !== $component->name) { throw new ParseException('Invalid MimeDir file. expected: "END:'.$component->name.'" got: "END:'.$name.'"'); } @@ -691,7 +686,7 @@ private function extractQuotedPrintableValue(): string // missing a whitespace. So if 'forgiving' is turned on, we will take // those as well. if ($this->options & self::OPTION_FORGIVING) { - while ('=' === \substr($value, -1) && $this->lineBuffer) { + while (str_ends_with($value, '=') && $this->lineBuffer) { // Reading the line $this->readLine(); // Grabbing the raw form diff --git a/lib/Parser/Parser.php b/lib/Parser/Parser.php index 29921d182..c5126445a 100644 --- a/lib/Parser/Parser.php +++ b/lib/Parser/Parser.php @@ -30,11 +30,6 @@ abstract class Parser */ public const OPTION_IGNORE_INVALID_LINES = 2; - /** - * Bitmask of parser options. - */ - protected int $options; - /** * Creates the parser. * @@ -42,12 +37,11 @@ abstract class Parser * * @param int $options any parser options (OPTION constants) */ - public function __construct($input = null, int $options = 0) + public function __construct($input = null, protected int $options = 0) { if (!is_null($input)) { $this->setInput($input); } - $this->options = $options; } /** diff --git a/lib/Parser/XML.php b/lib/Parser/XML.php index 9a5100684..77a229733 100644 --- a/lib/Parser/XML.php +++ b/lib/Parser/XML.php @@ -28,17 +28,17 @@ class XML extends Parser /** * The input data. */ - protected ?array $input; + protected ?array $input = null; /** * A pointer/reference to the input. */ - private ?array $pointer; + private ?array $pointer = null; /** * Document, root component. */ - protected ?Document $root; + protected ?Document $root = null; /** * Creates the parser. @@ -146,7 +146,7 @@ protected function parseVCardComponents(Component $parentComponent): void protected function parseProperties(Component $parentComponent, string $propertyNamePrefix = ''): void { foreach ($this->pointer ?: [] as $xmlProperty) { - list($namespace, $tagName) = SabreXml\Service::parseClarkNotation($xmlProperty['name']); + [$namespace, $tagName] = SabreXml\Service::parseClarkNotation($xmlProperty['name']); $propertyName = $tagName; $propertyValue = []; diff --git a/lib/Property.php b/lib/Property.php index 56b571e51..e369b5239 100644 --- a/lib/Property.php +++ b/lib/Property.php @@ -14,27 +14,8 @@ * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ -abstract class Property extends Node +abstract class Property extends Node implements \Stringable { - /** - * The root document. - */ - public ?Component $root; - - /** - * Property name. - * - * This will contain a string such as DTSTART, SUMMARY, FN. - */ - public ?string $name; - - /** - * Property group. - * - * This is only used in vcards - */ - public ?string $group; - /** * List of parameters. */ @@ -75,13 +56,13 @@ abstract class Property extends Node * @param array $parameters List of parameters * @param string|null $group The vcard property group */ - public function __construct(Component $root, ?string $name, $value = null, array $parameters = [], ?string $group = null, ?int $lineIndex = null, ?string $lineString = null) + public function __construct(public ?Component $root, /** + * Property name. + * + * This will contain a string such as DTSTART, SUMMARY, FN. + */ + public ?string $name, $value = null, array $parameters = [], public ?string $group = null, ?int $lineIndex = null, ?string $lineString = null) { - $this->name = $name; - $this->group = $group; - - $this->root = $root; - foreach ($parameters as $k => $v) { $this->add($k, $v); } @@ -242,7 +223,7 @@ public function serialize(): string ); // remove single space after last CRLF - return \substr($str, 0, -1); + return \substr((string) $str, 0, -1); } /** @@ -282,7 +263,7 @@ public function jsonSerialize(): array if ('VALUE' === $parameter->name) { continue; } - $parameters[strtolower($parameter->name)] = $parameter->jsonSerialize(); + $parameters[strtolower((string) $parameter->name)] = $parameter->jsonSerialize(); } // In jCard, we need to encode the property-group as a separate 'group' // parameter. @@ -292,7 +273,7 @@ public function jsonSerialize(): array return array_merge( [ - strtolower($this->name), + strtolower((string) $this->name), (object) $parameters, strtolower($this->getValueType()), ], @@ -329,13 +310,13 @@ public function xmlSerialize(Xml\Writer $writer): void $parameters[] = $parameter; } - $writer->startElement(strtolower($this->name)); + $writer->startElement(strtolower((string) $this->name)); if (!empty($parameters)) { $writer->startElement('parameters'); foreach ($parameters as $parameter) { - $writer->startElement(strtolower($parameter->name)); + $writer->startElement(strtolower((string) $parameter->name)); $writer->write($parameter); $writer->endElement(); } @@ -520,7 +501,7 @@ public function validate(int $options = 0): array } // Checking if the property name does not contain any invalid bytes. - if (!preg_match('/^([A-Z0-9-]+)$/', $this->name)) { + if (!preg_match('/^([A-Z0-9-]+)$/', (string) $this->name)) { $warnings[] = [ 'level' => $options & self::REPAIR ? 1 : 3, 'message' => 'The property name: '.$this->name.' contains invalid characters. Only A-Z, 0-9 and - are allowed', diff --git a/lib/Property/Binary.php b/lib/Property/Binary.php index 25cda8d25..124794c9d 100644 --- a/lib/Property/Binary.php +++ b/lib/Property/Binary.php @@ -62,7 +62,7 @@ public function setRawMimeDirValue(string $val): void */ public function getRawMimeDirValue(): string { - return base64_encode($this->value); + return base64_encode((string) $this->value); } /** @@ -83,7 +83,7 @@ public function getValueType(): string */ public function getJsonValue(): array { - return [base64_encode($this->getValue())]; + return [base64_encode((string) $this->getValue())]; } /** @@ -93,7 +93,7 @@ public function getJsonValue(): array */ public function setJsonValue(array $value): void { - $value = array_map('base64_decode', $value); + $value = array_map(base64_decode(...), $value); parent::setJsonValue($value); } } diff --git a/lib/Property/Boolean.php b/lib/Property/Boolean.php index cdc3408aa..f33fbafdc 100644 --- a/lib/Property/Boolean.php +++ b/lib/Property/Boolean.php @@ -56,9 +56,7 @@ public function getValueType(): string public function setXmlValue(array $value): void { $value = array_map( - function ($value) { - return 'true' === $value; - }, + fn ($value) => 'true' === $value, $value ); parent::setXmlValue($value); diff --git a/lib/Property/FloatValue.php b/lib/Property/FloatValue.php index 7a36e0976..32b02a972 100644 --- a/lib/Property/FloatValue.php +++ b/lib/Property/FloatValue.php @@ -67,7 +67,7 @@ public function getValueType(): string */ public function getJsonValue(): array { - $val = array_map('floatval', $this->getParts()); + $val = array_map(floatval(...), $this->getParts()); // Special-casing the GEO property. // @@ -86,7 +86,7 @@ public function getJsonValue(): array */ public function setXmlValue(array $value): void { - $value = array_map('floatval', $value); + $value = array_map(floatval(...), $value); parent::setXmlValue($value); } @@ -101,7 +101,7 @@ protected function xmlSerializeValue(Xml\Writer $writer): void // See: // http://tools.ietf.org/html/rfc6321#section-3.4.1.2 if ('GEO' === $this->name) { - $value = array_map('floatval', $this->getParts()); + $value = array_map(floatval(...), $this->getParts()); $writer->writeElement('latitude', $value[0]); $writer->writeElement('longitude', $value[1]); diff --git a/lib/Property/ICalendar/CalAddress.php b/lib/Property/ICalendar/CalAddress.php index 01969e87d..a2b13eee7 100644 --- a/lib/Property/ICalendar/CalAddress.php +++ b/lib/Property/ICalendar/CalAddress.php @@ -43,10 +43,10 @@ public function getValueType(): string public function getNormalizedValue(): string { $input = $this->getValue(); - if (!strpos($input, ':')) { + if (!strpos((string) $input, ':')) { return $input; } - list($schema, $everythingElse) = explode(':', $input, 2); + [$schema, $everythingElse] = explode(':', (string) $input, 2); $schema = strtolower($schema); if ('mailto' === $schema) { $everythingElse = strtolower($everythingElse); diff --git a/lib/Property/ICalendar/DateTime.php b/lib/Property/ICalendar/DateTime.php index 4f2ff4261..e4c876563 100644 --- a/lib/Property/ICalendar/DateTime.php +++ b/lib/Property/ICalendar/DateTime.php @@ -111,7 +111,7 @@ public function isFloating(): bool !$this->hasTime() || ( !isset($this['TZID']) - && false === strpos($this->getValue(), 'Z') + && !str_contains((string) $this->getValue(), 'Z') ); } @@ -286,9 +286,7 @@ public function setJsonValue(array $value): void // those. $this->setValue( array_map( - function ($item) { - return strtr($item, [':' => '', '-' => '']); - }, + fn ($item) => strtr($item, [':' => '', '-' => '']), $value ) ); @@ -306,7 +304,7 @@ function ($item) { public function offsetSet($offset, $value): void { parent::offsetSet($offset, $value); - if ('VALUE' !== strtoupper($offset)) { + if ('VALUE' !== strtoupper((string) $offset)) { return; } @@ -347,7 +345,7 @@ public function validate(int $options = 0): array DateTimeParser::parseDateTime($value); break; } - } catch (InvalidDataException $e) { + } catch (InvalidDataException) { $messages[] = [ 'level' => 3, 'message' => 'The supplied value ('.$value.') is not a correct '.$valueType, diff --git a/lib/Property/ICalendar/Period.php b/lib/Property/ICalendar/Period.php index 7632cb4d1..15291e805 100644 --- a/lib/Property/ICalendar/Period.php +++ b/lib/Property/ICalendar/Period.php @@ -64,9 +64,7 @@ public function getValueType(): string public function setJsonValue(array $value): void { $value = array_map( - function ($item) { - return strtr(implode('/', $item), [':' => '', '-' => '']); - }, + fn ($item) => strtr(implode('/', $item), [':' => '', '-' => '']), $value ); parent::setJsonValue($value); @@ -83,7 +81,7 @@ public function getJsonValue(): array { $return = []; foreach ($this->getParts() as $item) { - list($start, $end) = explode('/', $item, 2); + [$start, $end] = explode('/', (string) $item, 2); $start = DateTimeParser::parseDateTime($start); diff --git a/lib/Property/ICalendar/Recur.php b/lib/Property/ICalendar/Recur.php index 10081673e..66bb54970 100644 --- a/lib/Property/ICalendar/Recur.php +++ b/lib/Property/ICalendar/Recur.php @@ -29,7 +29,7 @@ class Recur extends Property /** * Reference to the parent object, if this is not the top object. */ - public ?Node $parent; + public ?Node $parent = null; /** * Updates the current value. @@ -52,17 +52,17 @@ public function setValue($value): void $v = strtoupper($v); // The value had multiple sub-values - if (false !== strpos($v, ',')) { + if (str_contains($v, ',')) { $v = explode(',', $v); } - if (0 === strcmp($k, 'until')) { + if (0 === strcmp((string) $k, 'until')) { $v = strtr($v, [':' => '', '-' => '']); } } elseif (is_array($v)) { - $v = array_map('strtoupper', $v); + $v = array_map(strtoupper(...), $v); } - $newVal[strtoupper($k)] = $v; + $newVal[strtoupper((string) $k)] = $v; } $this->value = $newVal; } elseif (is_string($value)) { @@ -151,13 +151,13 @@ public function getJsonValue(): array { $values = []; foreach ($this->getParts() as $k => $v) { - if (0 === strcmp($k, 'UNTIL')) { + if (0 === strcmp((string) $k, 'UNTIL')) { $date = new DateTime($this->root, null, $v); - $values[strtolower($k)] = $date->getJsonValue()[0]; - } elseif (0 === strcmp($k, 'COUNT')) { - $values[strtolower($k)] = intval($v); + $values[strtolower((string) $k)] = $date->getJsonValue()[0]; + } elseif (0 === strcmp((string) $k, 'COUNT')) { + $values[strtolower((string) $k)] = intval($v); } else { - $values[strtolower($k)] = $v; + $values[strtolower((string) $k)] = $v; } } @@ -196,10 +196,10 @@ public static function stringToArray(string $value): array throw new InvalidDataException('The supplied iCalendar RRULE part is incorrect: '.$part); } - list($partName, $partValue) = $parts; + [$partName, $partValue] = $parts; // The value itself had multiple values.. - if (false !== strpos($partValue, ',')) { + if (str_contains($partValue, ',')) { $partValue = explode(',', $partValue); } $newValue[$partName] = $partValue; diff --git a/lib/Property/IntegerValue.php b/lib/Property/IntegerValue.php index f1ae55d17..1cd7273cc 100644 --- a/lib/Property/IntegerValue.php +++ b/lib/Property/IntegerValue.php @@ -62,7 +62,7 @@ public function getJsonValue(): array */ public function setXmlValue(array $value): void { - $value = array_map('intval', $value); + $value = array_map(intval(...), $value); parent::setXmlValue($value); } } diff --git a/lib/Property/Text.php b/lib/Property/Text.php index 363c3fa4f..7b044346c 100644 --- a/lib/Property/Text.php +++ b/lib/Property/Text.php @@ -213,7 +213,7 @@ public function serialize(): string // If the resulting value contains a \n, we must encode it as // quoted-printable. - if (false !== \strpos($val, "\n")) { + if (str_contains((string) $val, "\n")) { $str .= ';ENCODING=QUOTED-PRINTABLE:'; $lastLine = $str; $out = ''; @@ -222,13 +222,13 @@ public function serialize(): string // encode newlines for us. Specifically, the \r\n sequence must in // vcards be encoded as =0D=OA and we must insert soft-newlines // every 75 bytes. - for ($ii = 0; $ii < \strlen($val); ++$ii) { + for ($ii = 0; $ii < \strlen((string) $val); ++$ii) { $ord = \ord($val[$ii]); // These characters are encoded as themselves. if ($ord >= 32 && $ord <= 126) { $lastLine .= $val[$ii]; } else { - $lastLine .= '='.\strtoupper(\bin2hex($val[$ii])); + $lastLine .= '='.\strtoupper(\bin2hex((string) $val[$ii])); } if (\strlen($lastLine) >= 75) { // Soft line break @@ -255,7 +255,7 @@ public function serialize(): string ); // remove single space after last CRLF - return \substr($str, 0, -1); + return \substr((string) $str, 0, -1); } /** diff --git a/lib/Property/Time.php b/lib/Property/Time.php index ed314fc0d..9c4016c64 100644 --- a/lib/Property/Time.php +++ b/lib/Property/Time.php @@ -104,7 +104,7 @@ public function getJsonValue(): array $timeStr .= 'Z'; } else { $timeStr .= - preg_replace('/([0-9]{2})([0-9]{2})$/', '$1:$2', $parts['timezone']); + preg_replace('/([0-9]{2})([0-9]{2})$/', '$1:$2', (string) $parts['timezone']); } } @@ -118,9 +118,7 @@ public function getJsonValue(): array public function setXmlValue(array $value): void { $value = array_map( - function ($value) { - return str_replace(':', '', $value); - }, + fn ($value) => str_replace(':', '', $value), $value ); parent::setXmlValue($value); diff --git a/lib/Property/Uri.php b/lib/Property/Uri.php index 228b7090f..ec98f7416 100644 --- a/lib/Property/Uri.php +++ b/lib/Property/Uri.php @@ -74,14 +74,10 @@ public function setRawMimeDirValue(string $val): void $matches = preg_split($regex, $val, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); $newVal = ''; foreach ($matches as $match) { - switch ($match) { - case '\:': - $newVal .= ':'; - break; - default: - $newVal .= $match; - break; - } + match ($match) { + '\:' => $newVal .= ':', + default => $newVal .= $match, + }; } $this->value = $newVal; } else { diff --git a/lib/Property/UtcOffset.php b/lib/Property/UtcOffset.php index 09aab0151..52d81f1f6 100644 --- a/lib/Property/UtcOffset.php +++ b/lib/Property/UtcOffset.php @@ -42,9 +42,7 @@ public function getValueType(): string public function setJsonValue(array $value): void { $value = array_map( - function ($value) { - return str_replace(':', '', $value); - }, + fn ($value) => str_replace(':', '', $value), $value ); parent::setJsonValue($value); @@ -58,10 +56,8 @@ function ($value) { public function getJsonValue(): array { return array_map( - function ($value) { - return substr($value, 0, -2).':'. - substr($value, -2); - }, + fn ($value) => substr((string) $value, 0, -2).':'. + substr((string) $value, -2), parent::getJsonValue() ); } diff --git a/lib/Property/VCard/DateAndOrTime.php b/lib/Property/VCard/DateAndOrTime.php index e87bf1b3a..7aa88a628 100644 --- a/lib/Property/VCard/DateAndOrTime.php +++ b/lib/Property/VCard/DateAndOrTime.php @@ -232,14 +232,10 @@ protected function xmlSerializeValue(Xml\Writer $writer): void $value = ''; // $d = defined - $d = function ($part) use ($parts): bool { - return !is_null($parts[$part]); - }; + $d = (fn ($part): bool => !is_null($parts[$part])); // $r = read - $r = function ($part) use ($parts) { - return $parts[$part]; - }; + $r = (fn ($part) => $parts[$part]); // From the Relax NG Schema. // @@ -341,7 +337,7 @@ public function validate(int $options = 0): array try { DateTimeParser::parseVCardDateTime($value); - } catch (InvalidDataException $e) { + } catch (InvalidDataException) { $messages[] = [ 'level' => 3, 'message' => 'The supplied value ('.$value.') is not a correct DATE-AND-OR-TIME property', diff --git a/lib/Recur/RDateIterator.php b/lib/Recur/RDateIterator.php index 4ad688f9d..b9bd0beff 100644 --- a/lib/Recur/RDateIterator.php +++ b/lib/Recur/RDateIterator.php @@ -26,9 +26,13 @@ class RDateIterator implements \Iterator * * @param string|array $rrule */ - public function __construct($rrule, \DateTimeInterface $start) + public function __construct($rrule, /** + * The reference start date/time for the rrule. + * + * All calculations are based on this initial date. + */ + protected \DateTimeInterface $startDate) { - $this->startDate = $start; $this->parseRDate($rrule); $this->currentDate = clone $this->startDate; } @@ -117,13 +121,6 @@ public function fastForward(\DateTimeInterface $dt): void } } - /** - * The reference start date/time for the rrule. - * - * All calculations are based on this initial date. - */ - protected \DateTimeInterface $startDate; - /** * The date of the current iteration. You can get this by calling * ->current(). diff --git a/lib/Recur/RRuleIterator.php b/lib/Recur/RRuleIterator.php index 87e185b1c..b260b2d04 100644 --- a/lib/Recur/RRuleIterator.php +++ b/lib/Recur/RRuleIterator.php @@ -37,9 +37,13 @@ class RRuleIterator implements \Iterator * * @throws InvalidDataException */ - public function __construct($rrule, \DateTimeInterface $start) + public function __construct($rrule, /** + * The reference start date/time for the rrule. + * + * All calculations are based on this initial date. + */ + protected \DateTimeInterface $startDate) { - $this->startDate = $start; $this->parseRRule($rrule); $this->currentDate = clone $this->startDate; } @@ -146,13 +150,6 @@ public function fastForward(\DateTimeInterface $dt): void } } - /** - * The reference start date/time for the rrule. - * - * All calculations are based on this initial date. - */ - protected \DateTimeInterface $startDate; - /** * The date of the current iteration. You can get this by calling * ->current(). @@ -752,10 +749,10 @@ protected function parseRRule($rrule): void } foreach ($rrule as $key => $value) { - $key = strtoupper($key); + $key = strtoupper((string) $key); switch ($key) { case 'FREQ': - $value = strtolower($value); + $value = strtolower((string) $value); if (!in_array( $value, ['secondly', 'minutely', 'hourly', 'daily', 'weekly', 'monthly', 'yearly'] @@ -806,7 +803,7 @@ protected function parseRRule($rrule): void case 'BYDAY': $value = (array) $value; foreach ($value as $part) { - if (!preg_match('#^ (-|\+)? ([1-5])? (MO|TU|WE|TH|FR|SA|SU) $# xi', $part)) { + if (!preg_match('#^ (-|\+)? ([1-5])? (MO|TU|WE|TH|FR|SA|SU) $# xi', (string) $part)) { throw new InvalidDataException('Invalid part in BYDAY clause: '.$part); } } @@ -849,7 +846,7 @@ protected function parseRRule($rrule): void break; case 'WKST': - $this->weekStart = strtoupper($value); + $this->weekStart = strtoupper((string) $value); break; default: @@ -889,7 +886,7 @@ protected function getMonthlyOccurrences(): array // that point and add it to the results. if ($this->byDay) { foreach ($this->byDay as $day) { - $dayName = $this->dayNames[$this->dayMap[substr($day, -2)]]; + $dayName = $this->dayNames[$this->dayMap[substr((string) $day, -2)]]; // Dayname will be something like 'wednesday'. Now we need to find // all wednesdays in this month. @@ -910,8 +907,8 @@ protected function getMonthlyOccurrences(): array // So now we have 'all wednesdays' for month. It is however // possible that the user only really wanted the 1st, 2nd or last // wednesday. - if (strlen($day) > 2) { - $offset = (int) substr($day, 0, -2); + if (strlen((string) $day) > 2) { + $offset = (int) substr((string) $day, 0, -2); if ($offset > 0) { // It is possible that the day does not exist, such as a @@ -1015,7 +1012,7 @@ protected function getDays(): array // The day may be preceded with a positive (+n) or // negative (-n) integer. However, this does not make // sense in 'weekly' so we ignore it here. - $recurrenceDays[] = $this->dayMap[substr($byDay, -2)]; + $recurrenceDays[] = $this->dayMap[substr((string) $byDay, -2)]; } return $recurrenceDays; diff --git a/lib/Splitter/VCard.php b/lib/Splitter/VCard.php index 40775a335..a802dbe75 100644 --- a/lib/Splitter/VCard.php +++ b/lib/Splitter/VCard.php @@ -22,13 +22,6 @@ */ class VCard implements SplitterInterface { - /** - * File handle. - * - * @var resource - */ - protected $input; - /** * Persistent parser. */ @@ -42,10 +35,12 @@ class VCard implements SplitterInterface * @param resource $input * @param int $options parser options, see the OPTIONS constants */ - public function __construct($input, int $options = 0) + public function __construct(/** + * File handle. + */ + protected $input, int $options = 0) { - $this->input = $input; - $this->parser = new MimeDir($input, $options); + $this->parser = new MimeDir($this->input, $options); } /** @@ -64,7 +59,7 @@ public function getNext(): ?Component if (!$object instanceof Component\VCard) { throw new VObject\ParseException('The supplied input contained non-VCARD data.'); } - } catch (VObject\EofException $e) { + } catch (VObject\EofException) { return null; } diff --git a/lib/TimezoneGuesser/FindFromTimezoneIdentifier.php b/lib/TimezoneGuesser/FindFromTimezoneIdentifier.php index ba84d45eb..7547d574a 100644 --- a/lib/TimezoneGuesser/FindFromTimezoneIdentifier.php +++ b/lib/TimezoneGuesser/FindFromTimezoneIdentifier.php @@ -46,7 +46,7 @@ public function find(string $tzid, ?bool $failIfUncertain = false): ?\DateTimeZo ) { return new \DateTimeZone($tzid); } - } catch (\Exception $e) { + } catch (\Exception) { } return null; diff --git a/lib/TimezoneGuesser/FindFromTimezoneMap.php b/lib/TimezoneGuesser/FindFromTimezoneMap.php index f203937bc..5beb5450d 100644 --- a/lib/TimezoneGuesser/FindFromTimezoneMap.php +++ b/lib/TimezoneGuesser/FindFromTimezoneMap.php @@ -27,7 +27,7 @@ public function find(string $tzid, ?bool $failIfUncertain = false): ?\DateTimeZo if ($this->hasTzInMap($tzid)) { try { return new \DateTimeZone($this->getTzFromMap($tzid)); - } catch (\Exception $e) { + } catch (\Exception) { return null; } } @@ -43,7 +43,7 @@ public function find(string $tzid, ?bool $failIfUncertain = false): ?\DateTimeZo if ($this->hasTzInMap($tzidAlternate)) { try { return new \DateTimeZone($this->getTzFromMap($tzidAlternate)); - } catch (\Exception $e) { + } catch (\Exception) { return null; } } diff --git a/lib/TimezoneGuesser/GuessFromLicEntry.php b/lib/TimezoneGuesser/GuessFromLicEntry.php index 486b33796..7212117f7 100644 --- a/lib/TimezoneGuesser/GuessFromLicEntry.php +++ b/lib/TimezoneGuesser/GuessFromLicEntry.php @@ -23,7 +23,7 @@ public function guess(VTimeZone $vtimezone, ?bool $failIfUncertain = false): ?\D // Libical generators may specify strings like // "SystemV/EST5EDT". For those we must remove the // SystemV part. - if ('SystemV/' === substr($lic, 0, 8)) { + if (str_starts_with($lic, 'SystemV/')) { $lic = substr($lic, 8); } diff --git a/lib/TimezoneGuesser/GuessFromMsTzId.php b/lib/TimezoneGuesser/GuessFromMsTzId.php index 392333cb0..82060bd27 100644 --- a/lib/TimezoneGuesser/GuessFromMsTzId.php +++ b/lib/TimezoneGuesser/GuessFromMsTzId.php @@ -105,7 +105,7 @@ public function guess(VTimeZone $vtimezone, ?bool $failIfUncertain = false): ?\D $cdoId = (int) $vtimezone->{'X-MICROSOFT-CDO-TZID'}->getValue(); // 2 can mean both Europe/Lisbon and Europe/Sarajevo. - if (2 === $cdoId && false !== strpos((string) $vtimezone->TZID, 'Sarajevo')) { + if (2 === $cdoId && str_contains((string) $vtimezone->TZID, 'Sarajevo')) { return new \DateTimeZone('Europe/Sarajevo'); } diff --git a/lib/VCardConverter.php b/lib/VCardConverter.php index ff8fe3f81..c1d41bdb3 100644 --- a/lib/VCardConverter.php +++ b/lib/VCardConverter.php @@ -127,7 +127,7 @@ protected function convertProperty(Component\VCard $input, Component\VCard $outp $output->add('ITEM'.$x.'.X-ABLABEL', '_$!!$_'); } } elseif ('KIND' === $property->name) { - switch (strtolower($property->getValue())) { + switch (strtolower((string) $property->getValue())) { case 'org': // vCard 3.0 does not have an equivalent to KIND:ORG, // but apple has an extension that means the same @@ -171,12 +171,12 @@ protected function convertProperty(Component\VCard $input, Component\VCard $outp } switch ($property->name) { case 'X-ABSHOWAS': - if ('COMPANY' === strtoupper($property->getValue())) { + if ('COMPANY' === strtoupper((string) $property->getValue())) { $newProperty = $output->createProperty('KIND', 'ORG'); } break; case 'X-ADDRESSBOOKSERVER-KIND': - if ('GROUP' === strtoupper($property->getValue())) { + if ('GROUP' === strtoupper((string) $property->getValue())) { $newProperty = $output->createProperty('KIND', 'GROUP'); } break; @@ -274,10 +274,10 @@ protected function convertBinaryToUri(Component\VCard $output, Binary $newProper $newTypes = []; foreach ($parameters['TYPE']->getParts() as $typePart) { if (in_array( - strtoupper($typePart), + strtoupper((string) $typePart), ['JPEG', 'PNG', 'GIF'] )) { - $mimeType = 'image/'.strtolower($typePart); + $mimeType = 'image/'.strtolower((string) $typePart); } else { $newTypes[] = $typePart; } @@ -292,7 +292,7 @@ protected function convertBinaryToUri(Component\VCard $output, Binary $newProper } } - $newProperty->setValue('data:'.$mimeType.';base64,'.base64_encode($value)); + $newProperty->setValue('data:'.$mimeType.';base64,'.base64_encode((string) $value)); return $newProperty; } @@ -313,7 +313,7 @@ protected function convertUriToBinary(Component\VCard $output, Uri $newProperty) $value = $newProperty->getValue(); // Only converting data: uris - if ('data:' !== substr($value, 0, 5)) { + if (!str_starts_with((string) $value, 'data:')) { return $newProperty; } @@ -325,12 +325,12 @@ protected function convertUriToBinary(Component\VCard $output, Uri $newProperty) 'BINARY' ); - $mimeType = substr($value, 5, strpos($value, ',') - 5); + $mimeType = substr((string) $value, 5, strpos((string) $value, ',') - 5); if (strpos($mimeType, ';')) { $mimeType = substr($mimeType, 0, strpos($mimeType, ';')); - $newProperty->setValue(base64_decode(substr($value, strpos($value, ',') + 1))); + $newProperty->setValue(base64_decode(substr((string) $value, strpos((string) $value, ',') + 1))); } else { - $newProperty->setValue(substr($value, strpos($value, ',') + 1)); + $newProperty->setValue(substr((string) $value, strpos((string) $value, ',') + 1)); } unset($value); @@ -367,7 +367,7 @@ protected function convertParameters40(Property $newProperty, array $parameters) // that's now PREF=1. case 'TYPE': foreach ($param->getParts() as $paramPart) { - if ('PREF' === strtoupper($paramPart)) { + if ('PREF' === strtoupper((string) $paramPart)) { $newProperty->add('PREF', '1'); } else { $newProperty->add($param->name, $paramPart); @@ -402,7 +402,7 @@ protected function convertParameters30(Property $newProperty, array $parameters) case 'ENCODING': // This value only existed in vCard 2.1, and should be // removed for anything else. - if ('QUOTED-PRINTABLE' !== strtoupper($param->getValue())) { + if ('QUOTED-PRINTABLE' !== strtoupper((string) $param->getValue())) { $newProperty->add($param->name, $param->getParts()); } break; diff --git a/rector.php b/rector.php new file mode 100644 index 000000000..201fc8090 --- /dev/null +++ b/rector.php @@ -0,0 +1,16 @@ +withPaths([ + __DIR__.'/lib', + __DIR__.'/tests', + ]) + // uncomment to reach your current PHP version + ->withPhpSets(false, true) + ->withTypeCoverageLevel(0) + ->withDeadCodeLevel(0) + ->withCodeQualityLevel(0); diff --git a/tests/VObject/CliTest.php b/tests/VObject/CliTest.php index c57d25226..6801593b2 100644 --- a/tests/VObject/CliTest.php +++ b/tests/VObject/CliTest.php @@ -302,14 +302,14 @@ public function testVCard3040(): void { $inputStream = fopen('php://memory', 'r+'); - fwrite($inputStream, <<cli->stdin = $inputStream; @@ -341,14 +341,14 @@ public function testVCard4030(): void { $inputStream = fopen('php://memory', 'r+'); - fwrite($inputStream, <<cli->stdin = $inputStream; @@ -380,14 +380,14 @@ public function testVCard4021(): void { $inputStream = fopen('php://memory', 'r+'); - fwrite($inputStream, <<cli->stdin = $inputStream; @@ -402,15 +402,15 @@ public function testValidate(): void { $inputStream = fopen('php://memory', 'r+'); - fwrite($inputStream, <<cli->stdin = $inputStream; @@ -426,12 +426,12 @@ public function testValidateFail(): void { $inputStream = fopen('php://memory', 'r+'); - fwrite($inputStream, <<cli->stdin = $inputStream; @@ -466,12 +466,12 @@ public function testRepair(): void { $inputStream = fopen('php://memory', 'r+'); - fwrite($inputStream, <<cli->stdin = $inputStream; diff --git a/tests/VObject/Component/AvailableTest.php b/tests/VObject/Component/AvailableTest.php index 98202ffcb..68c9d90f0 100644 --- a/tests/VObject/Component/AvailableTest.php +++ b/tests/VObject/Component/AvailableTest.php @@ -13,26 +13,26 @@ class AvailableTest extends TestCase { public function testAvailableComponent(): void { - $vcal = <<AVAILABLE); } public function testGetEffectiveStartEnd(): void { - $vcal = <<VAVAILABILITY); @@ -27,14 +27,14 @@ public function testVAvailabilityComponent(): void public function testGetEffectiveStartEnd(): void { - $vcal = <<VAVAILABILITY->AVAILABLE); @@ -241,91 +241,91 @@ public function testRFCxxxSection3Part1AvailablePropRequired(): void { // UID, DTSTAMP and DTSTART are present. self::assertIsValid(Reader::read( - <<validate(); if ($validationResult) { - $messages = array_map(function ($item) { return $item['message']; }, $validationResult); + $messages = array_map(fn ($item) => $item['message'], $validationResult); $this->fail('Failed to assert that the supplied document is a valid document. Validation messages: '.implode(', ', $messages)); } self::assertEmpty($document->validate()); @@ -425,17 +425,17 @@ protected function assertIsNotValid(VObject\Document $document): void protected function template(array $properties) { return $this->_template( - <<_template( - <<sender = $mainComponent->ATTENDEE->getValue(); $message->senderName = isset($mainComponent->ATTENDEE['CN']) ? $mainComponent->ATTENDEE['CN']->getValue() : null; $message->recipient = $mainComponent->ORGANIZER->getValue(); - $message->recipientName = isset($mainComponent->ORGANIZER['CN']) ? $mainComponent->ORGANIZER['CN'] : null; + $message->recipientName = $mainComponent->ORGANIZER['CN'] ?? null; } $broker = new Broker(); diff --git a/tests/VObject/ITip/BrokerTimezoneInParseEventInfoWithoutMasterTest.php b/tests/VObject/ITip/BrokerTimezoneInParseEventInfoWithoutMasterTest.php index e3f18f15e..c0afe4578 100644 --- a/tests/VObject/ITip/BrokerTimezoneInParseEventInfoWithoutMasterTest.php +++ b/tests/VObject/ITip/BrokerTimezoneInParseEventInfoWithoutMasterTest.php @@ -70,7 +70,6 @@ public function testTimezoneInParseEventInfoWithoutMaster(): void $broker = new Broker(); $reflectionMethod = new \ReflectionMethod($broker, 'parseEventInfo'); - $reflectionMethod->setAccessible(true); $data = $reflectionMethod->invoke($broker, $calendar); self::assertInstanceOf('DateTimeZone', $data['timezone']); self::assertEquals('Europe/Minsk', $data['timezone']->getName()); diff --git a/tests/VObject/Property/TextTest.php b/tests/VObject/Property/TextTest.php index 0fa320bfe..aadbd4141 100644 --- a/tests/VObject/Property/TextTest.php +++ b/tests/VObject/Property/TextTest.php @@ -66,14 +66,14 @@ public function testSerializeQuotedPrintableFold(): void public function testValidateMinimumPropValue(): void { - $vcard = <<validate()); diff --git a/tests/VObject/ReaderTest.php b/tests/VObject/ReaderTest.php index 645ed1813..7b6c953fd 100644 --- a/tests/VObject/ReaderTest.php +++ b/tests/VObject/ReaderTest.php @@ -318,7 +318,7 @@ public function testReadForgiving(): void $caught = false; try { Reader::read(implode("\r\n", $data)); - } catch (ParseException $e) { + } catch (ParseException) { $caught = true; } @@ -348,7 +348,7 @@ public function testReadWithInvalidLine(): void $caught = false; try { Reader::read(implode("\r\n", $data)); - } catch (ParseException $e) { + } catch (ParseException) { $caught = true; } diff --git a/tests/VObject/Recur/EventIterator/HandleRDateExpandTest.php b/tests/VObject/Recur/EventIterator/HandleRDateExpandTest.php index 4f434dd2a..aca783d3c 100644 --- a/tests/VObject/Recur/EventIterator/HandleRDateExpandTest.php +++ b/tests/VObject/Recur/EventIterator/HandleRDateExpandTest.php @@ -51,7 +51,7 @@ public function testExpand(): void new \DateTimeImmutable('2015-10-20', $utc), ]; - $result = array_map(function ($ev) {return $ev->DTSTART->getDateTime(); }, $result); + $result = array_map(fn ($ev) => $ev->DTSTART->getDateTime(), $result); self::assertEquals($expected, $result); } } diff --git a/tests/VObject/Splitter/ICalendarTest.php b/tests/VObject/Splitter/ICalendarTest.php index f5428e937..0688e378b 100644 --- a/tests/VObject/Splitter/ICalendarTest.php +++ b/tests/VObject/Splitter/ICalendarTest.php @@ -169,7 +169,7 @@ public function testICalendarImportEventWithoutUID(): void if ($messages) { $messages = array_map( - function ($item) { return $item['message']; }, + fn ($item) => $item['message'], $messages ); $this->fail('Validation errors: '.implode("\n", $messages)); diff --git a/tests/VObject/Splitter/VCardTest.php b/tests/VObject/Splitter/VCardTest.php index 7ebb07b95..b4d6b0a22 100644 --- a/tests/VObject/Splitter/VCardTest.php +++ b/tests/VObject/Splitter/VCardTest.php @@ -10,7 +10,7 @@ class VCardTest extends TestCase public function createStream($data) { $stream = fopen('php://memory', 'r+'); - fwrite($stream, $data); + fwrite($stream, (string) $data); rewind($stream); return $stream; diff --git a/tests/VObject/TimeZoneUtilTest.php b/tests/VObject/TimeZoneUtilTest.php index 462218e9b..bc45de8e5 100644 --- a/tests/VObject/TimeZoneUtilTest.php +++ b/tests/VObject/TimeZoneUtilTest.php @@ -20,7 +20,7 @@ public function testCorrectTZ(string $timezoneName): void $tz = new \DateTimeZone($timezoneName); self::assertInstanceOf('DateTimeZone', $tz); } catch (\Exception $e) { - if (false !== strpos($e->getMessage(), 'Unknown or bad timezone')) { + if (str_contains($e->getMessage(), 'Unknown or bad timezone')) { $this->markTestSkipped($timezoneName.' is not (yet) supported in this PHP version. Update pecl/timezonedb'); } else { throw $e; @@ -39,9 +39,7 @@ public function getMapping(): array // PHPUNit requires an array of arrays return array_map( - function ($value) { - return [$value]; - }, + fn ($value) => [$value], $map ); } @@ -202,9 +200,7 @@ public function getPHPTimeZoneIdentifiers(): array { // PHPUNit requires an array of arrays return array_map( - function ($value) { - return [$value]; - }, + fn ($value) => [$value], \DateTimeZone::listIdentifiers() ); } @@ -213,9 +209,7 @@ public function getPHPTimeZoneBCIdentifiers(): array { // PHPUNit requires an array of arrays return array_map( - function ($value) { - return [$value]; - }, + fn ($value) => [$value], include __DIR__.'/../../lib/timezonedata/php-bc.php' ); } diff --git a/tests/VObject/VCardConverterTest.php b/tests/VObject/VCardConverterTest.php index bb52be8a9..cff394fe5 100644 --- a/tests/VObject/VCardConverterTest.php +++ b/tests/VObject/VCardConverterTest.php @@ -14,21 +14,21 @@ class VCardConverterTest extends TestCase */ public function testConvert30to40(): void { - $input = <<expectException(\InvalidArgumentException::class); - $input = <<expectException(\InvalidArgumentException::class); - $input = << + + - - - ../lib/ - - . + + + ../lib/ + + From 7de87c73e964e0ded3d409d797c1dc7bfedf664c Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Thu, 11 Jun 2026 19:37:09 +0930 Subject: [PATCH 02/23] test: fix unit test things --- tests/VObject/Component/VAlarmTest.php | 2 +- tests/VObject/Component/VCalendarTest.php | 2 +- tests/VObject/Component/VCardTest.php | 2 +- tests/VObject/Component/VEventTest.php | 2 +- tests/VObject/Component/VJournalTest.php | 2 +- tests/VObject/Component/VTodoTest.php | 2 +- tests/VObject/ComponentTest.php | 2 +- tests/VObject/DateTimeParserTest.php | 2 +- tests/VObject/Parser/MimeDirTest.php | 4 +- .../Property/ICalendar/CalAddressTest.php | 2 +- .../Property/VCard/DateAndOrTimeTest.php | 2 +- tests/VObject/Recur/RRuleIteratorTest.php | 114 +++++++++--------- tests/VObject/TimeZoneUtilTest.php | 6 +- .../FindFromTimezoneMapTest.php | 2 +- 14 files changed, 73 insertions(+), 73 deletions(-) diff --git a/tests/VObject/Component/VAlarmTest.php b/tests/VObject/Component/VAlarmTest.php index a67991ff4..33da9410c 100644 --- a/tests/VObject/Component/VAlarmTest.php +++ b/tests/VObject/Component/VAlarmTest.php @@ -16,7 +16,7 @@ public function testInTimeRange(VAlarm $valarm, \DateTime $start, \DateTime $end self::assertEquals($outcome, $valarm->isInTimeRange($start, $end)); } - public function timeRangeTestData(): array + public static function timeRangeTestData(): array { $tests = []; diff --git a/tests/VObject/Component/VCalendarTest.php b/tests/VObject/Component/VCalendarTest.php index 847e17d02..ea1fe61b4 100644 --- a/tests/VObject/Component/VCalendarTest.php +++ b/tests/VObject/Component/VCalendarTest.php @@ -31,7 +31,7 @@ public function testExpand(string $input, string $output, string $timeZone = 'UT self::assertVObjectEqualsVObject($output, $vcal->serialize()); } - public function expandData(): array + public static function expandData(): array { $tests = []; diff --git a/tests/VObject/Component/VCardTest.php b/tests/VObject/Component/VCardTest.php index ff4979ed4..1ae83b81e 100644 --- a/tests/VObject/Component/VCardTest.php +++ b/tests/VObject/Component/VCardTest.php @@ -31,7 +31,7 @@ public function testValidate(string $input, array $expectedWarnings, string $exp ); } - public function validateData(): array + public static function validateData(): array { $tests = []; diff --git a/tests/VObject/Component/VEventTest.php b/tests/VObject/Component/VEventTest.php index 5944ea574..9c79c245d 100644 --- a/tests/VObject/Component/VEventTest.php +++ b/tests/VObject/Component/VEventTest.php @@ -14,7 +14,7 @@ public function testInTimeRange(VEvent $vevent, \DateTime $start, \DateTime $end self::assertEquals($outcome, $vevent->isInTimeRange($start, $end)); } - public function timeRangeTestData(): array + public static function timeRangeTestData(): array { $tests = []; diff --git a/tests/VObject/Component/VJournalTest.php b/tests/VObject/Component/VJournalTest.php index 98f478896..d0419253d 100644 --- a/tests/VObject/Component/VJournalTest.php +++ b/tests/VObject/Component/VJournalTest.php @@ -68,7 +68,7 @@ public function testValidateBroken(): void ); } - public function timeRangeTestData(): array + public static function timeRangeTestData(): array { $calendar = new VCalendar(); diff --git a/tests/VObject/Component/VTodoTest.php b/tests/VObject/Component/VTodoTest.php index 899d93982..5bd28c6fe 100644 --- a/tests/VObject/Component/VTodoTest.php +++ b/tests/VObject/Component/VTodoTest.php @@ -15,7 +15,7 @@ public function testInTimeRange(VTodo $vtodo, \DateTime $start, \DateTime $end, self::assertEquals($outcome, $vtodo->isInTimeRange($start, $end)); } - public function timeRangeTestData(): array + public static function timeRangeTestData(): array { $tests = []; diff --git a/tests/VObject/ComponentTest.php b/tests/VObject/ComponentTest.php index c48480983..8e930a564 100644 --- a/tests/VObject/ComponentTest.php +++ b/tests/VObject/ComponentTest.php @@ -546,7 +546,7 @@ public function testValidateRepairShouldDeduplicatePropertiesWhenValuesAreEqual( self::assertCount(1, $component->GIR); } - public function ruleData(): array + public static function ruleData(): array { return [ [[], 2], diff --git a/tests/VObject/DateTimeParserTest.php b/tests/VObject/DateTimeParserTest.php index cdf0fe990..2ae7a6897 100644 --- a/tests/VObject/DateTimeParserTest.php +++ b/tests/VObject/DateTimeParserTest.php @@ -189,7 +189,7 @@ public function testBadVCardTime(): void DateTimeParser::parseVCardTime('23:12:166'); } - public function vcardDates(): array + public static function vcardDates(): array { return [ [ diff --git a/tests/VObject/Parser/MimeDirTest.php b/tests/VObject/Parser/MimeDirTest.php index 2fb436d0f..4d27843be 100644 --- a/tests/VObject/Parser/MimeDirTest.php +++ b/tests/VObject/Parser/MimeDirTest.php @@ -101,7 +101,7 @@ public function testDecodeUnsupportedInlineCharset(): void $mimeDir->parse($vcard); } - public function provideEmptyParserInput(): array + public static function provideEmptyParserInput(): array { return [ [null, 'No input provided to parse'], @@ -212,7 +212,7 @@ public function testBrokenMultilineContentDoesBreakImport($vcalendar): void $mimeDir->parse($vcalendar); } - public function provideBrokenVCalendar(): array + public static function provideBrokenVCalendar(): array { return [[<<getJsonValue()); } - public function dates(): array + public static function dates(): array { return [ [ diff --git a/tests/VObject/Recur/RRuleIteratorTest.php b/tests/VObject/Recur/RRuleIteratorTest.php index ea049a170..639949209 100644 --- a/tests/VObject/Recur/RRuleIteratorTest.php +++ b/tests/VObject/Recur/RRuleIteratorTest.php @@ -43,11 +43,11 @@ public function test2HourlyOnDstTransition(string $start, array $expected): void ); } - public function dst2HourlyTransitionProvider(): iterable + public static function dst2HourlyTransitionProvider(): iterable { yield 'On transition start' => [ - 'Start' => '2023-03-26 00:00:00', - 'Expected' => [ + 'start' => '2023-03-26 00:00:00', + 'expected' => [ '2023-03-26 00:00:00', '2023-03-26 03:00:00', '2023-03-26 04:00:00', @@ -56,8 +56,8 @@ public function dst2HourlyTransitionProvider(): iterable ], ]; yield 'During transition' => [ - 'Start' => '2023-03-26 00:15:00', - 'Expected' => [ + 'start' => '2023-03-26 00:15:00', + 'expected' => [ '2023-03-26 00:15:00', '2023-03-26 03:15:00', '2023-03-26 04:15:00', @@ -66,8 +66,8 @@ public function dst2HourlyTransitionProvider(): iterable ], ]; yield 'On transition end' => [ - 'Start' => '2023-03-26 01:00:00', - 'Expected' => [ + 'start' => '2023-03-26 01:00:00', + 'expected' => [ '2023-03-26 01:00:00', '2023-03-26 03:00:00', '2023-03-26 05:00:00', @@ -76,8 +76,8 @@ public function dst2HourlyTransitionProvider(): iterable ], ]; yield 'After transition end' => [ - 'Start' => '2023-03-26 01:15:00', - 'Expected' => [ + 'start' => '2023-03-26 01:15:00', + 'expected' => [ '2023-03-26 01:15:00', '2023-03-26 03:15:00', '2023-03-26 05:15:00', @@ -101,11 +101,11 @@ public function testHourlyOnDstTransition(string $start, array $expected): void ); } - public function dst6HourlyTransitionProvider(): iterable + public static function dst6HourlyTransitionProvider(): iterable { yield 'On transition start' => [ - 'Start' => '2023-03-25 20:00:00', - 'Expected' => [ + 'start' => '2023-03-25 20:00:00', + 'expected' => [ '2023-03-25 20:00:00', '2023-03-26 03:00:00', '2023-03-26 08:00:00', @@ -114,8 +114,8 @@ public function dst6HourlyTransitionProvider(): iterable ], ]; yield 'During transition' => [ - 'Start' => '2023-03-25 20:15:00', - 'Expected' => [ + 'start' => '2023-03-25 20:15:00', + 'expected' => [ '2023-03-25 20:15:00', '2023-03-26 03:15:00', '2023-03-26 08:15:00', @@ -124,8 +124,8 @@ public function dst6HourlyTransitionProvider(): iterable ], ]; yield 'On transition end' => [ - 'Start' => '2023-03-25 21:00:00', - 'Expected' => [ + 'start' => '2023-03-25 21:00:00', + 'expected' => [ '2023-03-25 21:00:00', '2023-03-26 03:00:00', '2023-03-26 09:00:00', @@ -134,8 +134,8 @@ public function dst6HourlyTransitionProvider(): iterable ], ]; yield 'After transition end' => [ - 'Start' => '2023-03-25 21:15:00', - 'Expected' => [ + 'start' => '2023-03-25 21:15:00', + 'expected' => [ '2023-03-25 21:15:00', '2023-03-26 03:15:00', '2023-03-26 09:15:00', @@ -292,11 +292,11 @@ public function testDailyOnDstTransition(string $start, array $expected): void ); } - public function dstDailyTransitionProvider(): iterable + public static function dstDailyTransitionProvider(): iterable { yield 'On transition start' => [ - 'Start' => '2023-03-24 02:00:00', - 'Expected' => [ + 'start' => '2023-03-24 02:00:00', + 'expected' => [ '2023-03-24 02:00:00', '2023-03-25 02:00:00', '2023-03-26 03:00:00', @@ -305,8 +305,8 @@ public function dstDailyTransitionProvider(): iterable ], ]; yield 'During transition' => [ - 'Start' => '2023-03-24 02:15:00', - 'Expected' => [ + 'start' => '2023-03-24 02:15:00', + 'expected' => [ '2023-03-24 02:15:00', '2023-03-25 02:15:00', '2023-03-26 03:15:00', @@ -315,8 +315,8 @@ public function dstDailyTransitionProvider(): iterable ], ]; yield 'On transition end' => [ - 'Start' => '2023-03-24 03:00:00', - 'Expected' => [ + 'start' => '2023-03-24 03:00:00', + 'expected' => [ '2023-03-24 03:00:00', '2023-03-25 03:00:00', '2023-03-26 03:00:00', @@ -325,8 +325,8 @@ public function dstDailyTransitionProvider(): iterable ], ]; yield 'After transition end' => [ - 'Start' => '2023-03-24 03:15:00', - 'Expected' => [ + 'start' => '2023-03-24 03:15:00', + 'expected' => [ '2023-03-24 03:15:00', '2023-03-25 03:15:00', '2023-03-26 03:15:00', @@ -499,11 +499,11 @@ public function testWeeklyOnDstTransition(string $start, array $expected): void ); } - public function dstWeeklyTransitionProvider(): iterable + public static function dstWeeklyTransitionProvider(): iterable { yield 'On transition start' => [ - 'Start' => '2023-03-12 02:00:00', - 'Expected' => [ + 'start' => '2023-03-12 02:00:00', + 'expected' => [ '2023-03-12 02:00:00', '2023-03-19 02:00:00', '2023-03-26 03:00:00', @@ -512,8 +512,8 @@ public function dstWeeklyTransitionProvider(): iterable ], ]; yield 'During transition' => [ - 'Start' => '2023-03-12 02:15:00', - 'Expected' => [ + 'start' => '2023-03-12 02:15:00', + 'expected' => [ '2023-03-12 02:15:00', '2023-03-19 02:15:00', '2023-03-26 03:15:00', @@ -522,8 +522,8 @@ public function dstWeeklyTransitionProvider(): iterable ], ]; yield 'On transition end' => [ - 'Start' => '2023-03-12 03:00:00', - 'Expected' => [ + 'start' => '2023-03-12 03:00:00', + 'expected' => [ '2023-03-12 03:00:00', '2023-03-19 03:00:00', '2023-03-26 03:00:00', @@ -532,8 +532,8 @@ public function dstWeeklyTransitionProvider(): iterable ], ]; yield 'After transition end' => [ - 'Start' => '2023-03-12 03:15:00', - 'Expected' => [ + 'start' => '2023-03-12 03:15:00', + 'expected' => [ '2023-03-12 03:15:00', '2023-03-19 03:15:00', '2023-03-26 03:15:00', @@ -751,11 +751,11 @@ public function testMonthlyOnDstTransition(string $start, array $expected): void ); } - public function dstMonthlyTransitionProvider(): iterable + public static function dstMonthlyTransitionProvider(): iterable { yield 'On transition start' => [ - 'Start' => '2023-01-26 02:00:00', - 'Expected' => [ + 'start' => '2023-01-26 02:00:00', + 'expected' => [ '2023-01-26 02:00:00', '2023-02-26 02:00:00', '2023-03-26 03:00:00', @@ -764,8 +764,8 @@ public function dstMonthlyTransitionProvider(): iterable ], ]; yield 'During transition' => [ - 'Start' => '2023-01-26 02:15:00', - 'Expected' => [ + 'start' => '2023-01-26 02:15:00', + 'expected' => [ '2023-01-26 02:15:00', '2023-02-26 02:15:00', '2023-03-26 03:15:00', @@ -774,8 +774,8 @@ public function dstMonthlyTransitionProvider(): iterable ], ]; yield 'On transition end' => [ - 'Start' => '2023-01-26 03:00:00', - 'Expected' => [ + 'start' => '2023-01-26 03:00:00', + 'expected' => [ '2023-01-26 03:00:00', '2023-02-26 03:00:00', '2023-03-26 03:00:00', @@ -784,8 +784,8 @@ public function dstMonthlyTransitionProvider(): iterable ], ]; yield 'After transition end' => [ - 'Start' => '2023-01-26 03:15:00', - 'Expected' => [ + 'start' => '2023-01-26 03:15:00', + 'expected' => [ '2023-01-26 03:15:00', '2023-02-26 03:15:00', '2023-03-26 03:15:00', @@ -794,8 +794,8 @@ public function dstMonthlyTransitionProvider(): iterable ], ]; yield 'During transition on 31st day of month' => [ - 'Start' => '2024-01-31 02:15:00', - 'Expected' => [ + 'start' => '2024-01-31 02:15:00', + 'expected' => [ '2024-01-31 02:15:00', '2024-03-31 03:15:00', '2024-05-31 02:15:00', @@ -1160,11 +1160,11 @@ public function testYearlyOnDstTransition(string $start, array $expected): void ); } - public function dstYearlyTransitionProvider(): iterable + public static function dstYearlyTransitionProvider(): iterable { yield 'On transition start' => [ - 'Start' => '2021-03-26 02:00:00', - 'Expected' => [ + 'start' => '2021-03-26 02:00:00', + 'expected' => [ '2021-03-26 02:00:00', '2022-03-26 02:00:00', '2023-03-26 03:00:00', @@ -1173,8 +1173,8 @@ public function dstYearlyTransitionProvider(): iterable ], ]; yield 'During transition' => [ - 'Start' => '2021-03-26 02:15:00', - 'Expected' => [ + 'start' => '2021-03-26 02:15:00', + 'expected' => [ '2021-03-26 02:15:00', '2022-03-26 02:15:00', '2023-03-26 03:15:00', @@ -1183,8 +1183,8 @@ public function dstYearlyTransitionProvider(): iterable ], ]; yield 'On transition end' => [ - 'Start' => '2021-03-26 03:00:00', - 'Expected' => [ + 'start' => '2021-03-26 03:00:00', + 'expected' => [ '2021-03-26 03:00:00', '2022-03-26 03:00:00', '2023-03-26 03:00:00', @@ -1193,8 +1193,8 @@ public function dstYearlyTransitionProvider(): iterable ], ]; yield 'After transition end' => [ - 'Start' => '2021-03-26 03:15:00', - 'Expected' => [ + 'start' => '2021-03-26 03:15:00', + 'expected' => [ '2021-03-26 03:15:00', '2022-03-26 03:15:00', '2023-03-26 03:15:00', @@ -1427,7 +1427,7 @@ public function testYearlyStartDateNotOnRRuleList(string $rule, string $start, a $this->parse($rule, $start, $expected); } - public function yearlyStartDateNotOnRRuleListProvider(): array + public static function yearlyStartDateNotOnRRuleListProvider(): array { return [ [ diff --git a/tests/VObject/TimeZoneUtilTest.php b/tests/VObject/TimeZoneUtilTest.php index bc45de8e5..01d5a7bf6 100644 --- a/tests/VObject/TimeZoneUtilTest.php +++ b/tests/VObject/TimeZoneUtilTest.php @@ -28,7 +28,7 @@ public function testCorrectTZ(string $timezoneName): void } } - public function getMapping(): array + public static function getMapping(): array { $map = array_merge( include __DIR__.'/../../lib/timezonedata/windowszones.php', @@ -196,7 +196,7 @@ public function testTimeZoneBCIdentifiers(string $tzid): void self::assertEquals($ex->getName(), $tz->getName()); } - public function getPHPTimeZoneIdentifiers(): array + public static function getPHPTimeZoneIdentifiers(): array { // PHPUNit requires an array of arrays return array_map( @@ -205,7 +205,7 @@ public function getPHPTimeZoneIdentifiers(): array ); } - public function getPHPTimeZoneBCIdentifiers(): array + public static function getPHPTimeZoneBCIdentifiers(): array { // PHPUNit requires an array of arrays return array_map( diff --git a/tests/VObject/TimezoneGuesser/FindFromTimezoneMapTest.php b/tests/VObject/TimezoneGuesser/FindFromTimezoneMapTest.php index abda23823..05ebd267c 100644 --- a/tests/VObject/TimezoneGuesser/FindFromTimezoneMapTest.php +++ b/tests/VObject/TimezoneGuesser/FindFromTimezoneMapTest.php @@ -23,7 +23,7 @@ public function testUpdatedTimezonesResolve(string $mapKey, string $expectedOlso self::assertSame($expectedOlson, $tz->getName()); } - public function updatedTimezoneProvider(): array + public static function updatedTimezoneProvider(): array { return [ // windowszones.php From 5a4792f01e958f06c7b9b443f784bbe1cc22ceab Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Sun, 14 Jun 2026 13:19:36 +0930 Subject: [PATCH 03/23] fix: throw InvalidDataException if Cli tool cannot open input file And adjust the related unit test testConvertDefaultFormats so that it handles the PHP warning that is emitted by fopen. The Cli code catches InvalidDataException and exits with status 2, which is what was always expected by the unit test code. So this should preserve the real behavior. The test changes are necessary because PHPunit11 now exposes the PHP warning coming from fopen. So we catch and expect that in an error handler in the test. --- lib/Cli.php | 3 +++ tests/VObject/CliTest.php | 52 +++++++++++++++++++++++++-------------- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/lib/Cli.php b/lib/Cli.php index 38db47334..f5a150dea 100644 --- a/lib/Cli.php +++ b/lib/Cli.php @@ -594,6 +594,9 @@ protected function readInput(): ?Document if (!$this->parser) { if ('-' !== $this->inputPath) { $this->stdin = fopen($this->inputPath, 'r'); + if (false === $this->stdin) { + throw new InvalidDataException('Cannot open "'.$this->inputPath); + } } if ('mimedir' === $this->inputFormat) { diff --git a/tests/VObject/CliTest.php b/tests/VObject/CliTest.php index 6801593b2..2789ba050 100644 --- a/tests/VObject/CliTest.php +++ b/tests/VObject/CliTest.php @@ -272,30 +272,46 @@ public function testConvertMimeDir(): void ); } - public function testConvertDefaultFormats(): void + public static function provideFormats(): array { - $outputFile = $this->sabreTempDir.'bar.json'; - - self::assertEquals( - 2, - $this->cli->main(['vobject', 'convert', 'foo.json', $outputFile]) - ); - - self::assertEquals('json', $this->cli->inputFormat); - self::assertEquals('json', $this->cli->format); + return [ + ['foo.json', 'bar.json', 'json'], + ['foo.ics', 'bar.ics', 'mimedir'], + ]; } - public function testConvertDefaultFormats2(): void + /** + * @dataProvider provideFormats + */ + public function testConvertDefaultFormats($inputFilename, $outputFilename, $format): void { - $outputFile = $this->sabreTempDir.'bar.ics'; + $triggeredWarning = null; - self::assertEquals( - 2, - $this->cli->main(['vobject', 'convert', 'foo.ics', $outputFile]) - ); + // Set an error handler to catch the native PHP Warning + set_error_handler(function (int $errno, string $errstr) use (&$triggeredWarning) { + $triggeredWarning = $errstr; + + return true; // Prevents the error from propagating further + }, E_WARNING); + + $outputFile = $this->sabreTempDir.$outputFilename; - self::assertEquals('mimedir', $this->cli->inputFormat); - self::assertEquals('mimedir', $this->cli->format); + try { + self::assertEquals( + 2, + $this->cli->main(['vobject', 'convert', $inputFilename, $outputFile]) + ); + } finally { + restore_error_handler(); + } + + self::assertNotNull($triggeredWarning, 'A PHP warning was expected but never triggered.'); + $this->assertStringContainsString( + "fopen($inputFilename): Failed to open stream: No such file or directory", + $triggeredWarning + ); + self::assertEquals($format, $this->cli->inputFormat); + self::assertEquals($format, $this->cli->format); } public function testVCard3040(): void From 38acfdf41c905d7840b3f7f67d5455bd06c5397a Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Sun, 14 Jun 2026 13:45:11 +0930 Subject: [PATCH 04/23] test: fix PHPUnitAssertions to pass phpstan --- lib/PHPUnitAssertions.php | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/PHPUnitAssertions.php b/lib/PHPUnitAssertions.php index a201379be..ebf9956c6 100644 --- a/lib/PHPUnitAssertions.php +++ b/lib/PHPUnitAssertions.php @@ -41,7 +41,7 @@ public function assertVObjectEqualsVObject($expected, $actual, string $message = $input = Reader::read($input); } if (!$input instanceof Component) { - $this->fail('Input must be a string, stream or VObject component'); + $this::fail('Input must be a string, stream or VObject component'); } unset($input->PRODID); if ($input instanceof Component\VCalendar && 'GREGORIAN' === (string) $input->CALSCALE) { @@ -51,23 +51,26 @@ public function assertVObjectEqualsVObject($expected, $actual, string $message = return $input; }; - $expected = $getObj($expected)->serialize(); - $actual = $getObj($actual)->serialize(); + /** + * @var string $expectedSerialized + */ + $expectedSerialized = $getObj($expected)->serialize(); + $actualSerialized = $getObj($actual)->serialize(); // Finding wildcards in expected. - preg_match_all('|^([A-Z]+):\\*\\*ANY\\*\\*\r$|m', (string) $expected, $matches, PREG_SET_ORDER); + preg_match_all('|^([A-Z]+):\\*\\*ANY\\*\\*\r$|m', $expectedSerialized, $matches, PREG_SET_ORDER); foreach ($matches as $match) { - $actual = preg_replace( + $actualSerialized = preg_replace( '|^'.preg_quote($match[1], '|').':(.*)\r$|m', $match[1].':**ANY**'."\r", - (string) $actual + (string) $actualSerialized ); } - $this->assertEquals( - $expected, - $actual, + $this::assertEquals( + $expectedSerialized, + $actualSerialized, $message ); } From 67504c4e0ea1b3d8de07709b2a2265355a57b9f4 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Sun, 14 Jun 2026 13:59:48 +0930 Subject: [PATCH 05/23] test: adjust test code to pass PHPstan --- tests/VObject/CliTest.php | 4 +- tests/VObject/Component/VAvailabilityTest.php | 2 +- tests/VObject/ITip/BrokerTester.php | 6 ++- tests/VObject/Parser/MimeDirTest.php | 1 + tests/VObject/Parser/XmlTest.php | 2 +- .../Property/ICalendar/DateTimeTest.php | 2 +- .../VObject/Recur/EventIterator/MainTest.php | 44 +++++++++---------- tests/VObject/Splitter/ICalendarTest.php | 4 +- tests/VObject/Splitter/VCardTest.php | 1 + tests/VObject/TimeZoneUtilTest.php | 6 +-- 10 files changed, 39 insertions(+), 33 deletions(-) diff --git a/tests/VObject/CliTest.php b/tests/VObject/CliTest.php index 2789ba050..4b3671778 100644 --- a/tests/VObject/CliTest.php +++ b/tests/VObject/CliTest.php @@ -154,7 +154,7 @@ public function testConvertJson(): void public function testConvertJCardPretty(): void { if (version_compare(PHP_VERSION, '5.4.0') < 0) { - $this->markTestSkipped('This test required PHP 5.4.0'); + $this::markTestSkipped('This test required PHP 5.4.0'); } $inputStream = fopen('php://memory', 'r+'); @@ -306,7 +306,7 @@ public function testConvertDefaultFormats($inputFilename, $outputFilename, $form } self::assertNotNull($triggeredWarning, 'A PHP warning was expected but never triggered.'); - $this->assertStringContainsString( + $this::assertStringContainsString( "fopen($inputFilename): Failed to open stream: No such file or directory", $triggeredWarning ); diff --git a/tests/VObject/Component/VAvailabilityTest.php b/tests/VObject/Component/VAvailabilityTest.php index c2ad50da8..e47653910 100644 --- a/tests/VObject/Component/VAvailabilityTest.php +++ b/tests/VObject/Component/VAvailabilityTest.php @@ -412,7 +412,7 @@ protected function assertIsValid(VObject\Document $document): void $validationResult = $document->validate(); if ($validationResult) { $messages = array_map(fn ($item) => $item['message'], $validationResult); - $this->fail('Failed to assert that the supplied document is a valid document. Validation messages: '.implode(', ', $messages)); + $this::fail('Failed to assert that the supplied document is a valid document. Validation messages: '.implode(', ', $messages)); } self::assertEmpty($document->validate()); } diff --git a/tests/VObject/ITip/BrokerTester.php b/tests/VObject/ITip/BrokerTester.php index c46ef4bb4..43eb91814 100644 --- a/tests/VObject/ITip/BrokerTester.php +++ b/tests/VObject/ITip/BrokerTester.php @@ -40,6 +40,7 @@ public function parse($oldMessage, $newMessage, array $expected = [], string $cu $message->message->serialize() ); } else { + // @phpstan-ignore property.dynamicName self::assertEquals($val, $message->$key); } } @@ -59,8 +60,9 @@ public function process($input, $existingObject = null, $expected = false): void $vcal = Reader::read($input); $mainComponent = new VEvent($vcal, 'VEVENT'); - foreach ($vcal->getComponents() as $mainComponent) { - if ('VEVENT' === $mainComponent->name) { + foreach ($vcal->getComponents() as $nextComponent) { + if ('VEVENT' === $nextComponent->name) { + $mainComponent = $nextComponent; break; } } diff --git a/tests/VObject/Parser/MimeDirTest.php b/tests/VObject/Parser/MimeDirTest.php index 4d27843be..a265b4216 100644 --- a/tests/VObject/Parser/MimeDirTest.php +++ b/tests/VObject/Parser/MimeDirTest.php @@ -274,6 +274,7 @@ public function testPropertyName0(): void $mimeDir = new MimeDir(); $vevent = $mimeDir->parse($iCal); + // @phpstan-ignore property.dynamicName self::assertEquals('test', $vevent->VEVENT->{0}->getValue()); } diff --git a/tests/VObject/Parser/XmlTest.php b/tests/VObject/Parser/XmlTest.php index 6491d087b..1ad36474d 100644 --- a/tests/VObject/Parser/XmlTest.php +++ b/tests/VObject/Parser/XmlTest.php @@ -2704,6 +2704,6 @@ protected function assertXMLReflexivelyEqualsToMimeDir(string $xml, string $mime self::assertXMLEqualsToMimeDir($xml, $mimedir); $component = VObject\Reader::read($mimedir); - self::assertXmlStringEqualsXmlString($xml, VObject\Writer::writeXML($component)); + self::assertXmlStringEqualsXmlString($xml, VObject\Writer::writeXml($component)); } } diff --git a/tests/VObject/Property/ICalendar/DateTimeTest.php b/tests/VObject/Property/ICalendar/DateTimeTest.php index 5a70b2641..f0728d3ed 100644 --- a/tests/VObject/Property/ICalendar/DateTimeTest.php +++ b/tests/VObject/Property/ICalendar/DateTimeTest.php @@ -102,7 +102,7 @@ public function testSetDateTimeDATE(): void self::assertEquals('19850704', (string) $elem); self::assertNull($elem['TZID']); - self::assertEquals('DATE', (string) $elem['VALUE']); + self::assertEquals('DATE', $elem['VALUE']); self::assertFalse($elem->hasTime()); } diff --git a/tests/VObject/Recur/EventIterator/MainTest.php b/tests/VObject/Recur/EventIterator/MainTest.php index bce4bcd92..1899feea3 100644 --- a/tests/VObject/Recur/EventIterator/MainTest.php +++ b/tests/VObject/Recur/EventIterator/MainTest.php @@ -33,7 +33,7 @@ public function testValues(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); self::assertTrue($it->isInfinite()); } @@ -58,7 +58,7 @@ public function testInvalidFreq(): void $ev->add($dtStart); $vcal->add($ev); - new EventIterator($vcal, (string) $ev->UID); + new EventIterator($vcal, $ev->UID); } /** @@ -252,7 +252,7 @@ public function testDailyByDayByHour(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); // Grabbing the next 12 items $max = 12; @@ -304,7 +304,7 @@ public function testDailyByHour(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); // Grabbing the next 12 items $max = 12; @@ -356,7 +356,7 @@ public function testDailyByDay(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); // Grabbing the next 12 items $max = 12; @@ -408,7 +408,7 @@ public function testWeekly(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); // Max is to prevent overflow $max = 12; @@ -458,7 +458,7 @@ public function testWeeklyByDayByHour(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); // Grabbing the next 12 items $max = 15; @@ -513,7 +513,7 @@ public function testWeeklyByDaySpecificHour(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); // Grabbing the next 12 items $max = 12; @@ -565,7 +565,7 @@ public function testWeeklyByDay(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); // Grabbing the next 12 items $max = 12; @@ -617,7 +617,7 @@ public function testMonthly(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); $max = 14; $result = []; @@ -661,7 +661,7 @@ public function testMonthlyEndOfMonth(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); $max = 14; $result = []; @@ -719,7 +719,7 @@ public function testMonthlyByMonthDay(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); $max = 14; $result = []; @@ -772,7 +772,7 @@ public function testMonthlyByDay(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); $max = 20; $result = []; @@ -827,7 +827,7 @@ public function testMonthlyByDayByMonthDay(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); $max = 20; $result = []; @@ -876,7 +876,7 @@ public function testMonthlyByDayBySetPos(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); $max = 20; $result = []; @@ -930,7 +930,7 @@ public function testYearly(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); $max = 20; $result = []; @@ -979,7 +979,7 @@ public function testYearlyLeapYear(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); $max = 20; $result = []; @@ -1021,7 +1021,7 @@ public function testYearlyByMonth(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); $max = 20; $result = []; @@ -1068,7 +1068,7 @@ public function testYearlyByMonthByDay(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); $max = 20; $result = []; @@ -1115,7 +1115,7 @@ public function testFastForward(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); // The idea is that we're fast-forwarding too far in the future, so // there will be no results left. @@ -1157,7 +1157,7 @@ public function testFastForwardAllDayEventThatStopAtTheStartTime(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); $it->fastForward(new \DateTimeImmutable('2011-04-05T000000', new \DateTimeZone('UTC'))); @@ -1199,7 +1199,7 @@ public function testComplexExclusions(): void $vcal->add($ev); - $it = new EventIterator($vcal, (string) $ev->UID); + $it = new EventIterator($vcal, $ev->UID); $max = 20; $result = []; diff --git a/tests/VObject/Splitter/ICalendarTest.php b/tests/VObject/Splitter/ICalendarTest.php index 0688e378b..775496dc5 100644 --- a/tests/VObject/Splitter/ICalendarTest.php +++ b/tests/VObject/Splitter/ICalendarTest.php @@ -94,6 +94,7 @@ public function testICalendarImportInvalidEvent(): void public function testICalendarImportMultipleValidEvents(): void { + $event = []; $event[] = << $item['message'], $messages ); - $this->fail('Validation errors: '.implode("\n", $messages)); + $this::fail('Validation errors: '.implode("\n", $messages)); } else { self::assertEquals([], $messages); } @@ -217,6 +218,7 @@ public function testICalendarImportMultipleVTIMEZONESAndMultipleValidEvents(): v END:VTIMEZONE EOT; + $event = []; $event[] = <<expectException(ParseException::class); + $event = []; $event[] = <<getMessage(), 'Unknown or bad timezone')) { - $this->markTestSkipped($timezoneName.' is not (yet) supported in this PHP version. Update pecl/timezonedb'); + $this::markTestSkipped($timezoneName.' is not (yet) supported in this PHP version. Update pecl/timezonedb'); } else { throw $e; } @@ -187,8 +187,8 @@ public function testTimeZoneBCIdentifiers(string $tzid): void * that should be released in Feb 2023. */ $versionOfPHP = \phpversion(); - if ((('8.1.14' == $versionOfPHP) || ('8.2.1' == $versionOfPHP)) && \str_contains($tzid, '+')) { - $this->markTestSkipped("Timezone ids containing '+' do not work on PHP $versionOfPHP"); + if ((('8.1.14' === $versionOfPHP) || ('8.2.1' === $versionOfPHP)) && \str_contains($tzid, '+')) { + $this::markTestSkipped("Timezone ids containing '+' do not work on PHP $versionOfPHP"); } $tz = TimeZoneUtil::getTimeZone($tzid); $ex = new \DateTimeZone($tzid); From 2eca60cd99c0f2bf77055e096bfa64c061bef52e Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Sun, 14 Jun 2026 16:52:47 +0930 Subject: [PATCH 06/23] fix: adjust code so that phpstan passes --- lib/BirthdayCalendarGenerator.php | 2 +- lib/Cli.php | 3 +- lib/Component.php | 4 +- lib/Component/VAlarm.php | 2 +- lib/Component/VCalendar.php | 2 +- lib/Component/VCard.php | 6 +- lib/DateTimeParser.php | 6 +- lib/ITip/Broker.php | 30 ++++++---- lib/Parameter.php | 3 +- lib/Parser/MimeDir.php | 6 +- lib/Parser/XML.php | 6 +- lib/Property.php | 6 +- lib/Property/Binary.php | 2 +- lib/Property/ICalendar/DateTime.php | 4 +- lib/Property/ICalendar/Recur.php | 20 +++---- lib/Property/Text.php | 6 +- lib/Property/Uri.php | 2 +- lib/Property/VCard/DateAndOrTime.php | 2 +- lib/Recur/RRuleIterator.php | 59 ++++++++++--------- .../FindFromTimezoneIdentifier.php | 4 +- lib/VCardConverter.php | 19 +++--- 21 files changed, 103 insertions(+), 91 deletions(-) diff --git a/lib/BirthdayCalendarGenerator.php b/lib/BirthdayCalendarGenerator.php index 6fb21f4f2..11ae8be14 100644 --- a/lib/BirthdayCalendarGenerator.php +++ b/lib/BirthdayCalendarGenerator.php @@ -95,7 +95,7 @@ public function getResult(): VCalendar // We've seen clients (ez-vcard) putting "BDAY:" properties // without a value into vCards. If we come across those, we'll // skip them. - if (empty($object->BDAY->getValue())) { + if ('' === $object->BDAY->getValue()) { continue; } diff --git a/lib/Cli.php b/lib/Cli.php index f5a150dea..386cf2c73 100644 --- a/lib/Cli.php +++ b/lib/Cli.php @@ -147,7 +147,7 @@ public function main(array $argv): int throw new \InvalidArgumentException('Too many arguments'); } - if (!in_array($positional[0], ['validate', 'repair', 'convert', 'color'])) { + if (!in_array($positional[0], ['validate', 'repair', 'convert', 'color'], true)) { throw new \InvalidArgumentException('Unknown command: '.$positional[0]); } } catch (\InvalidArgumentException $e) { @@ -185,6 +185,7 @@ public function main(array $argv): int try { while ($input = $this->readInput()) { + // @phpstan-ignore method.dynamicName $returnCode = $this->$command($input); if (0 !== $returnCode) { $realCode = $returnCode; diff --git a/lib/Component.php b/lib/Component.php index b1a05d9aa..0849c5a8e 100644 --- a/lib/Component.php +++ b/lib/Component.php @@ -366,7 +366,7 @@ public function xmlSerialize(Xml\Writer $writer): void $writer->startElement(strtolower($this->name)); - if (!empty($properties)) { + if ([] !== $properties) { $writer->startElement('properties'); foreach ($properties as $property) { @@ -376,7 +376,7 @@ public function xmlSerialize(Xml\Writer $writer): void $writer->endElement(); } - if (!empty($components)) { + if ([] !== $components) { $writer->startElement('components'); foreach ($components as $component) { diff --git a/lib/Component/VAlarm.php b/lib/Component/VAlarm.php index 0e7f05ce0..47474dfbf 100644 --- a/lib/Component/VAlarm.php +++ b/lib/Component/VAlarm.php @@ -34,7 +34,7 @@ public function getEffectiveTriggerTime(): \DateTimeImmutable $trigger = $this->TRIGGER; if (!isset($trigger['VALUE']) || ($trigger['VALUE'] && 'DURATION' === strtoupper((string) $trigger['VALUE']))) { $triggerDuration = VObject\DateTimeParser::parseDuration($this->TRIGGER); - $related = (isset($trigger['RELATED']) && 'END' == strtoupper($trigger['RELATED'])) ? 'END' : 'START'; + $related = (isset($trigger['RELATED']) && 'END' === strtoupper($trigger['RELATED'])) ? 'END' : 'START'; /** @var VEvent|VTodo $parentComponent */ $parentComponent = $this->parent; diff --git a/lib/Component/VCalendar.php b/lib/Component/VCalendar.php index a4dff82c9..46928a62d 100644 --- a/lib/Component/VCalendar.php +++ b/lib/Component/VCalendar.php @@ -424,7 +424,7 @@ public function validate(int $options = 0): array if ($child instanceof Component) { ++$componentsFound; - if (!in_array($child->name, ['VEVENT', 'VTODO', 'VJOURNAL'])) { + if (!in_array($child->name, ['VEVENT', 'VTODO', 'VJOURNAL'], true)) { continue; } $componentTypes[] = $child->name; diff --git a/lib/Component/VCard.php b/lib/Component/VCard.php index 1fe654712..7c5ca739c 100644 --- a/lib/Component/VCard.php +++ b/lib/Component/VCard.php @@ -491,7 +491,7 @@ public function xmlSerialize(Xml\Writer $writer): void $writer->startElement(strtolower($this->name)); foreach ($propertiesByGroup as $group => $properties) { - if (!empty($group)) { + if ('' !== $group) { $writer->startElement('group'); $writer->writeAttribute('name', strtolower((string) $group)); } @@ -513,7 +513,7 @@ public function xmlSerialize(Xml\Writer $writer): void } } - if (!empty($group)) { + if ('' !== $group) { $writer->endElement(); } } @@ -529,7 +529,7 @@ public function getClassNameForPropertyName(string $propertyName): string $className = parent::getClassNameForPropertyName($propertyName); // In vCard 4, BINARY no longer exists, and we need URI instead. - if (VObject\Property\Binary::class == $className && self::VCARD40 === $this->getDocumentType()) { + if (VObject\Property\Binary::class === $className && self::VCARD40 === $this->getDocumentType()) { return VObject\Property\Uri::class; } diff --git a/lib/DateTimeParser.php b/lib/DateTimeParser.php index 44e27c6fa..9469cacea 100644 --- a/lib/DateTimeParser.php +++ b/lib/DateTimeParser.php @@ -329,7 +329,7 @@ public static function parseVCardDateTime(string $date): array $result = []; foreach ($parts as $part) { - if (empty($matches[$part])) { + if (!array_key_exists($part, $matches) || '' === $matches[$part]) { $result[$part] = null; } elseif ('-' === $matches[$part] || '--' === $matches[$part]) { $result[$part] = null; @@ -424,7 +424,7 @@ public static function parseVCardTime(string $date): array $result = []; foreach ($parts as $part) { - if (empty($matches[$part])) { + if (!array_key_exists($part, $matches) || '' === $matches[$part]) { $result[$part] = null; } elseif ('-' === $matches[$part]) { $result[$part] = null; @@ -541,7 +541,7 @@ public static function parseVCardDateAndOrTime(string $date): array $parts['year0'] = &$parts['year']; foreach ($parts as $part => &$value) { - if (!empty($matches[$part])) { + if (array_key_exists($part, $matches) && '' !== $matches[$part]) { $value = $matches[$part]; } } diff --git a/lib/ITip/Broker.php b/lib/ITip/Broker.php index f36b32787..42862cd8b 100644 --- a/lib/ITip/Broker.php +++ b/lib/ITip/Broker.php @@ -228,7 +228,7 @@ public function parseEvent($calendar, $userHref, $oldCalendar = null): array $eventInfo = $oldEventInfo; - if (in_array($eventInfo['organizer'], $userHref)) { + if (in_array($eventInfo['organizer'], $userHref, true)) { // This is an organizer deleting the event. $eventInfo['attendees'] = []; // Increasing the sequence, but only if the organizer deleted @@ -237,7 +237,7 @@ public function parseEvent($calendar, $userHref, $oldCalendar = null): array } else { // This is an attendee deleting the event. foreach ($eventInfo['attendees'] as $key => $attendee) { - if (in_array($attendee['href'], $userHref)) { + if (in_array($attendee['href'], $userHref, true)) { $eventInfo['attendees'][$key]['instances'] = ['master' => ['id' => 'master', 'partstat' => 'DECLINED'], ]; } @@ -247,13 +247,13 @@ public function parseEvent($calendar, $userHref, $oldCalendar = null): array } // Check if the user is the organizer - if (in_array($eventInfo['organizer'], $userHref)) { + if (in_array($eventInfo['organizer'], $userHref, true)) { return $this->parseEventForOrganizer($baseCalendar, $eventInfo, $oldEventInfo); } // Check if the user is an attendee foreach ($eventInfo['attendees'] as $attendee) { - if (in_array($attendee['href'], $userHref)) { + if (in_array($attendee['href'], $userHref, true)) { // If this is a event update, we always generate a reply if ($oldCalendar) { return $this->parseEventForAttendee($baseCalendar, $eventInfo, $oldEventInfo, $attendee['href']); @@ -566,7 +566,7 @@ protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo, $message->significantChange = 'REQUEST' === $attendee['forceSend'] - || count($oldAttendeeInstances) != count($newAttendeeInstances) + || count($oldAttendeeInstances) !== count($newAttendeeInstances) || count(array_diff($oldAttendeeInstances, $newAttendeeInstances)) > 0 || $oldEventInfo['significantChangeHash'] !== $eventInfo['significantChangeHash']; @@ -576,9 +576,9 @@ protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo, // We need to find a list of events that the attendee // is not a part of to add to the list of exceptions. $exceptions = []; - foreach ($eventInfo['instances'] as $instanceId => $vevent) { - if (!isset($attendee['newInstances'][$instanceId])) { - $exceptions[] = $instanceId; + foreach ($eventInfo['instances'] as $eventInstanceId => $vevent) { + if (!isset($attendee['newInstances'][$eventInstanceId])) { + $exceptions[] = $eventInstanceId; } } @@ -648,7 +648,12 @@ protected function parseEventForAttendee(VCalendar $calendar, array $eventInfo, return []; } - $oldInstances = !empty($oldEventInfo['attendees'][$attendee]['instances']) ? + $oldInstances = + is_array($oldEventInfo['attendees']) + && array_key_exists($attendee, $oldEventInfo['attendees']) + && is_array($oldEventInfo['attendees'][$attendee]) + && array_key_exists('instances', $oldEventInfo['attendees'][$attendee]) + && is_array($oldEventInfo['attendees'][$attendee]['instances']) ? $oldEventInfo['attendees'][$attendee]['instances'] : []; @@ -679,7 +684,7 @@ protected function parseEventForAttendee(VCalendar $calendar, array $eventInfo, // We only need to do that though, if the master event is not declined. if (isset($instances['master']) && 'DECLINED' !== $instances['master']['newstatus']) { foreach ($eventInfo['exdate'] as $exDate) { - if (!in_array($exDate, $oldEventInfo['exdate'] ?? [])) { + if (!in_array($exDate, $oldEventInfo['exdate'] ?? [], true)) { if (isset($instances[$exDate])) { $instances[$exDate]['newstatus'] = 'DECLINED'; } else { @@ -722,7 +727,7 @@ protected function parseEventForAttendee(VCalendar $calendar, array $eventInfo, $hasReply = false; foreach ($instances as $instance) { - if ($instance['oldstatus'] == $instance['newstatus'] && 'REPLY' !== $eventInfo['organizerForceSend']) { + if ($instance['oldstatus'] === $instance['newstatus'] && 'REPLY' !== $eventInfo['organizerForceSend']) { // Skip continue; } @@ -884,7 +889,7 @@ protected function parseEventInfo(VCalendar $calendar): array foreach ($vevent->select('RRULE') as $rr) { foreach ($rr->getParts() as $key => $val) { // ignore default values (https://github.com/sabre-io/vobject/issues/126) - if ('INTERVAL' === $key && 1 == $val) { + if ('INTERVAL' === $key && '1' === (string) $val) { continue; } if (is_array($val)) { @@ -951,6 +956,7 @@ protected function parseEventInfo(VCalendar $calendar): array } foreach ($this->significantChangeProperties as $prop) { + // @phpstan-ignore property.dynamicName if (isset($vevent->$prop)) { $propertyValues = $vevent->select($prop); diff --git a/lib/Parameter.php b/lib/Parameter.php index 5b0804ec5..64f77c187 100644 --- a/lib/Parameter.php +++ b/lib/Parameter.php @@ -163,7 +163,8 @@ public function has(string $value): bool { return in_array( strtolower($value), - array_map(strtolower(...), (array) $this->value) + array_map(strtolower(...), (array) $this->value), + true ); } diff --git a/lib/Parser/MimeDir.php b/lib/Parser/MimeDir.php index b71ae2634..47fa972ce 100644 --- a/lib/Parser/MimeDir.php +++ b/lib/Parser/MimeDir.php @@ -111,7 +111,7 @@ public function parse($input = null, int $options = 0): ?Document */ public function setCharset(string $charset): void { - if (!in_array($charset, self::$SUPPORTED_CHARSETS)) { + if (!in_array($charset, self::$SUPPORTED_CHARSETS, true)) { throw new \InvalidArgumentException('Unsupported encoding. (Supported encodings: '.implode(', ', self::$SUPPORTED_CHARSETS).')'); } $this->charset = $charset; @@ -396,7 +396,7 @@ protected function readProperty(string $line) throw new ParseException('Invalid Mimedir file. Line starting at '.$this->startLine.' did not follow iCalendar/vCard conventions'); } - if ('=' == $match[0][0] && self::TOKEN_PARAMNAME != $lastToken) { + if ('=' === $match[0][0] && self::TOKEN_PARAMNAME !== $lastToken) { throw new ParseException('Invalid Mimedir file. Line starting at '.$this->startLine.': Missing parameter name for parameter value "'.$match['paramValue'].'"'); } @@ -443,7 +443,7 @@ protected function readProperty(string $line) if (\is_null($property['value'])) { $property['value'] = ''; } - if (!isset($property['name']) || 0 == strlen($property['name'])) { + if (!isset($property['name']) || 0 === strlen($property['name'])) { if ($this->options & self::OPTION_IGNORE_INVALID_LINES) { return false; } diff --git a/lib/Parser/XML.php b/lib/Parser/XML.php index 77a229733..6fb3e5624 100644 --- a/lib/Parser/XML.php +++ b/lib/Parser/XML.php @@ -112,7 +112,7 @@ public function parse($input = null, int $options = 0): ?Document */ protected function parseVCalendarComponents(Component $parentComponent): void { - foreach ($this->pointer['value'] ?: [] as $children) { + foreach ($this->pointer['value'] ?? [] as $children) { switch (static::getTagName($children['name'])) { case 'properties': $this->pointer = &$children['value']; @@ -145,7 +145,7 @@ protected function parseVCardComponents(Component $parentComponent): void */ protected function parseProperties(Component $parentComponent, string $propertyNamePrefix = ''): void { - foreach ($this->pointer ?: [] as $xmlProperty) { + foreach ($this->pointer ?? [] as $xmlProperty) { [$namespace, $tagName] = SabreXml\Service::parseClarkNotation($xmlProperty['name']); $propertyName = $tagName; @@ -301,7 +301,7 @@ protected function parseProperties(Component $parentComponent, string $propertyN */ protected function parseComponent(Component $parentComponent): void { - $components = $this->pointer['value'] ?: []; + $components = $this->pointer['value'] ?? []; foreach ($components as $component) { $componentName = static::getTagName($component['name']); diff --git a/lib/Property.php b/lib/Property.php index e369b5239..7432e5064 100644 --- a/lib/Property.php +++ b/lib/Property.php @@ -104,7 +104,7 @@ public function setValue($value): void public function getValue() { if (is_array($this->value)) { - if (0 == count($this->value)) { + if (0 === count($this->value)) { return null; } elseif (1 === count($this->value)) { return $this->value[0]; @@ -312,7 +312,7 @@ public function xmlSerialize(Xml\Writer $writer): void $writer->startElement(strtolower((string) $this->name)); - if (!empty($parameters)) { + if ([] !== $parameters) { $writer->startElement('parameters'); foreach ($parameters as $parameter) { @@ -553,7 +553,7 @@ public function validate(int $options = 0): array } break; } - if ($allowedEncoding && !in_array(strtoupper($encoding), $allowedEncoding)) { + if ($allowedEncoding && !in_array(strtoupper($encoding), $allowedEncoding, true)) { $warnings[] = [ 'level' => 3, 'message' => 'ENCODING='.strtoupper($encoding).' is not valid for this document type.', diff --git a/lib/Property/Binary.php b/lib/Property/Binary.php index 124794c9d..9d68f415e 100644 --- a/lib/Property/Binary.php +++ b/lib/Property/Binary.php @@ -54,7 +54,7 @@ public function setValue($value): void */ public function setRawMimeDirValue(string $val): void { - $this->value = base64_decode($val); + $this->value = base64_decode($val, true); } /** diff --git a/lib/Property/ICalendar/DateTime.php b/lib/Property/ICalendar/DateTime.php index e4c876563..cd92475f3 100644 --- a/lib/Property/ICalendar/DateTime.php +++ b/lib/Property/ICalendar/DateTime.php @@ -205,7 +205,7 @@ public function setDateTimes(array $dt, $isFloating = false): void } if (is_null($tz)) { $tz = $d->getTimeZone(); - $isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z', '+00:00']); + $isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z', '+00:00'], true); if (!$isUtc) { $this->offsetSet('TZID', $tz->getName()); } @@ -257,7 +257,7 @@ public function getJsonValue(): array $isFloating = $this->isFloating(); $tz = $dts[0]->getTimeZone(); - $isUtc = !$isFloating && in_array($tz->getName(), ['UTC', 'GMT', 'Z']); + $isUtc = !$isFloating && in_array($tz->getName(), ['UTC', 'GMT', 'Z'], true); return array_map( function (\DateTimeInterface $dt) use ($hasTime, $isUtc) { diff --git a/lib/Property/ICalendar/Recur.php b/lib/Property/ICalendar/Recur.php index 66bb54970..f3d45158a 100644 --- a/lib/Property/ICalendar/Recur.php +++ b/lib/Property/ICalendar/Recur.php @@ -36,7 +36,7 @@ class Recur extends Property * * This may be either a single, or multiple strings in an array. * - * @param string|array $value + * @param string|array|object $value */ public function setValue($value): void { @@ -186,7 +186,7 @@ public static function stringToArray(string $value): array $newValue = []; foreach (explode(';', $value) as $part) { // Skipping empty parts. - if (empty($part)) { + if ('' === $part) { continue; } @@ -243,7 +243,7 @@ public function validate(int $options = 0): array if ($repair) { unset($values[$key]); } - } elseif ('BYMONTH' == $key) { + } elseif ('BYMONTH' === $key) { $byMonth = (array) $value; foreach ($byMonth as $i => $v) { if (!is_numeric($v) || (int) $v < 1 || (int) $v > 12) { @@ -262,13 +262,13 @@ public function validate(int $options = 0): array } } // if there is no valid entry left, remove the whole value - if (is_array($value) && empty($values[$key])) { + if (is_array($value) && ([] === $values[$key])) { unset($values[$key]); } - } elseif ('BYWEEKNO' == $key) { + } elseif ('BYWEEKNO' === $key) { $byWeekNo = (array) $value; foreach ($byWeekNo as $i => $v) { - if (!is_numeric($v) || (int) $v < -53 || 0 == (int) $v || (int) $v > 53) { + if (!is_numeric($v) || (int) $v < -53 || 0 === (int) $v || (int) $v > 53) { $warnings[] = [ 'level' => $repair ? 1 : 3, 'message' => 'BYWEEKNO in RRULE must have value(s) from -53 to -1, or 1 to 53!', @@ -284,13 +284,13 @@ public function validate(int $options = 0): array } } // if there is no valid entry left, remove the whole value - if (is_array($value) && empty($values[$key])) { + if (is_array($value) && ([] === $values[$key])) { unset($values[$key]); } - } elseif ('BYYEARDAY' == $key) { + } elseif ('BYYEARDAY' === $key) { $byYearDay = (array) $value; foreach ($byYearDay as $i => $v) { - if (!is_numeric($v) || (int) $v < -366 || 0 == (int) $v || (int) $v > 366) { + if (!is_numeric($v) || (int) $v < -366 || 0 === (int) $v || (int) $v > 366) { $warnings[] = [ 'level' => $repair ? 1 : 3, 'message' => 'BYYEARDAY in RRULE must have value(s) from -366 to -1, or 1 to 366!', @@ -306,7 +306,7 @@ public function validate(int $options = 0): array } } // if there is no valid entry left, remove the whole value - if (is_array($value) && empty($values[$key])) { + if (is_array($value) && ([] === $values[$key])) { unset($values[$key]); } } diff --git a/lib/Property/Text.php b/lib/Property/Text.php index 7b044346c..e585026fc 100644 --- a/lib/Property/Text.php +++ b/lib/Property/Text.php @@ -71,7 +71,7 @@ public function __construct(Component $root, string $name, $value = null, array // 2. structured value properties // // The former is always separated by a comma, the latter by semicolon. - if (in_array($name, $this->structuredValues)) { + if (in_array($name, $this->structuredValues, true)) { $this->delimiter = ';'; } @@ -155,7 +155,7 @@ public function getJsonValue(): array // Structured text values should always be returned as a single // array-item. Multi-value text should be returned as multiple items in // the top-array. - if (in_array($this->name, $this->structuredValues)) { + if (in_array($this->name, $this->structuredValues, true)) { return [$this->getParts()]; } @@ -270,7 +270,7 @@ protected function xmlSerializeValue(Xml\Writer $writer): void foreach ($items as $i => $item) { $writer->writeElement( $item, - !empty($values[$i]) ? $values[$i] : null + ('' !== $values[$i] && [] !== $values[$i]) ? $values[$i] : null ); } }; diff --git a/lib/Property/Uri.php b/lib/Property/Uri.php index ec98f7416..dce7bf4bb 100644 --- a/lib/Property/Uri.php +++ b/lib/Property/Uri.php @@ -38,7 +38,7 @@ public function getValueType(): string public function parameters(): array { $parameters = parent::parameters(); - if (!isset($parameters['VALUE']) && in_array($this->name, ['URL', 'PHOTO'])) { + if (!isset($parameters['VALUE']) && in_array($this->name, ['URL', 'PHOTO'], true)) { // If we are encoding a URI value, and this URI value has no // VALUE=URI parameter, we add it anyway. // diff --git a/lib/Property/VCard/DateAndOrTime.php b/lib/Property/VCard/DateAndOrTime.php index 7aa88a628..9a7693961 100644 --- a/lib/Property/VCard/DateAndOrTime.php +++ b/lib/Property/VCard/DateAndOrTime.php @@ -77,7 +77,7 @@ public function setValue($value): void public function setDateTime(\DateTimeInterface $dt): void { $tz = $dt->getTimeZone(); - $isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z']); + $isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z'], true); if ($isUtc) { $value = $dt->format('Ymd\\THis\\Z'); diff --git a/lib/Recur/RRuleIterator.php b/lib/Recur/RRuleIterator.php index b260b2d04..de1c123b5 100644 --- a/lib/Recur/RRuleIterator.php +++ b/lib/Recur/RRuleIterator.php @@ -354,23 +354,23 @@ protected function nextDaily(): void } $recurrenceHours = []; - if (!empty($this->byHour)) { + if ($this->byHour) { $recurrenceHours = $this->getHours(); } $recurrenceDays = []; - if (!empty($this->byDay)) { + if ($this->byDay) { $recurrenceDays = $this->getDays(); } $recurrenceMonths = []; - if (!empty($this->byMonth)) { + if ($this->byMonth) { $recurrenceMonths = $this->getMonths(); } do { if ($this->byHour) { - if ('23' == $this->currentDate->format('G')) { + if ('23' === $this->currentDate->format('G')) { // to obey the interval rule $this->currentDate = $this->currentDate->modify('+'.($this->interval - 1).' days'); } @@ -395,9 +395,9 @@ protected function nextDaily(): void return; } } while ( - ($this->byDay && !in_array($currentDay, $recurrenceDays)) - || ($this->byHour && !in_array($currentHour, $recurrenceHours)) - || ($this->byMonth && !in_array($currentMonth, $recurrenceMonths)) + ($this->byDay && !in_array((int) $currentDay, $recurrenceDays, true)) + || ($this->byHour && !in_array($currentHour, $recurrenceHours, true)) + || ($this->byMonth && !in_array($currentMonth, $recurrenceMonths, true)) ); } @@ -439,18 +439,18 @@ protected function nextWeekly(): void $currentHour = (int) $this->currentDate->format('G'); // We need to roll over to the next week - if ($currentDay === $firstDay && (!$this->byHour || '0' == $currentHour)) { + if ($currentDay === $firstDay && (!$this->byHour || 0 === $currentHour)) { $this->currentDate = $this->currentDate->modify('+'.($this->interval - 1).' weeks'); // We need to go to the first day of this week, but only if we // are not already on this first day of this week. - if ($this->currentDate->format('w') != $firstDay) { + if ((int) $this->currentDate->format('w') !== $firstDay) { $this->currentDate = $this->currentDate->modify('last '.$this->dayNames[$this->dayMap[$this->weekStart]]); } } // We have a match - } while (($this->byDay && !in_array($currentDay, $recurrenceDays)) || ($this->byHour && !in_array($currentHour, $recurrenceHours))); + } while (($this->byDay && !in_array($currentDay, $recurrenceDays, true)) || ($this->byHour && !in_array((string) $currentHour, $recurrenceHours, true))); } /** @@ -473,14 +473,14 @@ protected function nextMonthly(): void ++$increase; $tempDate = clone $this->currentDate; $tempDate = $tempDate->modify('+ '.($this->interval * $increase).' months '.$this->startTime()); - } while ($tempDate->format('j') != $currentDayOfMonth); + } while ($tempDate->format('j') !== $currentDayOfMonth); $this->currentDate = $tempDate; } return; } - $occurrence = -1; + $selectedOccurrence = -1; while (true) { $occurrences = $this->getMonthlyOccurrences(); @@ -488,6 +488,7 @@ protected function nextMonthly(): void // The first occurrence that's higher than the current // day of the month wins. if ($occurrence > $currentDayOfMonth) { + $selectedOccurrence = $occurrence; break 2; } } @@ -530,7 +531,7 @@ protected function nextMonthly(): void $this->currentDate = $this->currentDate->setDate( (int) $this->currentDate->format('Y'), (int) $this->currentDate->format('n'), - (int) $occurrence + (int) $selectedOccurrence )->modify($this->startTime()); } @@ -544,9 +545,9 @@ protected function nextYearly(): void $currentDayOfMonth = $this->currentDate->format('j'); // No sub-rules, so we just advance by year - if (empty($this->byMonth)) { + if (!$this->byMonth) { // Unless it was a leap day! - if (2 == $currentMonth && 29 == $currentDayOfMonth) { + if (2 === (int) $currentMonth && 29 === (int) $currentDayOfMonth) { $counter = 0; do { ++$counter; @@ -560,7 +561,7 @@ protected function nextYearly(): void // functions instead. $nextDate = clone $this->currentDate; $nextDate = $nextDate->modify('+ '.($this->interval * $counter).' years'); - } while (2 != $nextDate->format('n')); + } while (2 !== (int) $nextDate->format('n')); $this->currentDate = $nextDate; @@ -631,7 +632,7 @@ protected function nextYearly(): void $date = $date->sub(new \DateInterval('P'.abs($byYearDay + 1).'D')); } - if ($date > $this->currentDate && in_array($date->format('N'), $dayOffsets)) { + if ($date > $this->currentDate && in_array((int) $date->format('N'), $dayOffsets, true)) { $checkDates[] = $date; } } @@ -662,7 +663,7 @@ protected function nextYearly(): void // If we got a byDay or getMonthDay filter, we must first expand // further. if ($this->byDay || $this->byMonthDay) { - $occurrence = -1; + $selectedOccurrence = -1; while (true) { $occurrences = $this->getMonthlyOccurrences(); @@ -674,7 +675,8 @@ protected function nextYearly(): void if ($occurrence > $currentDayOfMonth || $advancedToNewMonth) { // only consider byMonth matches, // otherwise, we don't follow RRule correctly - if (in_array($currentMonth, $this->byMonth)) { + if (in_array((string) $currentMonth, $this->byMonth, true)) { + $selectedOccurrence = $occurrence; break 2; } } @@ -690,12 +692,12 @@ protected function nextYearly(): void $currentYear += $this->interval; $currentMonth = 1; } - } while (!in_array($currentMonth, $this->byMonth)); + } while (!in_array((string) $currentMonth, $this->byMonth, true)); $this->currentDate = $this->currentDate->setDate( (int) $currentYear, - (int) $currentMonth, - (int) $currentDayOfMonth + $currentMonth, + $currentDayOfMonth ); // To prevent running this forever (better: until we hit the max date of DateTimeImmutable) we simply @@ -711,7 +713,7 @@ protected function nextYearly(): void $this->currentDate = $this->currentDate->setDate( (int) $currentYear, (int) $currentMonth, - (int) $occurrence + (int) $selectedOccurrence )->modify($this->startTime()); return; @@ -724,10 +726,10 @@ protected function nextYearly(): void $currentYear += $this->interval; $currentMonth = 1; } - } while (!in_array($currentMonth, $this->byMonth)); + } while (!in_array((string) $currentMonth, $this->byMonth, true)); $this->currentDate = $this->currentDate->setDate( (int) $currentYear, - (int) $currentMonth, + $currentMonth, (int) $currentDayOfMonth )->modify($this->startTime()); } @@ -755,7 +757,8 @@ protected function parseRRule($rrule): void $value = strtolower((string) $value); if (!in_array( $value, - ['secondly', 'minutely', 'hourly', 'daily', 'weekly', 'monthly', 'yearly'] + ['secondly', 'minutely', 'hourly', 'daily', 'weekly', 'monthly', 'yearly'], + true )) { throw new InvalidDataException('Unknown value for FREQ='.strtoupper($value)); } @@ -817,7 +820,7 @@ protected function parseRRule($rrule): void case 'BYYEARDAY': $this->byYearDay = (array) $value; foreach ($this->byYearDay as $byYearDay) { - if (!is_numeric($byYearDay) || (int) $byYearDay < -366 || 0 == (int) $byYearDay || (int) $byYearDay > 366) { + if (!is_numeric($byYearDay) || (int) $byYearDay < -366 || 0 === (int) $byYearDay || (int) $byYearDay > 366) { throw new InvalidDataException('BYYEARDAY in RRULE must have value(s) from 1 to 366, or -366 to -1!'); } } @@ -826,7 +829,7 @@ protected function parseRRule($rrule): void case 'BYWEEKNO': $this->byWeekNo = (array) $value; foreach ($this->byWeekNo as $byWeekNo) { - if (!is_numeric($byWeekNo) || (int) $byWeekNo < -53 || 0 == (int) $byWeekNo || (int) $byWeekNo > 53) { + if (!is_numeric($byWeekNo) || (int) $byWeekNo < -53 || 0 === (int) $byWeekNo || (int) $byWeekNo > 53) { throw new InvalidDataException('BYWEEKNO in RRULE must have value(s) from 1 to 53, or -53 to -1!'); } } diff --git a/lib/TimezoneGuesser/FindFromTimezoneIdentifier.php b/lib/TimezoneGuesser/FindFromTimezoneIdentifier.php index 7547d574a..5e56b82f1 100644 --- a/lib/TimezoneGuesser/FindFromTimezoneIdentifier.php +++ b/lib/TimezoneGuesser/FindFromTimezoneIdentifier.php @@ -40,9 +40,9 @@ public function find(string $tzid, ?bool $failIfUncertain = false): ?\DateTimeZo try { if ( - in_array($tzid, $tzIdentifiers) + in_array($tzid, $tzIdentifiers, true) || preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches) - || in_array($tzid, $this->getIdentifiersBC()) + || in_array($tzid, $this->getIdentifiersBC(), true) ) { return new \DateTimeZone($tzid); } diff --git a/lib/VCardConverter.php b/lib/VCardConverter.php index c1d41bdb3..39969d558 100644 --- a/lib/VCardConverter.php +++ b/lib/VCardConverter.php @@ -38,10 +38,10 @@ public function convert(Component\VCard $input, int $targetVersion): Component\V return clone $input; } - if (!in_array($inputVersion, [Document::VCARD21, Document::VCARD30, Document::VCARD40])) { + if (!in_array($inputVersion, [Document::VCARD21, Document::VCARD30, Document::VCARD40], true)) { throw new \InvalidArgumentException('Only vCard 2.1, 3.0 and 4.0 are supported for the input data'); } - if (!in_array($targetVersion, [Document::VCARD30, Document::VCARD40])) { + if (!in_array($targetVersion, [Document::VCARD30, Document::VCARD40], true)) { throw new \InvalidArgumentException('You can only use vCard 3.0 or 4.0 for the target version'); } @@ -69,7 +69,7 @@ public function convert(Component\VCard $input, int $targetVersion): Component\V protected function convertProperty(Component\VCard $input, Component\VCard $output, Property $property, int $targetVersion): void { // Skipping these, those are automatically added. - if (in_array($property->name, ['VERSION', 'PRODID'])) { + if (in_array($property->name, ['VERSION', 'PRODID'], true)) { return; } @@ -93,7 +93,7 @@ protected function convertProperty(Component\VCard $input, Component\VCard $outp ); if (Document::VCARD30 === $targetVersion) { - if ($property instanceof Uri && in_array($property->name, ['PHOTO', 'LOGO', 'SOUND'])) { + if ($property instanceof Uri && in_array($property->name, ['PHOTO', 'LOGO', 'SOUND'], true)) { /** @var Uri $newProperty */ $newProperty = $this->convertUriToBinary($output, $newProperty); } elseif ($property instanceof Property\VCard\DateAndOrTime) { @@ -111,7 +111,7 @@ protected function convertProperty(Component\VCard $input, Component\VCard $outp $newProperty['X-APPLE-OMIT-YEAR'] = '1604'; } - if ('ANNIVERSARY' == $newProperty->name) { + if ('ANNIVERSARY' === $newProperty->name) { // Microsoft non-standard anniversary $newProperty->name = 'X-ANNIVERSARY'; @@ -149,7 +149,7 @@ protected function convertProperty(Component\VCard $input, Component\VCard $outp } } elseif (Document::VCARD40 === $targetVersion) { // These properties were removed in vCard 4.0 - if (in_array($property->name, ['NAME', 'MAILER', 'LABEL', 'CLASS'])) { + if (in_array($property->name, ['NAME', 'MAILER', 'LABEL', 'CLASS'], true)) { return; } @@ -275,7 +275,8 @@ protected function convertBinaryToUri(Component\VCard $output, Binary $newProper foreach ($parameters['TYPE']->getParts() as $typePart) { if (in_array( strtoupper((string) $typePart), - ['JPEG', 'PNG', 'GIF'] + ['JPEG', 'PNG', 'GIF'], + true )) { $mimeType = 'image/'.strtolower((string) $typePart); } else { @@ -328,7 +329,7 @@ protected function convertUriToBinary(Component\VCard $output, Uri $newProperty) $mimeType = substr((string) $value, 5, strpos((string) $value, ',') - 5); if (strpos($mimeType, ';')) { $mimeType = substr($mimeType, 0, strpos($mimeType, ';')); - $newProperty->setValue(base64_decode(substr((string) $value, strpos((string) $value, ',') + 1))); + $newProperty->setValue(base64_decode(substr((string) $value, strpos((string) $value, ',') + 1), true)); } else { $newProperty->setValue(substr((string) $value, strpos((string) $value, ',') + 1)); } @@ -413,7 +414,7 @@ protected function convertParameters30(Property $newProperty, array $parameters) * Any other PREF numbers we'll drop. */ case 'PREF': - if ('1' == $param->getValue()) { + if ('1' === (string) $param->getValue()) { $newProperty->add('TYPE', 'PREF'); } break; From 7d93a3f9aaf3dcfd6a1684f96a64c07aeaa2e169 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Sun, 14 Jun 2026 18:51:47 +0930 Subject: [PATCH 07/23] test: fix static calls to use self --- tests/VObject/CliTest.php | 4 ++-- tests/VObject/Component/VAvailabilityTest.php | 2 +- tests/VObject/Splitter/ICalendarTest.php | 2 +- tests/VObject/TimeZoneUtilTest.php | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/VObject/CliTest.php b/tests/VObject/CliTest.php index 4b3671778..5240bdd63 100644 --- a/tests/VObject/CliTest.php +++ b/tests/VObject/CliTest.php @@ -154,7 +154,7 @@ public function testConvertJson(): void public function testConvertJCardPretty(): void { if (version_compare(PHP_VERSION, '5.4.0') < 0) { - $this::markTestSkipped('This test required PHP 5.4.0'); + self::markTestSkipped('This test required PHP 5.4.0'); } $inputStream = fopen('php://memory', 'r+'); @@ -306,7 +306,7 @@ public function testConvertDefaultFormats($inputFilename, $outputFilename, $form } self::assertNotNull($triggeredWarning, 'A PHP warning was expected but never triggered.'); - $this::assertStringContainsString( + self::assertStringContainsString( "fopen($inputFilename): Failed to open stream: No such file or directory", $triggeredWarning ); diff --git a/tests/VObject/Component/VAvailabilityTest.php b/tests/VObject/Component/VAvailabilityTest.php index e47653910..d8040f851 100644 --- a/tests/VObject/Component/VAvailabilityTest.php +++ b/tests/VObject/Component/VAvailabilityTest.php @@ -412,7 +412,7 @@ protected function assertIsValid(VObject\Document $document): void $validationResult = $document->validate(); if ($validationResult) { $messages = array_map(fn ($item) => $item['message'], $validationResult); - $this::fail('Failed to assert that the supplied document is a valid document. Validation messages: '.implode(', ', $messages)); + self::fail('Failed to assert that the supplied document is a valid document. Validation messages: '.implode(', ', $messages)); } self::assertEmpty($document->validate()); } diff --git a/tests/VObject/Splitter/ICalendarTest.php b/tests/VObject/Splitter/ICalendarTest.php index 775496dc5..be913780a 100644 --- a/tests/VObject/Splitter/ICalendarTest.php +++ b/tests/VObject/Splitter/ICalendarTest.php @@ -173,7 +173,7 @@ public function testICalendarImportEventWithoutUID(): void fn ($item) => $item['message'], $messages ); - $this::fail('Validation errors: '.implode("\n", $messages)); + self::fail('Validation errors: '.implode("\n", $messages)); } else { self::assertEquals([], $messages); } diff --git a/tests/VObject/TimeZoneUtilTest.php b/tests/VObject/TimeZoneUtilTest.php index fa704b459..9bbf0b0c4 100644 --- a/tests/VObject/TimeZoneUtilTest.php +++ b/tests/VObject/TimeZoneUtilTest.php @@ -21,7 +21,7 @@ public function testCorrectTZ(string $timezoneName): void self::assertInstanceOf('DateTimeZone', $tz); } catch (\Exception $e) { if (str_contains($e->getMessage(), 'Unknown or bad timezone')) { - $this::markTestSkipped($timezoneName.' is not (yet) supported in this PHP version. Update pecl/timezonedb'); + self::markTestSkipped($timezoneName.' is not (yet) supported in this PHP version. Update pecl/timezonedb'); } else { throw $e; } @@ -188,7 +188,7 @@ public function testTimeZoneBCIdentifiers(string $tzid): void */ $versionOfPHP = \phpversion(); if ((('8.1.14' === $versionOfPHP) || ('8.2.1' === $versionOfPHP)) && \str_contains($tzid, '+')) { - $this::markTestSkipped("Timezone ids containing '+' do not work on PHP $versionOfPHP"); + self::markTestSkipped("Timezone ids containing '+' do not work on PHP $versionOfPHP"); } $tz = TimeZoneUtil::getTimeZone($tzid); $ex = new \DateTimeZone($tzid); From 0d7d1340310eee34edb4ef587b4fe4dd1ceac23f Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Sun, 14 Jun 2026 19:06:52 +0930 Subject: [PATCH 08/23] chore: simplify calls that used call_user_func_array --- lib/Document.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Document.php b/lib/Document.php index c8fd99054..7cdb65d4f 100644 --- a/lib/Document.php +++ b/lib/Document.php @@ -118,10 +118,10 @@ public function getDocumentType(): int public function create(string $name) { if (isset(static::$componentMap[strtoupper($name)])) { - return call_user_func_array($this->createComponent(...), func_get_args()); + return $this->createComponent(...func_get_args()); } - return call_user_func_array($this->createProperty(...), func_get_args()); + return $this->createProperty(...func_get_args()); } /** From 013f8bbc9c7000378895120aa894be4c989dbd93 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 15 Jun 2026 12:05:16 +0930 Subject: [PATCH 09/23] test: check result of preg_match_all in PHPUnitAssertions --- lib/PHPUnitAssertions.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/PHPUnitAssertions.php b/lib/PHPUnitAssertions.php index ebf9956c6..aad5438a5 100644 --- a/lib/PHPUnitAssertions.php +++ b/lib/PHPUnitAssertions.php @@ -41,7 +41,7 @@ public function assertVObjectEqualsVObject($expected, $actual, string $message = $input = Reader::read($input); } if (!$input instanceof Component) { - $this::fail('Input must be a string, stream or VObject component'); + self::fail('Input must be a string, stream or VObject component'); } unset($input->PRODID); if ($input instanceof Component\VCalendar && 'GREGORIAN' === (string) $input->CALSCALE) { @@ -58,7 +58,9 @@ public function assertVObjectEqualsVObject($expected, $actual, string $message = $actualSerialized = $getObj($actual)->serialize(); // Finding wildcards in expected. - preg_match_all('|^([A-Z]+):\\*\\*ANY\\*\\*\r$|m', $expectedSerialized, $matches, PREG_SET_ORDER); + $result = preg_match_all('|^([A-Z]+):\\*\\*ANY\\*\\*\r$|m', $expectedSerialized, $matches, PREG_SET_ORDER); + + self::assertNotFalse($result); foreach ($matches as $match) { $actualSerialized = preg_replace( @@ -68,7 +70,7 @@ public function assertVObjectEqualsVObject($expected, $actual, string $message = ); } - $this::assertEquals( + self::assertEquals( $expectedSerialized, $actualSerialized, $message From fb1aa15c90a6761a2b57d581a2f26036870c7cd0 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 15 Jun 2026 16:34:38 +0930 Subject: [PATCH 10/23] ci: pin GitHub actions to SHA --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a7e69fb7..34724f7af 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,10 +30,10 @@ jobs: rector-check: 'yes' steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 #https://github.com/shivammathur/setup-php + uses: shivammathur/setup-php@7c071dfe9dc99bdf297fa79cb49ea005b9fcadbc # v2.37.1 with: php-version: ${{ matrix.php-versions }} extensions: mbstring, dom, fileinfo, mysql, redis, opcache @@ -45,7 +45,7 @@ jobs: run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache composer dependencies - uses: actions/cache@v5 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: ${{ steps.composer-cache.outputs.dir }} # Use composer.json for key, if composer.lock is not committed. @@ -72,5 +72,5 @@ jobs: run: vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-clover clover.xml - name: Code Coverage - uses: codecov/codecov-action@v6 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 if: matrix.coverage != 'none' From 55cb62c0e00db01c5a1f505320677718fdf79a5b Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 15 Jun 2026 18:29:18 +0930 Subject: [PATCH 11/23] fix: minor code fixes Modifiers at the end of a regex should not have whitespace before them. Change "# xi" to just "#xi". Date format "t" returns a string that is the numbers of days in the month. Explicitly cast it to int when using it in a calculation. These were two minor things pointed out by my editor. Existing tests will cover the changes here. There is no actual bug being fixed. --- lib/Recur/RRuleIterator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Recur/RRuleIterator.php b/lib/Recur/RRuleIterator.php index de1c123b5..6f1f28ae3 100644 --- a/lib/Recur/RRuleIterator.php +++ b/lib/Recur/RRuleIterator.php @@ -806,7 +806,7 @@ protected function parseRRule($rrule): void case 'BYDAY': $value = (array) $value; foreach ($value as $part) { - if (!preg_match('#^ (-|\+)? ([1-5])? (MO|TU|WE|TH|FR|SA|SU) $# xi', (string) $part)) { + if (!preg_match('#^ (-|\+)? ([1-5])? (MO|TU|WE|TH|FR|SA|SU) $#xi', (string) $part)) { throw new InvalidDataException('Invalid part in BYDAY clause: '.$part); } } @@ -946,7 +946,7 @@ protected function getMonthlyOccurrences(): array $byMonthDayResults[] = $monthDay; } else { // Negative values - $byMonthDayResults[] = $startDate->format('t') + 1 + $monthDay; + $byMonthDayResults[] = (int) $startDate->format('t') + 1 + $monthDay; } } } From cdabfd09dafbb0fbf943f0e6ca8655118a8a0b47 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Sun, 28 Jun 2026 21:53:48 +0930 Subject: [PATCH 12/23] test: convert DataProvider annotation to attribute --- rector.php | 4 +++ tests/VObject/CliTest.php | 4 +-- tests/VObject/Component/VAlarmTest.php | 4 +-- tests/VObject/Component/VCalendarTest.php | 4 +-- tests/VObject/Component/VCardTest.php | 4 +-- tests/VObject/Component/VEventTest.php | 4 +-- tests/VObject/Component/VJournalTest.php | 4 +-- tests/VObject/Component/VTodoTest.php | 4 +-- tests/VObject/ComponentTest.php | 4 +-- tests/VObject/DateTimeParserTest.php | 4 +-- tests/VObject/Parser/MimeDirTest.php | 10 +++---- .../Property/ICalendar/CalAddressTest.php | 4 +-- .../Property/VCard/DateAndOrTimeTest.php | 4 +-- tests/VObject/Recur/RRuleIteratorTest.php | 27 +++++-------------- tests/VObject/TimeZoneUtilTest.php | 12 +++------ .../FindFromTimezoneMapTest.php | 3 +-- 16 files changed, 29 insertions(+), 71 deletions(-) diff --git a/rector.php b/rector.php index 201fc8090..bfb4215a9 100644 --- a/rector.php +++ b/rector.php @@ -3,6 +3,7 @@ declare(strict_types=1); use Rector\Config\RectorConfig; +use Rector\PHPUnit\AnnotationsToAttributes\Rector\ClassMethod\DataProviderAnnotationToAttributeRector; return RectorConfig::configure() ->withPaths([ @@ -11,6 +12,9 @@ ]) // uncomment to reach your current PHP version ->withPhpSets(false, true) + ->withRules([ + DataProviderAnnotationToAttributeRector::class, + ]) ->withTypeCoverageLevel(0) ->withDeadCodeLevel(0) ->withCodeQualityLevel(0); diff --git a/tests/VObject/CliTest.php b/tests/VObject/CliTest.php index 5240bdd63..38d752dc8 100644 --- a/tests/VObject/CliTest.php +++ b/tests/VObject/CliTest.php @@ -280,9 +280,7 @@ public static function provideFormats(): array ]; } - /** - * @dataProvider provideFormats - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideFormats')] public function testConvertDefaultFormats($inputFilename, $outputFilename, $format): void { $triggeredWarning = null; diff --git a/tests/VObject/Component/VAlarmTest.php b/tests/VObject/Component/VAlarmTest.php index 33da9410c..bd27a3f79 100644 --- a/tests/VObject/Component/VAlarmTest.php +++ b/tests/VObject/Component/VAlarmTest.php @@ -8,9 +8,7 @@ class VAlarmTest extends TestCase { - /** - * @dataProvider timeRangeTestData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('timeRangeTestData')] public function testInTimeRange(VAlarm $valarm, \DateTime $start, \DateTime $end, bool $outcome): void { self::assertEquals($outcome, $valarm->isInTimeRange($start, $end)); diff --git a/tests/VObject/Component/VCalendarTest.php b/tests/VObject/Component/VCalendarTest.php index ea1fe61b4..c2c86add2 100644 --- a/tests/VObject/Component/VCalendarTest.php +++ b/tests/VObject/Component/VCalendarTest.php @@ -10,9 +10,7 @@ class VCalendarTest extends TestCase { use VObject\PHPUnitAssertions; - /** - * @dataProvider expandData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('expandData')] public function testExpand(string $input, string $output, string $timeZone = 'UTC', string $start = '2011-12-01', string $end = '2011-12-31'): void { $vcal = VObject\Reader::read($input); diff --git a/tests/VObject/Component/VCardTest.php b/tests/VObject/Component/VCardTest.php index 1ae83b81e..07db490c1 100644 --- a/tests/VObject/Component/VCardTest.php +++ b/tests/VObject/Component/VCardTest.php @@ -7,9 +7,7 @@ class VCardTest extends TestCase { - /** - * @dataProvider validateData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('validateData')] public function testValidate(string $input, array $expectedWarnings, string $expectedRepairedOutput): void { $vcard = VObject\Reader::read($input); diff --git a/tests/VObject/Component/VEventTest.php b/tests/VObject/Component/VEventTest.php index 9c79c245d..95103132e 100644 --- a/tests/VObject/Component/VEventTest.php +++ b/tests/VObject/Component/VEventTest.php @@ -6,9 +6,7 @@ class VEventTest extends TestCase { - /** - * @dataProvider timeRangeTestData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('timeRangeTestData')] public function testInTimeRange(VEvent $vevent, \DateTime $start, \DateTime $end, bool $outcome): void { self::assertEquals($outcome, $vevent->isInTimeRange($start, $end)); diff --git a/tests/VObject/Component/VJournalTest.php b/tests/VObject/Component/VJournalTest.php index d0419253d..41c6d0793 100644 --- a/tests/VObject/Component/VJournalTest.php +++ b/tests/VObject/Component/VJournalTest.php @@ -7,9 +7,7 @@ class VJournalTest extends TestCase { - /** - * @dataProvider timeRangeTestData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('timeRangeTestData')] public function testInTimeRange(VJournal $vtodo, \DateTime $start, \DateTime $end, bool $outcome): void { self::assertEquals($outcome, $vtodo->isInTimeRange($start, $end)); diff --git a/tests/VObject/Component/VTodoTest.php b/tests/VObject/Component/VTodoTest.php index 5bd28c6fe..4dae22d42 100644 --- a/tests/VObject/Component/VTodoTest.php +++ b/tests/VObject/Component/VTodoTest.php @@ -7,9 +7,7 @@ class VTodoTest extends TestCase { - /** - * @dataProvider timeRangeTestData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('timeRangeTestData')] public function testInTimeRange(VTodo $vtodo, \DateTime $start, \DateTime $end, bool $outcome): void { self::assertEquals($outcome, $vtodo->isInTimeRange($start, $end)); diff --git a/tests/VObject/ComponentTest.php b/tests/VObject/ComponentTest.php index 8e930a564..d2b98026f 100644 --- a/tests/VObject/ComponentTest.php +++ b/tests/VObject/ComponentTest.php @@ -458,9 +458,7 @@ public function testRemoveNotFound(): void $comp->remove($prop); } - /** - * @dataProvider ruleData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('ruleData')] public function testValidateRules(array $componentList, int $errorCount): void { $vcard = new VCard(); diff --git a/tests/VObject/DateTimeParserTest.php b/tests/VObject/DateTimeParserTest.php index 2ae7a6897..a49d9a415 100644 --- a/tests/VObject/DateTimeParserTest.php +++ b/tests/VObject/DateTimeParserTest.php @@ -166,9 +166,7 @@ public function testParseICalendarDateInvalidDate(): void DateTimeParser::parseDate('20101331'); } - /** - * @dataProvider vcardDates - */ + #[\PHPUnit\Framework\Attributes\DataProvider('vcardDates')] public function testVCardDate(string $input, array $output): void { self::assertEquals( diff --git a/tests/VObject/Parser/MimeDirTest.php b/tests/VObject/Parser/MimeDirTest.php index a265b4216..6b383129e 100644 --- a/tests/VObject/Parser/MimeDirTest.php +++ b/tests/VObject/Parser/MimeDirTest.php @@ -109,9 +109,7 @@ public static function provideEmptyParserInput(): array ]; } - /** - * @dataProvider provideEmptyParserInput - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideEmptyParserInput')] public function testParseEmpty($input, $expectedExceptionMessage): void { $this->expectException(ParseException::class); @@ -188,9 +186,8 @@ public function testParsingTwiceSameContent(): void /** * @covers \Sabre\VObject\Parser\MimeDir::readProperty - * - * @dataProvider provideBrokenVCalendar */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideBrokenVCalendar')] public function testBrokenMultilineContentDoesNotBreakImportWhenSetToIgnoreBrokenLines(string $vcalendar): void { $mimeDir = new MimeDir(null, MimeDir::OPTION_IGNORE_INVALID_LINES); @@ -201,10 +198,9 @@ public function testBrokenMultilineContentDoesNotBreakImportWhenSetToIgnoreBroke /** * @covers \Sabre\VObject\Parser\MimeDir::readProperty * - * @dataProvider provideBrokenVCalendar - * * @param string $vcalendar */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideBrokenVCalendar')] public function testBrokenMultilineContentDoesBreakImport($vcalendar): void { $mimeDir = new MimeDir(); diff --git a/tests/VObject/Property/ICalendar/CalAddressTest.php b/tests/VObject/Property/ICalendar/CalAddressTest.php index 1142f2a9d..b74d9865f 100644 --- a/tests/VObject/Property/ICalendar/CalAddressTest.php +++ b/tests/VObject/Property/ICalendar/CalAddressTest.php @@ -6,9 +6,7 @@ class CalAddressTest extends TestCase { - /** - * @dataProvider values - */ + #[\PHPUnit\Framework\Attributes\DataProvider('values')] public function testGetNormalizedValue(string $expected, string $input): void { $vobj = new \Sabre\VObject\Component\VCalendar(); diff --git a/tests/VObject/Property/VCard/DateAndOrTimeTest.php b/tests/VObject/Property/VCard/DateAndOrTimeTest.php index 5d24850fc..8c78a6171 100644 --- a/tests/VObject/Property/VCard/DateAndOrTimeTest.php +++ b/tests/VObject/Property/VCard/DateAndOrTimeTest.php @@ -8,9 +8,7 @@ class DateAndOrTimeTest extends TestCase { - /** - * @dataProvider dates - */ + #[\PHPUnit\Framework\Attributes\DataProvider('dates')] public function testGetJsonValue(string $input, string $output): void { $vcard = new VObject\Component\VCard(); diff --git a/tests/VObject/Recur/RRuleIteratorTest.php b/tests/VObject/Recur/RRuleIteratorTest.php index 639949209..6429bb049 100644 --- a/tests/VObject/Recur/RRuleIteratorTest.php +++ b/tests/VObject/Recur/RRuleIteratorTest.php @@ -29,9 +29,7 @@ public function testHourly(): void ); } - /** - * @dataProvider dst2HourlyTransitionProvider - */ + #[\PHPUnit\Framework\Attributes\DataProvider('dst2HourlyTransitionProvider')] public function test2HourlyOnDstTransition(string $start, array $expected): void { $this->parse( @@ -87,9 +85,7 @@ public static function dst2HourlyTransitionProvider(): iterable ]; } - /** - * @dataProvider dst6HourlyTransitionProvider - */ + #[\PHPUnit\Framework\Attributes\DataProvider('dst6HourlyTransitionProvider')] public function testHourlyOnDstTransition(string $start, array $expected): void { $this->parse( @@ -278,9 +274,7 @@ public function testDailyBySetPosLoop(): void ); } - /** - * @dataProvider dstDailyTransitionProvider - */ + #[\PHPUnit\Framework\Attributes\DataProvider('dstDailyTransitionProvider')] public function testDailyOnDstTransition(string $start, array $expected): void { $this->parse( @@ -485,9 +479,7 @@ public function testWeeklyByDayByHourOnDstTransition(): void ); } - /** - * @dataProvider dstWeeklyTransitionProvider - */ + #[\PHPUnit\Framework\Attributes\DataProvider('dstWeeklyTransitionProvider')] public function testWeeklyOnDstTransition(string $start, array $expected): void { $this->parse( @@ -737,9 +729,7 @@ public function testMonthlyByDayBySetPos(): void ); } - /** - * @dataProvider dstMonthlyTransitionProvider - */ + #[\PHPUnit\Framework\Attributes\DataProvider('dstMonthlyTransitionProvider')] public function testMonthlyOnDstTransition(string $start, array $expected): void { $this->parse( @@ -1146,9 +1136,7 @@ public function testYearlyByDayByWeekNo(): void ); } - /** - * @dataProvider dstYearlyTransitionProvider - */ + #[\PHPUnit\Framework\Attributes\DataProvider('dstYearlyTransitionProvider')] public function testYearlyOnDstTransition(string $start, array $expected): void { $this->parse( @@ -1419,9 +1407,8 @@ public function testYearlyBySetPosLoop(): void /** * This caused an incorrect date to be returned by the rule iterator when * start date was not on the rrule list. - * - * @dataProvider yearlyStartDateNotOnRRuleListProvider */ + #[\PHPUnit\Framework\Attributes\DataProvider('yearlyStartDateNotOnRRuleListProvider')] public function testYearlyStartDateNotOnRRuleList(string $rule, string $start, array $expected): void { $this->parse($rule, $start, $expected); diff --git a/tests/VObject/TimeZoneUtilTest.php b/tests/VObject/TimeZoneUtilTest.php index 9bbf0b0c4..1162c0428 100644 --- a/tests/VObject/TimeZoneUtilTest.php +++ b/tests/VObject/TimeZoneUtilTest.php @@ -11,9 +11,7 @@ public function setUp(): void TimeZoneUtil::clean(); } - /** - * @dataProvider getMapping - */ + #[\PHPUnit\Framework\Attributes\DataProvider('getMapping')] public function testCorrectTZ(string $timezoneName): void { try { @@ -163,9 +161,7 @@ public function testWindowsTimeZone(): void self::assertEquals($ex->getName(), $tz->getName()); } - /** - * @dataProvider getPHPTimeZoneIdentifiers - */ + #[\PHPUnit\Framework\Attributes\DataProvider('getPHPTimeZoneIdentifiers')] public function testTimeZoneIdentifiers(string $tzid): void { $tz = TimeZoneUtil::getTimeZone($tzid); @@ -174,9 +170,7 @@ public function testTimeZoneIdentifiers(string $tzid): void self::assertEquals($ex->getName(), $tz->getName()); } - /** - * @dataProvider getPHPTimeZoneBCIdentifiers - */ + #[\PHPUnit\Framework\Attributes\DataProvider('getPHPTimeZoneBCIdentifiers')] public function testTimeZoneBCIdentifiers(string $tzid): void { /* diff --git a/tests/VObject/TimezoneGuesser/FindFromTimezoneMapTest.php b/tests/VObject/TimezoneGuesser/FindFromTimezoneMapTest.php index 05ebd267c..d543ae75b 100644 --- a/tests/VObject/TimezoneGuesser/FindFromTimezoneMapTest.php +++ b/tests/VObject/TimezoneGuesser/FindFromTimezoneMapTest.php @@ -11,9 +11,8 @@ class FindFromTimezoneMapTest extends TestCase /** * Verify that previously-deprecated IANA names have been replaced with * their canonical successors and resolve correctly. - * - * @dataProvider updatedTimezoneProvider */ + #[\PHPUnit\Framework\Attributes\DataProvider('updatedTimezoneProvider')] public function testUpdatedTimezonesResolve(string $mapKey, string $expectedOlson): void { $finder = new FindFromTimezoneMap(); From 00890508311e5c71eee6a954dde53fb65e35bf5f Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Sun, 28 Jun 2026 21:58:43 +0930 Subject: [PATCH 13/23] test: AddParamArrayDocblockFromDataProviderRector --- rector.php | 2 ++ tests/VObject/ComponentTest.php | 3 +++ tests/VObject/DateTimeParserTest.php | 3 +++ tests/VObject/Recur/RRuleIteratorTest.php | 20 ++++++++++++++++++++ 4 files changed, 28 insertions(+) diff --git a/rector.php b/rector.php index bfb4215a9..5a4426098 100644 --- a/rector.php +++ b/rector.php @@ -4,6 +4,7 @@ use Rector\Config\RectorConfig; use Rector\PHPUnit\AnnotationsToAttributes\Rector\ClassMethod\DataProviderAnnotationToAttributeRector; +use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddParamArrayDocblockFromDataProviderRector; return RectorConfig::configure() ->withPaths([ @@ -13,6 +14,7 @@ // uncomment to reach your current PHP version ->withPhpSets(false, true) ->withRules([ + AddParamArrayDocblockFromDataProviderRector::class, DataProviderAnnotationToAttributeRector::class, ]) ->withTypeCoverageLevel(0) diff --git a/tests/VObject/ComponentTest.php b/tests/VObject/ComponentTest.php index d2b98026f..6f4f49c67 100644 --- a/tests/VObject/ComponentTest.php +++ b/tests/VObject/ComponentTest.php @@ -458,6 +458,9 @@ public function testRemoveNotFound(): void $comp->remove($prop); } + /** + * @param string[] $componentList + */ #[\PHPUnit\Framework\Attributes\DataProvider('ruleData')] public function testValidateRules(array $componentList, int $errorCount): void { diff --git a/tests/VObject/DateTimeParserTest.php b/tests/VObject/DateTimeParserTest.php index a49d9a415..452b61185 100644 --- a/tests/VObject/DateTimeParserTest.php +++ b/tests/VObject/DateTimeParserTest.php @@ -166,6 +166,9 @@ public function testParseICalendarDateInvalidDate(): void DateTimeParser::parseDate('20101331'); } + /** + * @param array $output + */ #[\PHPUnit\Framework\Attributes\DataProvider('vcardDates')] public function testVCardDate(string $input, array $output): void { diff --git a/tests/VObject/Recur/RRuleIteratorTest.php b/tests/VObject/Recur/RRuleIteratorTest.php index 6429bb049..41de0e385 100644 --- a/tests/VObject/Recur/RRuleIteratorTest.php +++ b/tests/VObject/Recur/RRuleIteratorTest.php @@ -29,6 +29,9 @@ public function testHourly(): void ); } + /** + * @param string[] $expected + */ #[\PHPUnit\Framework\Attributes\DataProvider('dst2HourlyTransitionProvider')] public function test2HourlyOnDstTransition(string $start, array $expected): void { @@ -85,6 +88,9 @@ public static function dst2HourlyTransitionProvider(): iterable ]; } + /** + * @param string[] $expected + */ #[\PHPUnit\Framework\Attributes\DataProvider('dst6HourlyTransitionProvider')] public function testHourlyOnDstTransition(string $start, array $expected): void { @@ -274,6 +280,9 @@ public function testDailyBySetPosLoop(): void ); } + /** + * @param string[] $expected + */ #[\PHPUnit\Framework\Attributes\DataProvider('dstDailyTransitionProvider')] public function testDailyOnDstTransition(string $start, array $expected): void { @@ -479,6 +488,9 @@ public function testWeeklyByDayByHourOnDstTransition(): void ); } + /** + * @param string[] $expected + */ #[\PHPUnit\Framework\Attributes\DataProvider('dstWeeklyTransitionProvider')] public function testWeeklyOnDstTransition(string $start, array $expected): void { @@ -729,6 +741,9 @@ public function testMonthlyByDayBySetPos(): void ); } + /** + * @param string[] $expected + */ #[\PHPUnit\Framework\Attributes\DataProvider('dstMonthlyTransitionProvider')] public function testMonthlyOnDstTransition(string $start, array $expected): void { @@ -1136,6 +1151,9 @@ public function testYearlyByDayByWeekNo(): void ); } + /** + * @param string[] $expected + */ #[\PHPUnit\Framework\Attributes\DataProvider('dstYearlyTransitionProvider')] public function testYearlyOnDstTransition(string $start, array $expected): void { @@ -1407,6 +1425,8 @@ public function testYearlyBySetPosLoop(): void /** * This caused an incorrect date to be returned by the rule iterator when * start date was not on the rrule list. + * + * @param string[] $expected */ #[\PHPUnit\Framework\Attributes\DataProvider('yearlyStartDateNotOnRRuleListProvider')] public function testYearlyStartDateNotOnRRuleList(string $rule, string $start, array $expected): void From 6afc39c83d09e52c7c0a72bf019aca411b805153 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Sun, 28 Jun 2026 22:18:47 +0930 Subject: [PATCH 14/23] test: import DataProvider class to tests --- tests/VObject/CliTest.php | 3 ++- tests/VObject/Component/VAlarmTest.php | 3 ++- tests/VObject/Component/VCalendarTest.php | 3 ++- tests/VObject/Component/VCardTest.php | 3 ++- tests/VObject/Component/VEventTest.php | 3 ++- tests/VObject/Component/VJournalTest.php | 3 ++- tests/VObject/Component/VTodoTest.php | 3 ++- tests/VObject/ComponentTest.php | 3 ++- tests/VObject/DateTimeParserTest.php | 3 ++- tests/VObject/Parser/MimeDirTest.php | 7 ++++--- .../VObject/Property/ICalendar/CalAddressTest.php | 3 ++- .../VObject/Property/VCard/DateAndOrTimeTest.php | 3 ++- tests/VObject/Recur/RRuleIteratorTest.php | 15 ++++++++------- tests/VObject/TimeZoneUtilTest.php | 7 ++++--- .../TimezoneGuesser/FindFromTimezoneMapTest.php | 3 ++- 15 files changed, 40 insertions(+), 25 deletions(-) diff --git a/tests/VObject/CliTest.php b/tests/VObject/CliTest.php index 38d752dc8..a75de69e9 100644 --- a/tests/VObject/CliTest.php +++ b/tests/VObject/CliTest.php @@ -2,6 +2,7 @@ namespace Sabre\VObject; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; /** @@ -280,7 +281,7 @@ public static function provideFormats(): array ]; } - #[\PHPUnit\Framework\Attributes\DataProvider('provideFormats')] + #[DataProvider('provideFormats')] public function testConvertDefaultFormats($inputFilename, $outputFilename, $format): void { $triggeredWarning = null; diff --git a/tests/VObject/Component/VAlarmTest.php b/tests/VObject/Component/VAlarmTest.php index bd27a3f79..9a7bd63ad 100644 --- a/tests/VObject/Component/VAlarmTest.php +++ b/tests/VObject/Component/VAlarmTest.php @@ -2,13 +2,14 @@ namespace Sabre\VObject\Component; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Sabre\VObject\InvalidDataException; use Sabre\VObject\Reader; class VAlarmTest extends TestCase { - #[\PHPUnit\Framework\Attributes\DataProvider('timeRangeTestData')] + #[DataProvider('timeRangeTestData')] public function testInTimeRange(VAlarm $valarm, \DateTime $start, \DateTime $end, bool $outcome): void { self::assertEquals($outcome, $valarm->isInTimeRange($start, $end)); diff --git a/tests/VObject/Component/VCalendarTest.php b/tests/VObject/Component/VCalendarTest.php index c2c86add2..7aed1ff84 100644 --- a/tests/VObject/Component/VCalendarTest.php +++ b/tests/VObject/Component/VCalendarTest.php @@ -2,6 +2,7 @@ namespace Sabre\VObject\Component; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Sabre\VObject; use Sabre\VObject\InvalidDataException; @@ -10,7 +11,7 @@ class VCalendarTest extends TestCase { use VObject\PHPUnitAssertions; - #[\PHPUnit\Framework\Attributes\DataProvider('expandData')] + #[DataProvider('expandData')] public function testExpand(string $input, string $output, string $timeZone = 'UTC', string $start = '2011-12-01', string $end = '2011-12-31'): void { $vcal = VObject\Reader::read($input); diff --git a/tests/VObject/Component/VCardTest.php b/tests/VObject/Component/VCardTest.php index 07db490c1..cffcde629 100644 --- a/tests/VObject/Component/VCardTest.php +++ b/tests/VObject/Component/VCardTest.php @@ -2,12 +2,13 @@ namespace Sabre\VObject\Component; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Sabre\VObject; class VCardTest extends TestCase { - #[\PHPUnit\Framework\Attributes\DataProvider('validateData')] + #[DataProvider('validateData')] public function testValidate(string $input, array $expectedWarnings, string $expectedRepairedOutput): void { $vcard = VObject\Reader::read($input); diff --git a/tests/VObject/Component/VEventTest.php b/tests/VObject/Component/VEventTest.php index 95103132e..4ffb0143a 100644 --- a/tests/VObject/Component/VEventTest.php +++ b/tests/VObject/Component/VEventTest.php @@ -2,11 +2,12 @@ namespace Sabre\VObject\Component; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class VEventTest extends TestCase { - #[\PHPUnit\Framework\Attributes\DataProvider('timeRangeTestData')] + #[DataProvider('timeRangeTestData')] public function testInTimeRange(VEvent $vevent, \DateTime $start, \DateTime $end, bool $outcome): void { self::assertEquals($outcome, $vevent->isInTimeRange($start, $end)); diff --git a/tests/VObject/Component/VJournalTest.php b/tests/VObject/Component/VJournalTest.php index 41c6d0793..9741a16b6 100644 --- a/tests/VObject/Component/VJournalTest.php +++ b/tests/VObject/Component/VJournalTest.php @@ -2,12 +2,13 @@ namespace Sabre\VObject\Component; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Sabre\VObject\Reader; class VJournalTest extends TestCase { - #[\PHPUnit\Framework\Attributes\DataProvider('timeRangeTestData')] + #[DataProvider('timeRangeTestData')] public function testInTimeRange(VJournal $vtodo, \DateTime $start, \DateTime $end, bool $outcome): void { self::assertEquals($outcome, $vtodo->isInTimeRange($start, $end)); diff --git a/tests/VObject/Component/VTodoTest.php b/tests/VObject/Component/VTodoTest.php index 4dae22d42..c8be4f692 100644 --- a/tests/VObject/Component/VTodoTest.php +++ b/tests/VObject/Component/VTodoTest.php @@ -2,12 +2,13 @@ namespace Sabre\VObject\Component; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Sabre\VObject\Reader; class VTodoTest extends TestCase { - #[\PHPUnit\Framework\Attributes\DataProvider('timeRangeTestData')] + #[DataProvider('timeRangeTestData')] public function testInTimeRange(VTodo $vtodo, \DateTime $start, \DateTime $end, bool $outcome): void { self::assertEquals($outcome, $vtodo->isInTimeRange($start, $end)); diff --git a/tests/VObject/ComponentTest.php b/tests/VObject/ComponentTest.php index 6f4f49c67..a9be399b5 100644 --- a/tests/VObject/ComponentTest.php +++ b/tests/VObject/ComponentTest.php @@ -2,6 +2,7 @@ namespace Sabre\VObject; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Sabre\VObject\Component\VCalendar; use Sabre\VObject\Component\VCard; @@ -461,7 +462,7 @@ public function testRemoveNotFound(): void /** * @param string[] $componentList */ - #[\PHPUnit\Framework\Attributes\DataProvider('ruleData')] + #[DataProvider('ruleData')] public function testValidateRules(array $componentList, int $errorCount): void { $vcard = new VCard(); diff --git a/tests/VObject/DateTimeParserTest.php b/tests/VObject/DateTimeParserTest.php index 452b61185..077d2e1fc 100644 --- a/tests/VObject/DateTimeParserTest.php +++ b/tests/VObject/DateTimeParserTest.php @@ -2,6 +2,7 @@ namespace Sabre\VObject; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class DateTimeParserTest extends TestCase @@ -169,7 +170,7 @@ public function testParseICalendarDateInvalidDate(): void /** * @param array $output */ - #[\PHPUnit\Framework\Attributes\DataProvider('vcardDates')] + #[DataProvider('vcardDates')] public function testVCardDate(string $input, array $output): void { self::assertEquals( diff --git a/tests/VObject/Parser/MimeDirTest.php b/tests/VObject/Parser/MimeDirTest.php index 6b383129e..5465efc35 100644 --- a/tests/VObject/Parser/MimeDirTest.php +++ b/tests/VObject/Parser/MimeDirTest.php @@ -2,6 +2,7 @@ namespace Sabre\VObject\Parser; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Sabre\VObject\Component\VCalendar; use Sabre\VObject\ParseException; @@ -109,7 +110,7 @@ public static function provideEmptyParserInput(): array ]; } - #[\PHPUnit\Framework\Attributes\DataProvider('provideEmptyParserInput')] + #[DataProvider('provideEmptyParserInput')] public function testParseEmpty($input, $expectedExceptionMessage): void { $this->expectException(ParseException::class); @@ -187,7 +188,7 @@ public function testParsingTwiceSameContent(): void /** * @covers \Sabre\VObject\Parser\MimeDir::readProperty */ - #[\PHPUnit\Framework\Attributes\DataProvider('provideBrokenVCalendar')] + #[DataProvider('provideBrokenVCalendar')] public function testBrokenMultilineContentDoesNotBreakImportWhenSetToIgnoreBrokenLines(string $vcalendar): void { $mimeDir = new MimeDir(null, MimeDir::OPTION_IGNORE_INVALID_LINES); @@ -200,7 +201,7 @@ public function testBrokenMultilineContentDoesNotBreakImportWhenSetToIgnoreBroke * * @param string $vcalendar */ - #[\PHPUnit\Framework\Attributes\DataProvider('provideBrokenVCalendar')] + #[DataProvider('provideBrokenVCalendar')] public function testBrokenMultilineContentDoesBreakImport($vcalendar): void { $mimeDir = new MimeDir(); diff --git a/tests/VObject/Property/ICalendar/CalAddressTest.php b/tests/VObject/Property/ICalendar/CalAddressTest.php index b74d9865f..a81656d25 100644 --- a/tests/VObject/Property/ICalendar/CalAddressTest.php +++ b/tests/VObject/Property/ICalendar/CalAddressTest.php @@ -2,11 +2,12 @@ namespace Sabre\VObject\Property\ICalendar; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class CalAddressTest extends TestCase { - #[\PHPUnit\Framework\Attributes\DataProvider('values')] + #[DataProvider('values')] public function testGetNormalizedValue(string $expected, string $input): void { $vobj = new \Sabre\VObject\Component\VCalendar(); diff --git a/tests/VObject/Property/VCard/DateAndOrTimeTest.php b/tests/VObject/Property/VCard/DateAndOrTimeTest.php index 8c78a6171..b85e915f1 100644 --- a/tests/VObject/Property/VCard/DateAndOrTimeTest.php +++ b/tests/VObject/Property/VCard/DateAndOrTimeTest.php @@ -2,13 +2,14 @@ namespace Sabre\VObject\Property\VCard; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Sabre\VObject; use Sabre\VObject\Reader; class DateAndOrTimeTest extends TestCase { - #[\PHPUnit\Framework\Attributes\DataProvider('dates')] + #[DataProvider('dates')] public function testGetJsonValue(string $input, string $output): void { $vcard = new VObject\Component\VCard(); diff --git a/tests/VObject/Recur/RRuleIteratorTest.php b/tests/VObject/Recur/RRuleIteratorTest.php index 41de0e385..0d14cbae5 100644 --- a/tests/VObject/Recur/RRuleIteratorTest.php +++ b/tests/VObject/Recur/RRuleIteratorTest.php @@ -2,6 +2,7 @@ namespace Sabre\VObject\Recur; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Sabre\VObject\InvalidDataException; @@ -32,7 +33,7 @@ public function testHourly(): void /** * @param string[] $expected */ - #[\PHPUnit\Framework\Attributes\DataProvider('dst2HourlyTransitionProvider')] + #[DataProvider('dst2HourlyTransitionProvider')] public function test2HourlyOnDstTransition(string $start, array $expected): void { $this->parse( @@ -91,7 +92,7 @@ public static function dst2HourlyTransitionProvider(): iterable /** * @param string[] $expected */ - #[\PHPUnit\Framework\Attributes\DataProvider('dst6HourlyTransitionProvider')] + #[DataProvider('dst6HourlyTransitionProvider')] public function testHourlyOnDstTransition(string $start, array $expected): void { $this->parse( @@ -283,7 +284,7 @@ public function testDailyBySetPosLoop(): void /** * @param string[] $expected */ - #[\PHPUnit\Framework\Attributes\DataProvider('dstDailyTransitionProvider')] + #[DataProvider('dstDailyTransitionProvider')] public function testDailyOnDstTransition(string $start, array $expected): void { $this->parse( @@ -491,7 +492,7 @@ public function testWeeklyByDayByHourOnDstTransition(): void /** * @param string[] $expected */ - #[\PHPUnit\Framework\Attributes\DataProvider('dstWeeklyTransitionProvider')] + #[DataProvider('dstWeeklyTransitionProvider')] public function testWeeklyOnDstTransition(string $start, array $expected): void { $this->parse( @@ -744,7 +745,7 @@ public function testMonthlyByDayBySetPos(): void /** * @param string[] $expected */ - #[\PHPUnit\Framework\Attributes\DataProvider('dstMonthlyTransitionProvider')] + #[DataProvider('dstMonthlyTransitionProvider')] public function testMonthlyOnDstTransition(string $start, array $expected): void { $this->parse( @@ -1154,7 +1155,7 @@ public function testYearlyByDayByWeekNo(): void /** * @param string[] $expected */ - #[\PHPUnit\Framework\Attributes\DataProvider('dstYearlyTransitionProvider')] + #[DataProvider('dstYearlyTransitionProvider')] public function testYearlyOnDstTransition(string $start, array $expected): void { $this->parse( @@ -1428,7 +1429,7 @@ public function testYearlyBySetPosLoop(): void * * @param string[] $expected */ - #[\PHPUnit\Framework\Attributes\DataProvider('yearlyStartDateNotOnRRuleListProvider')] + #[DataProvider('yearlyStartDateNotOnRRuleListProvider')] public function testYearlyStartDateNotOnRRuleList(string $rule, string $start, array $expected): void { $this->parse($rule, $start, $expected); diff --git a/tests/VObject/TimeZoneUtilTest.php b/tests/VObject/TimeZoneUtilTest.php index 1162c0428..3690e64c2 100644 --- a/tests/VObject/TimeZoneUtilTest.php +++ b/tests/VObject/TimeZoneUtilTest.php @@ -2,6 +2,7 @@ namespace Sabre\VObject; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class TimeZoneUtilTest extends TestCase @@ -11,7 +12,7 @@ public function setUp(): void TimeZoneUtil::clean(); } - #[\PHPUnit\Framework\Attributes\DataProvider('getMapping')] + #[DataProvider('getMapping')] public function testCorrectTZ(string $timezoneName): void { try { @@ -161,7 +162,7 @@ public function testWindowsTimeZone(): void self::assertEquals($ex->getName(), $tz->getName()); } - #[\PHPUnit\Framework\Attributes\DataProvider('getPHPTimeZoneIdentifiers')] + #[DataProvider('getPHPTimeZoneIdentifiers')] public function testTimeZoneIdentifiers(string $tzid): void { $tz = TimeZoneUtil::getTimeZone($tzid); @@ -170,7 +171,7 @@ public function testTimeZoneIdentifiers(string $tzid): void self::assertEquals($ex->getName(), $tz->getName()); } - #[\PHPUnit\Framework\Attributes\DataProvider('getPHPTimeZoneBCIdentifiers')] + #[DataProvider('getPHPTimeZoneBCIdentifiers')] public function testTimeZoneBCIdentifiers(string $tzid): void { /* diff --git a/tests/VObject/TimezoneGuesser/FindFromTimezoneMapTest.php b/tests/VObject/TimezoneGuesser/FindFromTimezoneMapTest.php index d543ae75b..d7958ebec 100644 --- a/tests/VObject/TimezoneGuesser/FindFromTimezoneMapTest.php +++ b/tests/VObject/TimezoneGuesser/FindFromTimezoneMapTest.php @@ -4,6 +4,7 @@ namespace Sabre\VObject\TimezoneGuesser; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class FindFromTimezoneMapTest extends TestCase @@ -12,7 +13,7 @@ class FindFromTimezoneMapTest extends TestCase * Verify that previously-deprecated IANA names have been replaced with * their canonical successors and resolve correctly. */ - #[\PHPUnit\Framework\Attributes\DataProvider('updatedTimezoneProvider')] + #[DataProvider('updatedTimezoneProvider')] public function testUpdatedTimezonesResolve(string $mapKey, string $expectedOlson): void { $finder = new FindFromTimezoneMap(); From c61f57fdfca3387c0ac4e8cb74b9c15130ceaed4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 23:32:41 +0000 Subject: [PATCH 15/23] chore(deps): bump actions/cache from 5.0.5 to 6.1.0 Bumps [actions/cache](https://github.com/actions/cache) from 5.0.5 to 6.1.0. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/27d5ce7f107fe9357f9df03efb73ab90386fccae...55cc8345863c7cc4c66a329aec7e433d2d1c52a9) --- updated-dependencies: - dependency-name: actions/cache dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34724f7af..1453e07ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,7 +45,7 @@ jobs: run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache composer dependencies - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ${{ steps.composer-cache.outputs.dir }} # Use composer.json for key, if composer.lock is not committed. From 8d81b947065cc4987d64bdc3c95a6f23c77be6b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 23:32:44 +0000 Subject: [PATCH 16/23] chore(deps): bump actions/checkout from 6.0.3 to 7.0.0 Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.3 to 7.0.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/df4cb1c069e1874edd31b4311f1884172cec0e10...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34724f7af..1b34dc6c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: rector-check: 'yes' steps: - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Setup PHP, with composer and extensions uses: shivammathur/setup-php@7c071dfe9dc99bdf297fa79cb49ea005b9fcadbc # v2.37.1 From 5ab2c27738959a216129808e653eb022e2773607 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 23:32:47 +0000 Subject: [PATCH 17/23] chore(deps): bump shivammathur/setup-php from 2.37.1 to 2.37.2 Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.37.1 to 2.37.2. - [Release notes](https://github.com/shivammathur/setup-php/releases) - [Commits](https://github.com/shivammathur/setup-php/compare/7c071dfe9dc99bdf297fa79cb49ea005b9fcadbc...f3e473d116dcccaddc5834248c87452386958240) --- updated-dependencies: - dependency-name: shivammathur/setup-php dependency-version: 2.37.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34724f7af..0e7507e69 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@7c071dfe9dc99bdf297fa79cb49ea005b9fcadbc # v2.37.1 + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2 with: php-version: ${{ matrix.php-versions }} extensions: mbstring, dom, fileinfo, mysql, redis, opcache From 04c0bc4f906ecc213e5371cdaba645e41863596c Mon Sep 17 00:00:00 2001 From: Yadd Date: Tue, 14 Oct 2025 10:16:44 +0200 Subject: [PATCH 18/23] feat: preserve UTC 'Z' indicator in Period JSON serialization When serializing PERIOD values to JSON (for jCal format), preserve the 'Z' timezone indicator for UTC datetimes. This ensures FREEBUSY periods maintain explicit UTC designation, avoiding timezone ambiguity. Without this change: FREEBUSY:20120226T230000Z/20120226T230000Z => serializes to: ["2012-02-26T23:00:00", "2012-02-26T23:00:00"] With this change: FREEBUSY:20120226T230000Z/20120226T230000Z => serializes to: ["2012-02-26T23:00:00Z", "2012-02-26T23:00:00Z"] This behavior is consistent with RFC 5545 which requires UTC times to be designated with the 'Z' suffix. Related: https://github.com/sabre-io/vobject/issues/411 --- lib/Property/ICalendar/Period.php | 15 +++++--- tests/VObject/JCalTest.php | 2 +- tests/VObject/Parser/JsonTest.php | 4 +- tests/VObject/Parser/XmlTest.php | 10 ++--- .../Property/ICalendar/DateTimeTest.php | 38 +++++++++++++++++-- 5 files changed, 52 insertions(+), 17 deletions(-) diff --git a/lib/Property/ICalendar/Period.php b/lib/Property/ICalendar/Period.php index 15291e805..da9d972b1 100644 --- a/lib/Property/ICalendar/Period.php +++ b/lib/Property/ICalendar/Period.php @@ -70,6 +70,11 @@ public function setJsonValue(array $value): void parent::setJsonValue($value); } + public function appendUtc(string $strDate) + { + return !str_ends_with($strDate, 'Z') ? '' : 'Z'; + } + /** * Returns the value, in the format it should be encoded for json. * @@ -83,19 +88,19 @@ public function getJsonValue(): array foreach ($this->getParts() as $item) { [$start, $end] = explode('/', (string) $item, 2); - $start = DateTimeParser::parseDateTime($start); + $startDt = DateTimeParser::parseDateTime($start)->format('Y-m-d\\TH:i:s').$this->appendUtc($start); // This is a duration value. if ('P' === $end[0]) { $return[] = [ - $start->format('Y-m-d\\TH:i:s'), + $startDt, $end, ]; } else { - $end = DateTimeParser::parseDateTime($end); + $endDt = DateTimeParser::parseDateTime($end)->format('Y-m-d\\TH:i:s').$this->appendUtc($end); $return[] = [ - $start->format('Y-m-d\\TH:i:s'), - $end->format('Y-m-d\\TH:i:s'), + $startDt, + $endDt, ]; } } diff --git a/tests/VObject/JCalTest.php b/tests/VObject/JCalTest.php index fa3479665..7624b8f84 100644 --- a/tests/VObject/JCalTest.php +++ b/tests/VObject/JCalTest.php @@ -97,7 +97,7 @@ public function testToJCal(): void 'sequence', new \stdClass(), 'integer', 5, ], [ - 'freebusy', new \stdClass(), 'period', ['2013-05-26T21:02:13', 'PT1H'], ['2013-06-26T12:00:00', '2013-06-26T13:00:00'], + 'freebusy', new \stdClass(), 'period', ['2013-05-26T21:02:13Z', 'PT1H'], ['2013-06-26T12:00:00Z', '2013-06-26T13:00:00Z'], ], [ 'url', new \stdClass(), 'uri', 'http://example.org/', diff --git a/tests/VObject/Parser/JsonTest.php b/tests/VObject/Parser/JsonTest.php index 392cc7c11..c50af87c4 100644 --- a/tests/VObject/Parser/JsonTest.php +++ b/tests/VObject/Parser/JsonTest.php @@ -261,7 +261,7 @@ public function testRoundTripJCal(): void 'sequence', new \stdClass(), 'integer', 5, ], [ - 'freebusy', new \stdClass(), 'period', ['2013-05-26T21:02:13', 'PT1H'], ['2013-06-26T12:00:00', '2013-06-26T13:00:00'], + 'freebusy', new \stdClass(), 'period', ['2013-05-26T21:02:13Z', 'PT1H'], ['2013-06-26T12:00:00Z', '2013-06-26T13:00:00Z'], ], [ 'url', new \stdClass(), 'uri', 'http://example.org/', @@ -330,7 +330,7 @@ public function testRoundTripJCal(): void ATTENDEE;CN=Dominik;PARTSTAT=DECLINED:mailto:dominik@example.org GEO:51.96668;7.61876 SEQUENCE:5 -FREEBUSY:20130526T210213/PT1H,20130626T120000/20130626T130000 +FREEBUSY:20130526T210213Z/PT1H,20130626T120000Z/20130626T130000Z URL;VALUE=URI:http://example.org/ TZOFFSETFROM:+0500 RRULE:FREQ=WEEKLY;BYDAY=MO,TU diff --git a/tests/VObject/Parser/XmlTest.php b/tests/VObject/Parser/XmlTest.php index 1ad36474d..223b0f105 100644 --- a/tests/VObject/Parser/XmlTest.php +++ b/tests/VObject/Parser/XmlTest.php @@ -684,7 +684,7 @@ public function testRFC6321Section3Part6Part9(): void - 2011-05-17T12:00:00 + 2011-05-17T12:00:00Z P1H @@ -693,7 +693,7 @@ public function testRFC6321Section3Part6Part9(): void XML, 'BEGIN:VCALENDAR'."\n". - 'FREEBUSY:20110517T120000/P1H'."\n". + 'FREEBUSY:20110517T120000Z/P1H'."\n". 'END:VCALENDAR'."\n" ); @@ -705,8 +705,8 @@ public function testRFC6321Section3Part6Part9(): void - 2011-05-17T12:00:00 - 2012-05-17T12:00:00 + 2011-05-17T12:00:00Z + 2012-05-17T12:00:00Z @@ -714,7 +714,7 @@ public function testRFC6321Section3Part6Part9(): void XML, 'BEGIN:VCALENDAR'."\n". - 'FREEBUSY:20110517T120000/20120517T120000'."\n". + 'FREEBUSY:20110517T120000Z/20120517T120000Z'."\n". 'END:VCALENDAR'."\n" ); } diff --git a/tests/VObject/Property/ICalendar/DateTimeTest.php b/tests/VObject/Property/ICalendar/DateTimeTest.php index f0728d3ed..bb87eada6 100644 --- a/tests/VObject/Property/ICalendar/DateTimeTest.php +++ b/tests/VObject/Property/ICalendar/DateTimeTest.php @@ -2,6 +2,7 @@ namespace Sabre\VObject\Property\ICalendar; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Sabre\VObject\Component\VCalendar; use Sabre\VObject\InvalidDataException; @@ -320,12 +321,41 @@ public function testUpdateValueParameter(): void self::assertEquals("DTSTART;VALUE=DATE:20130607\r\n", $dtStart->serialize()); } - public function testValidate(): void + public static function validateDateTimeProvider(): array { - $exDate = $this->vcal->createProperty('EXDATE', '-00011130T143000Z'); + return [ + [ + '-00011130T143000Z', + 1, + 3, + ], + [ + '20260627T100000', + 0, + 0, + ], + [ + '20260627T100000Z', + 0, + 0, + ], + [ + '20260627T100000Z+0200', + 1, + 3, + ], + ]; + } + + #[DataProvider('validateDateTimeProvider')] + public function testValidate(string $invalidDateTime, int $expectedMessageCount, int $expectedMessageLevel): void + { + $exDate = $this->vcal->createProperty('EXDATE', $invalidDateTime); $messages = $exDate->validate(); - self::assertCount(1, $messages); - self::assertEquals(3, $messages[0]['level']); + self::assertCount($expectedMessageCount, $messages); + if ($expectedMessageCount > 0) { + self::assertEquals($expectedMessageLevel, $messages[0]['level']); + } } /** From af78936eddf0da7bf9a58189ccf9f01eb8320e55 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Tue, 7 Jul 2026 12:48:27 +0930 Subject: [PATCH 19/23] chore: prepare release 4.6.1 --- CHANGELOG.md | 4 ++++ lib/Version.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f69e8e57a..b1d82e757 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ ChangeLog ========= +4.6.1 (2026-07-07) +------------------ +* #776 fix: preserve UTC 'Z' indicator in Period JSON serialization (@bastien-roucaries @guimard @phil-davis) + 4.6.0 (2026-05-31) ------------------ * #718 feat(VCard): add getByTypes method (@JimKnoxx) diff --git a/lib/Version.php b/lib/Version.php index 72391f59a..5bee64427 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -14,5 +14,5 @@ class Version /** * Full version number. */ - public const VERSION = '4.6.0'; + public const VERSION = '4.6.1'; } From fb5863d18391818b2388191a7ba5840da83afeea Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 15 Jun 2026 19:17:53 +0930 Subject: [PATCH 20/23] chore: prepare major release 5.0.0 --- CHANGELOG.md | 13 +++++++++++++ lib/Version.php | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1d82e757..d06c310d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,19 @@ ChangeLog ========= +5.0.0 (2026-07-07) +------------------ +* #767 Drop PHP 7.4 8.0 8.1 and support PHP 8.2 and up (@phil-davis) +* #769 fix: minor code fixes (@phil-davis) +* #586 PHP types (@tcitworld @phil-davis) + +This major release v5 declares function parameter and return types across the +codebase. Users will need to be careful to call functions/methods with the +correct types. + +Support for PHP below 8.2 has been dropped. +This release supports PHP 8.2 and up. + 4.6.1 (2026-07-07) ------------------ * #776 fix: preserve UTC 'Z' indicator in Period JSON serialization (@bastien-roucaries @guimard @phil-davis) diff --git a/lib/Version.php b/lib/Version.php index 5bee64427..e851a6507 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -14,5 +14,5 @@ class Version /** * Full version number. */ - public const VERSION = '4.6.1'; + public const VERSION = '5.0.0'; } From 32c942b3ce0a2373def1dd83880f0724b21f665d Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 3 Aug 2026 00:10:54 +0930 Subject: [PATCH 21/23] chore: drop deprecated AddParamArrayDocblockFromDataProviderRector from rector Signed-off-by: Phillip Davis (cherry picked from commit 4a3e8c9a33a861f41b4590c2d27c8cb41a79bbda) --- rector.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/rector.php b/rector.php index 5a4426098..bfb4215a9 100644 --- a/rector.php +++ b/rector.php @@ -4,7 +4,6 @@ use Rector\Config\RectorConfig; use Rector\PHPUnit\AnnotationsToAttributes\Rector\ClassMethod\DataProviderAnnotationToAttributeRector; -use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddParamArrayDocblockFromDataProviderRector; return RectorConfig::configure() ->withPaths([ @@ -14,7 +13,6 @@ // uncomment to reach your current PHP version ->withPhpSets(false, true) ->withRules([ - AddParamArrayDocblockFromDataProviderRector::class, DataProviderAnnotationToAttributeRector::class, ]) ->withTypeCoverageLevel(0) From 3473176c21f3cc981a2c759237c68bae87106846 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Fri, 4 Sep 2026 12:42:35 +0930 Subject: [PATCH 22/23] chore: apply rector IfToNullCoalescingAssignRector rule And require ar least rector 2.6 Signed-off-by: Phillip Davis (cherry picked from commit cd2fa3b3f5a4259f7ee116d0c597184584b244d5) --- composer.json | 2 +- lib/Component/VCard.php | 4 +--- lib/DateTimeParser.php | 4 +--- lib/Document.php | 8 ++------ lib/FreeBusyGenerator.php | 4 +--- lib/ITip/Broker.php | 4 +--- lib/Parser/MimeDir.php | 8 ++------ lib/Recur/EventIterator.php | 4 +--- lib/TimeZoneUtil.php | 4 +--- 9 files changed, 11 insertions(+), 31 deletions(-) diff --git a/composer.json b/composer.json index a03c434ca..e65762e17 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "phpstan/extension-installer": "^1.4", "phpunit/phpunit": "^11.5", "phpunit/php-invoker" : "^5.0", - "rector/rector": "^2.4" + "rector/rector": "^2.6" }, "suggest" : { "hoa/bench" : "If you would like to run the benchmark scripts" diff --git a/lib/Component/VCard.php b/lib/Component/VCard.php index 7c5ca739c..bbb852cc2 100644 --- a/lib/Component/VCard.php +++ b/lib/Component/VCard.php @@ -481,9 +481,7 @@ public function xmlSerialize(Xml\Writer $writer): void foreach ($this->children() as $property) { $group = $property->group; - if (!isset($propertiesByGroup[$group])) { - $propertiesByGroup[$group] = []; - } + $propertiesByGroup[$group] ??= []; $propertiesByGroup[$group][] = $property; } diff --git a/lib/DateTimeParser.php b/lib/DateTimeParser.php index 9469cacea..9b0c55b2e 100644 --- a/lib/DateTimeParser.php +++ b/lib/DateTimeParser.php @@ -63,9 +63,7 @@ public static function parseDate(string $date, ?\DateTimeZone $tz = null): \Date throw new InvalidDataException('The supplied iCalendar date value is incorrect: '.$date); } - if (is_null($tz)) { - $tz = new \DateTimeZone('UTC'); - } + $tz ??= new \DateTimeZone('UTC'); try { $date = new \DateTimeImmutable($matches[1].'-'.$matches[2].'-'.$matches[3], $tz); diff --git a/lib/Document.php b/lib/Document.php index 916152438..d07e2af93 100644 --- a/lib/Document.php +++ b/lib/Document.php @@ -146,9 +146,7 @@ public function createComponent(string $name, ?array $children = null, bool $def if (isset(static::$componentMap[$name])) { $class = static::$componentMap[$name]; } - if (is_null($children)) { - $children = []; - } + $children ??= []; return new $class($this, $name, $children, $defaults); } @@ -205,9 +203,7 @@ public function createProperty(string $name, $value = null, ?array $parameters = } } - if (is_null($parameters)) { - $parameters = []; - } + $parameters ??= []; return new $class($this, $name, $value, $parameters, $group, $lineIndex, $lineString); } diff --git a/lib/FreeBusyGenerator.php b/lib/FreeBusyGenerator.php index 3bcc842e3..9eec5c8d7 100644 --- a/lib/FreeBusyGenerator.php +++ b/lib/FreeBusyGenerator.php @@ -79,9 +79,7 @@ public function __construct(?\DateTimeInterface $start = null, ?\DateTimeInterfa if ($objects) { $this->setObjects($objects); } - if (is_null($timeZone)) { - $timeZone = new \DateTimeZone('UTC'); - } + $timeZone ??= new \DateTimeZone('UTC'); $this->setTimeZone($timeZone); } diff --git a/lib/ITip/Broker.php b/lib/ITip/Broker.php index 42862cd8b..19de8b16b 100644 --- a/lib/ITip/Broker.php +++ b/lib/ITip/Broker.php @@ -609,9 +609,7 @@ protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo, // We're adding PARTSTAT=NEEDS-ACTION to ensure that // iOS shows an "Inbox Item" - if (!isset($currentEventAttendee['PARTSTAT'])) { - $currentEventAttendee['PARTSTAT'] = 'NEEDS-ACTION'; - } + $currentEventAttendee['PARTSTAT'] ??= 'NEEDS-ACTION'; } } diff --git a/lib/Parser/MimeDir.php b/lib/Parser/MimeDir.php index c1d621a5d..85fd4156e 100644 --- a/lib/Parser/MimeDir.php +++ b/lib/Parser/MimeDir.php @@ -436,9 +436,7 @@ protected function readProperty(string $line) } if (isset($match['paramName'])) { $lastParam = strtoupper($match['paramName']); - if (!isset($property['parameters'][$lastParam])) { - $property['parameters'][$lastParam] = null; - } + $property['parameters'][$lastParam] ??= null; $lastToken = self::TOKEN_PARAMNAME; continue; } @@ -458,9 +456,7 @@ protected function readProperty(string $line) // @codeCoverageIgnoreEnd } - if (\is_null($property['value'])) { - $property['value'] = ''; - } + $property['value'] ??= ''; if (!isset($property['name']) || 0 === strlen($property['name'])) { if ($this->options & self::OPTION_IGNORE_INVALID_LINES) { return false; diff --git a/lib/Recur/EventIterator.php b/lib/Recur/EventIterator.php index d0317e14b..5db409350 100644 --- a/lib/Recur/EventIterator.php +++ b/lib/Recur/EventIterator.php @@ -91,9 +91,7 @@ class EventIterator implements \Iterator */ public function __construct($input, ?string $uid = null, ?\DateTimeZone $timeZone = null) { - if (is_null($timeZone)) { - $timeZone = new \DateTimeZone('UTC'); - } + $timeZone ??= new \DateTimeZone('UTC'); $this->timeZone = $timeZone; if (is_array($input)) { diff --git a/lib/TimeZoneUtil.php b/lib/TimeZoneUtil.php index b75a6310d..0c3bf8e57 100644 --- a/lib/TimeZoneUtil.php +++ b/lib/TimeZoneUtil.php @@ -50,9 +50,7 @@ private function __construct() private static function getInstance(): self { - if (null === self::$instance) { - self::$instance = new self(); - } + self::$instance ??= new self(); return self::$instance; } From 3b084ae573726e85b2ef6ad57b2dea1756d08270 Mon Sep 17 00:00:00 2001 From: William Gotti Date: Wed, 30 Sep 2026 15:25:50 +0200 Subject: [PATCH 23/23] Adapt Proton code to 5.x tooling Brings the fork-specific code in line with the upstream 5.0.0 toolchain (PHPUnit 11, PHPStan strict rules, Rector, php-cs-fixer): - tests: static data providers, DataProvider attributes and self::assert* calls - RRuleIterator: promoted readonly $yearlySkipUpperLimit, match for the frequency coefficient, integer tuple comparison when sorting occurrences, and ruleValuesOrCurrent() instead of short ternaries - GuessFromCustomizedTimeZone: explicit checks on getTransitions(), which returns array|false No behaviour change intended; the full test suite passes. --- lib/Recur/RRuleIterator.php | 67 +++---- lib/TimezoneGuesser/FindFromOffsetName.php | 2 +- .../GuessFromCustomizedTimeZone.php | 4 +- tests/VObject/Component/VTimeZoneTest.php | 2 +- tests/VObject/InvalidValueParamTest.php | 4 +- tests/VObject/Parser/UnfoldingTest.php | 4 +- tests/VObject/Recur/FastForwardBeforeTest.php | 166 +++++++++--------- tests/VObject/Recur/FastForwardTest.php | 140 +++++++-------- tests/VObject/Recur/FastForwardToEndTest.php | 48 ++--- tests/VObject/Recur/RRuleIteratorTest.php | 14 +- tests/VObject/TimeZoneUtilTest.php | 34 ++-- 11 files changed, 229 insertions(+), 256 deletions(-) diff --git a/lib/Recur/RRuleIterator.php b/lib/Recur/RRuleIterator.php index 50966a9be..b59c5c946 100644 --- a/lib/Recur/RRuleIterator.php +++ b/lib/Recur/RRuleIterator.php @@ -28,8 +28,6 @@ class RRuleIterator implements \Iterator */ public const dateUpperLimit = 253402300799; - private bool $yearlySkipUpperLimit; - /** * Creates the Iterator. * @@ -42,11 +40,10 @@ public function __construct($rrule, /** * * All calculations are based on this initial date. */ - protected \DateTimeInterface $startDate, bool $yearlySkipUpperLimit = true) + protected \DateTimeInterface $startDate, private readonly bool $yearlySkipUpperLimit = true) { $this->parseRRule($rrule); $this->currentDate = clone $this->startDate; - $this->yearlySkipUpperLimit = $yearlySkipUpperLimit; } /* Implementation of the Iterator interface {{{ */ @@ -229,27 +226,14 @@ public function getFrequency(): string */ private function getFrequencyCoeff() { - $frequencyCoeff = null; - - switch ($this->frequency) { - case 'hourly': - $frequencyCoeff = 1 / 24; - break; - case 'daily': - $frequencyCoeff = 1; - break; - case 'weekly': - $frequencyCoeff = 7; - break; - case 'monthly': - $frequencyCoeff = 30; - break; - case 'yearly': - $frequencyCoeff = 365; - break; - } - - return $frequencyCoeff; + return match ($this->frequency) { + 'hourly' => 1 / 24, + 'daily' => 1, + 'weekly' => 7, + 'monthly' => 30, + 'yearly' => 365, + default => null, + }; } /** @@ -301,6 +285,18 @@ private function jumpForward(\DateTimeInterface $dt): void } } + /** + * Falls back to the current value when the BYSECOND, BYMINUTE or BYHOUR rule part is absent. + * + * @param array|null $ruleValues + * + * @return array + */ + private function ruleValuesOrCurrent(?array $ruleValues, int $current): array + { + return null === $ruleValues || [] === $ruleValues ? [$current] : $ruleValues; + } + /** * Occurrences are [day, hour, minute, second] tuples, compared in that order. * @@ -1168,19 +1164,8 @@ protected function getMonthlyOccurrences(): array $result = $this->addDailyOccurences($result); $result = array_unique($result, SORT_REGULAR); - $sortLex = function ($a, $b) { - if ($a[0] != $b[0]) { - return $a[0] - $b[0]; - } - if ($a[1] != $b[1]) { - return $a[1] - $b[1]; - } - if ($a[2] != $b[2]) { - return $a[2] - $b[2]; - } - - return $a[3] - $b[3]; - }; + // BYHOUR/BYMINUTE/BYSECOND values are numeric strings, so compare the tuples as integers. + $sortLex = static fn (array $a, array $b): int => array_map(intval(...), $a) <=> array_map(intval(...), $b); usort($result, $sortLex); // The last thing that needs checking is the BYSETPOS. If it's set, it @@ -1218,9 +1203,9 @@ protected function addDailyOccurences(array $result): array $minute = (int) $this->currentDate->format('i'); $second = (int) $this->currentDate->format('s'); foreach ($result as $day) { - $seconds = $this->bySecond ? $this->bySecond : [$second]; - $minutes = $this->byMinute ? $this->byMinute : [$minute]; - $hours = $this->byHour ? $this->byHour : [$hour]; + $seconds = $this->ruleValuesOrCurrent($this->bySecond, $second); + $minutes = $this->ruleValuesOrCurrent($this->byMinute, $minute); + $hours = $this->ruleValuesOrCurrent($this->byHour, $hour); foreach ($hours as $h) { foreach ($minutes as $m) { foreach ($seconds as $s) { diff --git a/lib/TimezoneGuesser/FindFromOffsetName.php b/lib/TimezoneGuesser/FindFromOffsetName.php index 0916f1283..efefcc13a 100644 --- a/lib/TimezoneGuesser/FindFromOffsetName.php +++ b/lib/TimezoneGuesser/FindFromOffsetName.php @@ -44,7 +44,7 @@ public function find(string $tzid, ?bool $failIfUncertain = false): ?\DateTimeZo $tzid = new \DateTimeZone($tzid); return new \DateTimeZone(self::$offsetTimezones[$tzid->getName()]) ?? null; - } catch (\Exception $e) { + } catch (\Exception) { return null; } } diff --git a/lib/TimezoneGuesser/GuessFromCustomizedTimeZone.php b/lib/TimezoneGuesser/GuessFromCustomizedTimeZone.php index f1f4f6899..56d872864 100644 --- a/lib/TimezoneGuesser/GuessFromCustomizedTimeZone.php +++ b/lib/TimezoneGuesser/GuessFromCustomizedTimeZone.php @@ -51,7 +51,7 @@ public function guess(VTimeZone $vtimezone, ?bool $failIfUncertain = false): ?\D // check standard $timestamp = $standardIterator->current()->getTimestamp(); $transitions = $tz->getTransitions($timestamp + $day, $timestamp + $day + 1); - if (empty($transitions)) { + if (false === $transitions || [] === $transitions) { continue; } @@ -68,7 +68,7 @@ public function guess(VTimeZone $vtimezone, ?bool $failIfUncertain = false): ?\D // check daylight $timestamp = $daylightIterator->current()->getTimestamp(); $transitions = $tz->getTransitions($timestamp + $day, $timestamp + $day + 1); - if (empty($transitions)) { + if (false === $transitions || [] === $transitions) { continue; } diff --git a/tests/VObject/Component/VTimeZoneTest.php b/tests/VObject/Component/VTimeZoneTest.php index 19a88cb74..bb8102d89 100644 --- a/tests/VObject/Component/VTimeZoneTest.php +++ b/tests/VObject/Component/VTimeZoneTest.php @@ -67,7 +67,7 @@ public function testGetEmptyTimeZone() $tz = new \DateTimeZone(date_default_timezone_get()); - $this->assertEquals( + self::assertEquals( $tz, $obj->VTIMEZONE->getTimeZone() ); diff --git a/tests/VObject/InvalidValueParamTest.php b/tests/VObject/InvalidValueParamTest.php index 744fb193c..a73b7229d 100644 --- a/tests/VObject/InvalidValueParamTest.php +++ b/tests/VObject/InvalidValueParamTest.php @@ -26,7 +26,7 @@ public function testWorkaround() ICS; $doc = Reader::read($event); - $this->assertEquals("LOCATION:EXAMPLE\r\n", $doc->VEVENT->LOCATION->serialize()); + self::assertEquals("LOCATION:EXAMPLE\r\n", $doc->VEVENT->LOCATION->serialize()); } public function testInvalidValue() @@ -49,6 +49,6 @@ public function testInvalidValue() ICS; $doc = Reader::read($event); - $this->assertEquals("LOCATION:consectetur adipiscing elit\,sed do eiusmod tempor\r\n", $doc->VEVENT->LOCATION->serialize()); + self::assertEquals("LOCATION:consectetur adipiscing elit\,sed do eiusmod tempor\r\n", $doc->VEVENT->LOCATION->serialize()); } } diff --git a/tests/VObject/Parser/UnfoldingTest.php b/tests/VObject/Parser/UnfoldingTest.php index 8d751ae7a..1bccfb116 100644 --- a/tests/VObject/Parser/UnfoldingTest.php +++ b/tests/VObject/Parser/UnfoldingTest.php @@ -36,7 +36,7 @@ public function testFixUnfoldingICS() $mimeDir = new MimeDir(); $vcard = $mimeDir->parse($vcard, Reader::OPTION_FIX_UNFOLDING); - $this->assertNotNull($vcard->children()[0]->{'X-APPLE-STRUCTURED-LOCATION'}->getValue()); + self::assertNotNull($vcard->children()[0]->{'X-APPLE-STRUCTURED-LOCATION'}->getValue()); } public function testNotFixUnfolding() @@ -94,6 +94,6 @@ public function testNotFixUnknownProperty() $vcard = (new MimeDir())->parse($vcard); - $this->assertNotNull($vcard->children()[0]->CONFERENCE->getValue()); + self::assertNotNull($vcard->children()[0]->CONFERENCE->getValue()); } } diff --git a/tests/VObject/Recur/FastForwardBeforeTest.php b/tests/VObject/Recur/FastForwardBeforeTest.php index 0426cc5ec..8cb62f8d6 100644 --- a/tests/VObject/Recur/FastForwardBeforeTest.php +++ b/tests/VObject/Recur/FastForwardBeforeTest.php @@ -15,7 +15,7 @@ private function fastForward(RRuleIterator $ruleIterator, \DateTimeInterface $ff $ruleIterator->fastForwardBefore($ffDate); $ru = getrusage(); $endTime = $ru['ru_utime.tv_sec'] * 1000000 + $ru['ru_utime.tv_usec']; - $this->assertLessThan(self::FF_TIMEOUT, $endTime - $startTime); + self::assertLessThan(self::FF_TIMEOUT, $endTime - $startTime); } public function testFastForwardBeforeYearlyBasic() @@ -33,50 +33,50 @@ public function testFastForwardBeforeYearlyBasic() ->setDate(99998, 10, 23) ->setTime(0, 0, 0) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += $year; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); // It's a leap $expected += $year + 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += $year; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += $year; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += $year; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); // leap $expected += $year + 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += $year; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += $year; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += $year; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); // leap $expected += $year + 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); } @@ -97,42 +97,42 @@ public function testFastForwardBeforeYearlyByYearDay() ->setDate(99999, 1, 1) ->setTime(0, 0, 0) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 20th day $rrule->next(); $expected += 19 * $day; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 300th day $rrule->next(); $expected += 280 * $day; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 1st day $expected += 66 * $day; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 20th day $rrule->next(); $expected += 19 * $day; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 300th day $rrule->next(); $expected += 280 * $day; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 1st day (leap year, we have 366 days in this year) $rrule->next(); $expected += 67 * $day; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 20th day $rrule->next(); $expected += 19 * $day; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardBeforeYearlyByWeekNo() @@ -153,12 +153,12 @@ public function testFastForwardBeforeYearlyByWeekNo() ->setDate(99999, 1, 4) ->setTime(0, 0, 0) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 20st week $rrule->next(); $expected += $week * 19; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardBeforeYearlyAdvanced() @@ -174,43 +174,43 @@ public function testFastForwardBeforeYearlyAdvanced() ->setDate(10000, 1, 2) ->setTime(8, 30, 56) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 7 * 24 * 60 * 60 - 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 7 * 24 * 60 * 60 - 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 7 * 24 * 60 * 60 - 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 7 * 24 * 60 * 60 - 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // jump to 6th january 10002 $rrule->next(); @@ -218,11 +218,11 @@ public function testFastForwardBeforeYearlyAdvanced() ->setDate(10002, 1, 6) ->setTime(8, 30, 56) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardBeforeMonthlyBasic() @@ -238,36 +238,36 @@ public function testFastForwardBeforeMonthlyBasic() ->setDate(18000, 1, 23) ->setTime(22, 42, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // february $rrule->next(); $expected += 31 * 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // march $rrule->next(); $expected += 29 * 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // april $rrule->next(); $expected += 31 * 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // may $rrule->next(); $expected += 30 * 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // june $rrule->next(); $expected += 31 * 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // july $rrule->next(); $expected += 30 * 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // august $rrule->next(); $expected += 31 * 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardBeforeMonthly31thDay() @@ -283,49 +283,49 @@ public function testFastForwardBeforeMonthly31thDay() $expected = (new \DateTime('midnight', new \DateTimeZone($timezone))) ->setDate(18000, 1, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // march $rrule->next(); $expected = (new \DateTime('1970-01-31 00:00:00', new \DateTimeZone($timezone))) ->setDate(18000, 3, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // may $rrule->next(); $expected = (new \DateTime('1970-01-31 00:00:00', new \DateTimeZone($timezone))) ->setDate(18000, 5, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // july $rrule->next(); $expected = (new \DateTime('1970-01-31 00:00:00', new \DateTimeZone($timezone))) ->setDate(18000, 7, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // august $rrule->next(); $expected = (new \DateTime('1970-01-31 00:00:00', new \DateTimeZone($timezone))) ->setDate(18000, 8, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // october $rrule->next(); $expected = (new \DateTime('1970-01-31 00:00:00', new \DateTimeZone($timezone))) ->setDate(18000, 10, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // december $rrule->next(); $expected = (new \DateTime('1970-01-31 00:00:00', new \DateTimeZone($timezone))) ->setDate(18000, 12, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardBeforeMonthlyAdvanced() @@ -343,49 +343,49 @@ public function testFastForwardBeforeMonthlyAdvanced() $expected = (new \DateTime('midnight', new \DateTimeZone($timezone))) ->setDate(8000, 1, 3) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // tuesday $expected = (new \DateTime('1970-01-31 00:00:00', new \DateTimeZone($timezone))) ->setDate(8000, 1, 11) ->getTimestamp(); $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // wednesday $expected = (new \DateTime('1970-01-31 00:00:00', new \DateTimeZone($timezone))) ->setDate(8000, 1, 19) ->getTimestamp(); $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // thursday $expected = (new \DateTime('1970-01-31 00:00:00', new \DateTimeZone($timezone))) ->setDate(8000, 1, 27) ->getTimestamp(); $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // monday march $expected = (new \DateTime('1970-01-31 00:00:00', new \DateTimeZone($timezone))) ->setDate(8000, 3, 6) ->getTimestamp(); $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // tuesday $expected = (new \DateTime('1970-01-31 00:00:00', new \DateTimeZone($timezone))) ->setDate(8000, 3, 14) ->getTimestamp(); $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // wednesday (this month starts on wednesday so that's just the next day) $expected = (new \DateTime('1970-01-31 00:00:00', new \DateTimeZone($timezone))) ->setDate(8000, 3, 15) ->getTimestamp(); $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // thursday $expected += 8 * 24 * 60 * 60; @@ -393,7 +393,7 @@ public function testFastForwardBeforeMonthlyAdvanced() ->setDate(8000, 3, 23) ->getTimestamp(); $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardBeforeDailyBasic() @@ -409,31 +409,31 @@ public function testFastForwardBeforeDailyBasic() $expected = (new \DateTime('midnight', new \DateTimeZone($timezone))) ->setDate(4000, 1, 1) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 24 * 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 24 * 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 24 * 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 24 * 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 24 * 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 24 * 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardBeforeDailyAdvanced() @@ -451,37 +451,37 @@ public function testFastForwardBeforeDailyAdvanced() ->setDate(4000, 1, 4) ->setTime(16, 0, 0) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 17:00 $expected += 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 18:00 $expected += 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 16:00 $expected += 10 * 24 * 60 * 60 - 2 * 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 17:00 $expected += 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 18:00 $expected += 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 16:00 $expected += 10 * 24 * 60 * 60 - 2 * 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardBeforeHourlyBasic() @@ -498,31 +498,31 @@ public function testFastForwardBeforeHourlyBasic() ->setDate(4000, 1, 2) ->setTime(1, 12, 34) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardBeforeNotInFrequency() @@ -540,7 +540,7 @@ public function testFastForwardBeforeNotInFrequency() ->setDate(2020, 2, 29) ->setTime(0, 0, 0) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // the next leap year $expected = (new \DateTime('midnight', new \DateTimeZone($timezone))) @@ -548,7 +548,7 @@ public function testFastForwardBeforeNotInFrequency() ->setTime(0, 0, 0) ->getTimestamp(); $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardBeforeMultipleTimesBasic() @@ -559,12 +559,12 @@ public function testFastForwardBeforeMultipleTimesBasic() $expected = new \DateTime('2020-01-16 00:00:00', new \DateTimeZone('zulu')); $this->fastForward($rrule, $ffDate); - $this->assertEquals($expected->getTimestamp(), $rrule->current()->getTimestamp()); + self::assertEquals($expected->getTimestamp(), $rrule->current()->getTimestamp()); $this->fastForward($rrule, $ffDate); - $this->assertEquals($expected->getTimestamp(), $rrule->current()->getTimestamp()); + self::assertEquals($expected->getTimestamp(), $rrule->current()->getTimestamp()); $this->fastForward($rrule, $ffDate); - $this->assertEquals($expected->getTimestamp(), $rrule->current()->getTimestamp()); + self::assertEquals($expected->getTimestamp(), $rrule->current()->getTimestamp()); } } diff --git a/tests/VObject/Recur/FastForwardTest.php b/tests/VObject/Recur/FastForwardTest.php index 62effc4de..3c6a14c50 100644 --- a/tests/VObject/Recur/FastForwardTest.php +++ b/tests/VObject/Recur/FastForwardTest.php @@ -15,7 +15,7 @@ private function fastForward(RRuleIterator $ruleIterator, \DateTimeInterface $ff $ruleIterator->fastForward($ffDate); $ru = getrusage(); $endTime = $ru['ru_utime.tv_sec'] * 1000000 + $ru['ru_utime.tv_usec']; - $this->assertLessThan(self::FF_TIMEOUT, $endTime - $startTime); + self::assertLessThan(self::FF_TIMEOUT, $endTime - $startTime); } public function testFastForwardYearlyBasic() @@ -33,37 +33,37 @@ public function testFastForwardYearlyBasic() ->setDate(99999, 10, 23) ->setTime(0, 0, 0) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); // It's a leap $expected += $year + 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += $year; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += $year; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += $year; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); // leap $expected += $year + 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += $year; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += $year; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += $year; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); // leap $expected += $year + 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); } @@ -82,33 +82,33 @@ public function testFastForwardYearlyByYearDay() ->setDate(99999, 1, 1)// 20th day ->setTime(0, 0, 0) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); // 300th day $expected += 19 * $day; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); // 1st day $expected += 280 * $day; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 20th day $expected += 66 * $day; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 300th day $rrule->next(); $expected += 19 * $day; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 280 * $day; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); // 1st day (leap year, we have 366 days in this year) $expected += 67 * $day; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 19 * $day; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardYearlyByWeekNo() @@ -127,10 +127,10 @@ public function testFastForwardYearlyByWeekNo() ->setDate(99999, 1, 4)// 1st day ->setTime(0, 0, 0) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += $week * 19; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardYearlyAdvanced() @@ -146,43 +146,43 @@ public function testFastForwardYearlyAdvanced() ->setDate(10000, 1, 2) ->setTime(8, 30, 56) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 7 * 24 * 60 * 60 - 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 7 * 24 * 60 * 60 - 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 7 * 24 * 60 * 60 - 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 7 * 24 * 60 * 60 - 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // jump to 6th january 10002 $rrule->next(); @@ -190,11 +190,11 @@ public function testFastForwardYearlyAdvanced() ->setDate(10002, 1, 6) ->setTime(8, 30, 56) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $rrule->next(); $expected += 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardMonthlyBasic() @@ -210,36 +210,36 @@ public function testFastForwardMonthlyBasic() ->setDate(18000, 1, 23) ->setTime(22, 42, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // february $rrule->next(); $expected += 31 * 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // march $rrule->next(); $expected += 29 * 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // april $rrule->next(); $expected += 31 * 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // may $rrule->next(); $expected += 30 * 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // june $rrule->next(); $expected += 31 * 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // july $rrule->next(); $expected += 30 * 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // august $rrule->next(); $expected += 31 * 24 * 60 * 60; - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardMonthly31thDay() @@ -255,14 +255,14 @@ public function testFastForwardMonthly31thDay() $expected = (new \DateTime('midnight', new \DateTimeZone('America/New_York'))) ->setDate(18000, 1, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // march $rrule->next(); $expected = (new \DateTime('midnight', new \DateTimeZone('America/New_York'))) ->setDate(18000, 3, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // may $rrule->next(); @@ -270,35 +270,35 @@ public function testFastForwardMonthly31thDay() $expected = (new \DateTime('midnight', new \DateTimeZone('America/New_York'))) ->setDate(18000, 5, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // july $rrule->next(); $expected = (new \DateTime('midnight', new \DateTimeZone('America/New_York'))) ->setDate(18000, 7, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // august $rrule->next(); $expected = (new \DateTime('midnight', new \DateTimeZone('America/New_York'))) ->setDate(18000, 8, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // october $rrule->next(); $expected = (new \DateTime('midnight', new \DateTimeZone('America/New_York'))) ->setDate(18000, 10, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // december $rrule->next(); $expected = (new \DateTime('midnight', new \DateTimeZone('America/New_York'))) ->setDate(18000, 12, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardMonthlyAdvanced() @@ -315,56 +315,56 @@ public function testFastForwardMonthlyAdvanced() $expected = (new \DateTime('midnight', new \DateTimeZone($timezone))) ->setDate(8000, 1, 3) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // tuesday $expected = (new \DateTime('midnight', new \DateTimeZone($timezone))) ->setDate(8000, 1, 11) ->getTimestamp(); $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // wednesday $expected = (new \DateTime('midnight', new \DateTimeZone($timezone))) ->setDate(8000, 1, 19) ->getTimestamp(); $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // thursday $expected = (new \DateTime('midnight', new \DateTimeZone($timezone))) ->setDate(8000, 1, 27) ->getTimestamp(); $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // monday march $expected = (new \DateTime('midnight', new \DateTimeZone($timezone))) ->setDate(8000, 3, 6) ->getTimestamp(); $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // tuesday $expected = (new \DateTime('midnight', new \DateTimeZone($timezone))) ->setDate(8000, 3, 14) ->getTimestamp(); $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // wednesday (this month starts on wednesday so that's just the next day) $expected = (new \DateTime('midnight', new \DateTimeZone($timezone))) ->setDate(8000, 3, 15) ->getTimestamp(); $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // thursday $expected = (new \DateTime('midnight', new \DateTimeZone($timezone))) ->setDate(8000, 3, 23) ->getTimestamp(); $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardDailyBasic() @@ -380,31 +380,31 @@ public function testFastForwardDailyBasic() $expected = (new \DateTime('midnight', new \DateTimeZone($timezone))) ->setDate(4000, 1, 1) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 24 * 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 24 * 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 24 * 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 24 * 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 24 * 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); $expected += 24 * 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardDailyAdvanced() @@ -421,36 +421,36 @@ public function testFastForwardDailyAdvanced() ->setDate(4000, 1, 4) ->setTime(16, 0, 0) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 17:00 $expected += 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 18:00 $expected += 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 16:00 $expected += 10 * 24 * 60 * 60 - 2 * 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 17:00 $expected += 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 18:00 $expected += 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); // 16:00 $expected += 10 * 24 * 60 * 60 - 2 * 60 * 60; $rrule->next(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } } diff --git a/tests/VObject/Recur/FastForwardToEndTest.php b/tests/VObject/Recur/FastForwardToEndTest.php index 0b94f3945..2b1201b09 100644 --- a/tests/VObject/Recur/FastForwardToEndTest.php +++ b/tests/VObject/Recur/FastForwardToEndTest.php @@ -15,9 +15,9 @@ private function fastForwardToEnd(RRuleIterator $ruleIterator, $enfoceTiming = t $ruleIterator->fastForwardToEnd(); $ru = getrusage(); $endTime = $ru['ru_utime.tv_sec'] * 1000000 + $ru['ru_utime.tv_usec']; - $enfoceTiming && $this->assertLessThan(self::FF_TIMEOUT, $endTime - $startTime); - $this->assertTrue($ruleIterator->valid()); - $this->assertNotNull($ruleIterator->current()); + $enfoceTiming && self::assertLessThan(self::FF_TIMEOUT, $endTime - $startTime); + self::assertTrue($ruleIterator->valid()); + self::assertNotNull($ruleIterator->current()); } public function testFastForwardToEndWithoutEndYearlyBasic(): void @@ -42,7 +42,7 @@ public function testFastForwardToEndCountYearlyBasic(): void ->setDate(9746, 10, 23) ->setTime(0, 0, 0) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardToEndUntilYearlyBasic(): void @@ -57,7 +57,7 @@ public function testFastForwardToEndUntilYearlyBasic(): void ->setDate(9746, 10, 23) ->setTime(0, 0, 0) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardToEndCountYearlyByYearDay(): void @@ -73,7 +73,7 @@ public function testFastForwardToEndCountYearlyByYearDay(): void ->setDate(5303, 1, 20) ->setTime(0, 0, 0) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardToEndUntilYearlyByYearDay(): void @@ -88,7 +88,7 @@ public function testFastForwardToEndUntilYearlyByYearDay(): void ->setDate(5303, 1, 20) ->setTime(0, 0, 0) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } /* @@ -106,7 +106,7 @@ public function testFastForwardToEndCountYearlyByWeekNo() ->setDate(2019, 12, 30) ->setTime(0, 0, 0) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardToEndUntilYearlyByWeekNo() @@ -121,7 +121,7 @@ public function testFastForwardToEndUntilYearlyByWeekNo() ->setDate(2019, 12, 30) ->setTime(0, 0, 0) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } */ @@ -137,7 +137,7 @@ public function testFastForwardToEndCountYearlyAdvanced() ->setDate(4226, 1, 1) ->setTime(8, 30, 56) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardToEndUntilYearlyAdvanced() @@ -151,7 +151,7 @@ public function testFastForwardToEndUntilYearlyAdvanced() ->setDate(4218, 1, 25) ->setTime(8, 30, 56) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardToEndCountMonthlyBasic() @@ -166,7 +166,7 @@ public function testFastForwardToEndCountMonthlyBasic() ->setDate(2804, 1, 23) ->setTime(22, 42, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardToEndUntilMonthlyBasic() @@ -180,7 +180,7 @@ public function testFastForwardToEndUntilMonthlyBasic() ->setDate(2803, 12, 23) ->setTime(22, 42, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } /** @@ -197,7 +197,7 @@ public function testFastForwardToEndCountMonthly31thDay() $expected = (new \DateTime('midnight', new \DateTimeZone('America/New_York'))) ->setDate(3398, 10, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } /** @@ -213,7 +213,7 @@ public function testFastForwardToEndCountMonthly31thDayPHP81() $expected = (new \DateTime('midnight', new \DateTimeZone('America/New_York'))) ->setDate(3398, 7, 31); - $this->assertEquals($expected->getTimestamp(), $rrule->current()->getTimestamp()); + self::assertEquals($expected->getTimestamp(), $rrule->current()->getTimestamp()); } public function testFastForwardToEndUntilMonthly31thDay() @@ -226,7 +226,7 @@ public function testFastForwardToEndUntilMonthly31thDay() $expected = (new \DateTime('midnight', new \DateTimeZone('America/New_York'))) ->setDate(3398, 8, 31) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } /** @@ -244,7 +244,7 @@ public function testFastForwardToEndCountMonthlyAdvanced() $expected = (new \DateTime('midnight', new \DateTimeZone('America/New_York'))) ->setDate(2386, 9, 17) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardToEndUntilMonthlyAdvanced() @@ -258,7 +258,7 @@ public function testFastForwardToEndUntilMonthlyAdvanced() $expected = (new \DateTime('midnight', new \DateTimeZone('America/New_York'))) ->setDate(2386, 9, 9) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardToEndCountDailyBasic() @@ -273,7 +273,7 @@ public function testFastForwardToEndCountDailyBasic() $expected = (new \DateTime('midnight', new \DateTimeZone($timezone))) ->setDate(2244, 8, 6) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardToEndUntilDailyBasic() @@ -287,7 +287,7 @@ public function testFastForwardToEndUntilDailyBasic() $expected = (new \DateTime('midnight', new \DateTimeZone($timezone))) ->setDate(2244, 8, 6) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardToEndCountDailyAdvanced() @@ -304,7 +304,7 @@ public function testFastForwardToEndCountDailyAdvanced() ->setDate(2062, 1, 13) ->setTime(18, 0, 0) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardToEndUntilDailyAdvanced() @@ -320,7 +320,7 @@ public function testFastForwardToEndUntilDailyAdvanced() ->setDate(2062, 1, 13) ->setTime(18, 0, 0) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardToEndCountHourlyBasic() @@ -336,7 +336,7 @@ public function testFastForwardToEndCountHourlyBasic() ->setDate(1982, 3, 21) ->setTime(2, 12, 34) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } public function testFastForwardToEndUntilHourlyBasic() @@ -351,6 +351,6 @@ public function testFastForwardToEndUntilHourlyBasic() ->setDate(1982, 3, 21) ->setTime(2, 12, 34) ->getTimestamp(); - $this->assertEquals($expected, $rrule->current()->getTimestamp()); + self::assertEquals($expected, $rrule->current()->getTimestamp()); } } diff --git a/tests/VObject/Recur/RRuleIteratorTest.php b/tests/VObject/Recur/RRuleIteratorTest.php index fdfa21cd8..8aa92ad1f 100644 --- a/tests/VObject/Recur/RRuleIteratorTest.php +++ b/tests/VObject/Recur/RRuleIteratorTest.php @@ -346,7 +346,7 @@ public function testInvalidByMonthDay(): void ); } - /** @dataProvider invalidFreqByCombinationProviders */ + #[DataProvider('invalidFreqByCombinationProviders')] public function testInvalidFreqByCombination(string $rule): void { $this->expectException(InvalidDataException::class); @@ -357,7 +357,7 @@ public function testInvalidFreqByCombination(string $rule): void ); } - public function invalidFreqByCombinationProviders(): iterable + public static function invalidFreqByCombinationProviders(): iterable { return [ ['FREQ=DAILY;BYWEEKNO=13,15,50'], @@ -1254,15 +1254,15 @@ public function parse( ?string $fastForward = null, string $tz = 'UTC', bool $runTillTheEnd = false, - bool $yearlySkipUpperLimit = true + bool $yearlySkipUpperLimit = true, ): void { $dt = new \DateTime($start, new \DateTimeZone($tz)); $parser = new RRuleIterator($rule, $dt, $yearlySkipUpperLimit); - $this->assertEquals($expectedFreq, $parser->getFrequency()); - $this->assertEquals($expectedCount, $parser->getCount()); - $this->assertEquals($expectedInterval, $parser->getInterval()); - $this->assertEquals($expectedUntil, $parser->getUntil()); + self::assertEquals($expectedFreq, $parser->getFrequency()); + self::assertEquals($expectedCount, $parser->getCount()); + self::assertEquals($expectedInterval, $parser->getInterval()); + self::assertEquals($expectedUntil, $parser->getUntil()); if ($fastForward) { $parser->fastForward(new \DateTime($fastForward)); diff --git a/tests/VObject/TimeZoneUtilTest.php b/tests/VObject/TimeZoneUtilTest.php index 5ad862bcf..d0974c969 100644 --- a/tests/VObject/TimeZoneUtilTest.php +++ b/tests/VObject/TimeZoneUtilTest.php @@ -44,9 +44,7 @@ public static function getMapping(): array ); } - /** - * @dataProvider getMapping - */ + #[DataProvider('getMapping')] public function testSlashTZ($timezonename): void { $slashTimezone = '/'.$timezonename; @@ -414,9 +412,7 @@ public function testMicrosoftMap(): void self::assertEquals($ex->getName(), $tz->getName()); } - /** - * @dataProvider unSupportTimezoneProvider - */ + #[DataProvider('unSupportTimezoneProvider')] public function testPHPUnSupportTimeZone(string $origin, string $expected): void { $tz = TimeZoneUtil::getTimeZone($origin, null, true); @@ -424,7 +420,7 @@ public function testPHPUnSupportTimeZone(string $origin, string $expected): void self::assertEquals($ex->getName(), $tz->getName()); } - public function unSupportTimezoneProvider(): iterable + public static function unSupportTimezoneProvider(): iterable { yield 'America/Santa_Isabel' => [ 'origin' => 'America/Santa_Isabel', @@ -483,9 +479,7 @@ public function unSupportTimezoneProvider(): iterable } } - /** - * @dataProvider offsetTimeZoneProvider - */ + #[DataProvider('offsetTimeZoneProvider')] public function testOffsetTimeZones(string $origin, string $expected): void { $tz = TimeZoneUtil::getTimeZone($origin, null, true); @@ -493,7 +487,7 @@ public function testOffsetTimeZones(string $origin, string $expected): void self::assertEquals($ex->getName(), $tz->getName()); } - public function offsetTimeZoneProvider(): iterable + public static function offsetTimeZoneProvider(): iterable { yield 'UTC-05:00' => [ 'origin' => 'UTC-05:00', @@ -516,9 +510,7 @@ public function offsetTimeZoneProvider(): iterable ]; } - /** - * @dataProvider letterCaseTimeZoneProvider - */ + #[DataProvider('letterCaseTimeZoneProvider')] public function testDifferentLetterCaseTimeZone(string $origin, string $expected): void { $tz = TimeZoneUtil::getTimeZone($origin, null, true); @@ -526,7 +518,7 @@ public function testDifferentLetterCaseTimeZone(string $origin, string $expected self::assertEquals($ex->getName(), $tz->getName()); } - public function letterCaseTimeZoneProvider(): iterable + public static function letterCaseTimeZoneProvider(): iterable { yield 'case 1' => [ 'origin' => 'Europe/paris', @@ -549,9 +541,7 @@ public function letterCaseTimeZoneProvider(): iterable ]; } - /** - * @dataProvider outlookCitiesProvider - */ + #[DataProvider('outlookCitiesProvider')] public function testOutlookCities(string $origin, bool $failIfUncertain, string $expected): void { $tz = TimeZoneUtil::getTimeZone($origin, null, $failIfUncertain); @@ -559,7 +549,7 @@ public function testOutlookCities(string $origin, bool $failIfUncertain, string self::assertEquals($ex->getName(), $tz->getName()); } - public function outlookCitiesProvider(): iterable + public static function outlookCitiesProvider(): iterable { yield 'case 1' => [ 'origin' => 'TZID:(UTC+01:00) Bruxelles\, København\, Madrid\, Paris', @@ -586,9 +576,7 @@ public function outlookCitiesProvider(): iterable ]; } - /** - * @dataProvider versionTzProvider - */ + #[DataProvider('versionTzProvider')] public function testVersionTz(string $origin, bool $failIfUncertain, string $expected): void { $tz = TimeZoneUtil::getTimeZone($origin, null, $failIfUncertain); @@ -596,7 +584,7 @@ public function testVersionTz(string $origin, bool $failIfUncertain, string $exp self::assertEquals($ex->getName(), $tz->getName()); } - public function versionTzProvider(): iterable + public static function versionTzProvider(): iterable { yield 'case 1' => [ 'origin' => 'Eastern Standard Time 1',