From d77aba0e762fe1d44cd7335bd0bdffad7c5352e9 Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Fri, 28 Mar 2025 02:24:40 +0000 Subject: [PATCH 1/2] [TASK] Add `Positionable` interface and implementing trait This is for CSS items which have a position in the document. New methods are added: - `getLineNumber` to replace `getLineNo`; - `getColumnNumber` to replace `getColNo`. These return a nullable `int`, instead of using zero to indicate absence. The old methods are now deprecated, but defined in the interface and implemented in the trait. Note that this change only adds the interface and trait. It does not modify any classes to actually implement or use these. Part of #1207. --- src/Position/Position.php | 68 +++++ src/Position/Positionable.php | 45 +++ .../Position/Fixtures/ConcretePosition.php | 13 + tests/Unit/Position/PositionTest.php | 284 ++++++++++++++++++ 4 files changed, 410 insertions(+) create mode 100644 src/Position/Position.php create mode 100644 src/Position/Positionable.php create mode 100644 tests/Unit/Position/Fixtures/ConcretePosition.php create mode 100644 tests/Unit/Position/PositionTest.php diff --git a/src/Position/Position.php b/src/Position/Position.php new file mode 100644 index 000000000..655f34b9b --- /dev/null +++ b/src/Position/Position.php @@ -0,0 +1,68 @@ +|null + */ + protected $lineNumber; + + /** + * @var int<0, max>|null + */ + protected $columnNumber; + + /** + * @return int<1, max>|null + */ + public function getLineNumber(): ?int + { + return $this->lineNumber; + } + + /** + * @return int<0, max> + */ + public function getLineNo(): int + { + return $this->getLineNumber() ?? 0; + } + + /** + * @return int<0, max>|null + */ + public function getColumnNumber(): ?int + { + return $this->columnNumber; + } + + /** + * @return int<0, max> + */ + public function getColNo(): int + { + return $this->getColumnNumber() ?? 0; + } + + /** + * @param int<0, max>|null $lineNumber + * @param int<0, max>|null $columnNumber + */ + public function setPosition(?int $lineNumber, ?int $columnNumber = null): void + { + // The conditional is for backwards compatibility (backcompat); `0` will not be allowed in future. + $this->lineNumber = $lineNumber !== 0 ? $lineNumber : null; + $this->columnNumber = $columnNumber; + } +} diff --git a/src/Position/Positionable.php b/src/Position/Positionable.php new file mode 100644 index 000000000..124752feb --- /dev/null +++ b/src/Position/Positionable.php @@ -0,0 +1,45 @@ +|null + */ + public function getLineNumber(): ?int; + + /** + * @deprecated in version 9.0.0, will be removed in v10.0. Use `getLineNumber()` instead. + * + * @return int<0, max> + */ + public function getLineNo(): int; + + /** + * @return int<0, max>|null + */ + public function getColumnNumber(): ?int; + + /** + * @deprecated in version 9.0.0, will be removed in v10.0. Use `getColumnNumber()` instead. + * + * @return int<0, max> + */ + public function getColNo(): int; + + /** + * @param int<0, max>|null $lineNumber + * Providing zero for this parameter is deprecated in version 9.0.0, and will not be supported from v10.0. + * Use `null` instead when no line number is available. + * @param int<0, max>|null $columnNumber + */ + public function setPosition(?int $lineNumber, ?int $columnNumber = null): void; +} diff --git a/tests/Unit/Position/Fixtures/ConcretePosition.php b/tests/Unit/Position/Fixtures/ConcretePosition.php new file mode 100644 index 000000000..0db387065 --- /dev/null +++ b/tests/Unit/Position/Fixtures/ConcretePosition.php @@ -0,0 +1,13 @@ +subject = new ConcretePosition(); + } + + /** + * @test + */ + public function getLineNumberInitiallyReturnsNull(): void + { + self::assertNull($this->subject->getLineNumber()); + } + + /** + * @test + */ + public function getColumnNumberInitiallyReturnsNull(): void + { + self::assertNull($this->subject->getColumnNumber()); + } + + /** + * @return array}> + */ + public function provideLineNumber(): array + { + return [ + 'line 1' => [1], + 'line 42' => [42], + ]; + } + + /** + * @test + * + * @param int<1, max> $lineNumber + * + * @dataProvider provideLineNumber + */ + public function setPositionOnVirginSetsLineNumber(int $lineNumber): void + { + $this->subject->setPosition($lineNumber); + + self::assertSame($lineNumber, $this->subject->getLineNumber()); + } + + /** + * @test + * + * @param int<1, max> $lineNumber + * + * @dataProvider provideLineNumber + */ + public function setPositionSetsNewLineNumber(int $lineNumber): void + { + $this->subject->setPosition(99); + + $this->subject->setPosition($lineNumber); + + self::assertSame($lineNumber, $this->subject->getLineNumber()); + } + + /** + * @test + */ + public function setPositionWithNullClearsLineNumber(): void + { + $this->subject->setPosition(99); + + $this->subject->setPosition(null); + + self::assertNull($this->subject->getLineNumber()); + } + + /** + * @return array}> + */ + public function provideColumnNumber(): array + { + return [ + 'column 0' => [0], + 'column 14' => [14], + 'column 39' => [39], + ]; + } + + /** + * @test + * + * @param int<0, max> $columnNumber + * + * @dataProvider provideColumnNumber + */ + public function setPositionOnVirginSetsColumnNumber(int $columnNumber): void + { + $this->subject->setPosition(1, $columnNumber); + + self::assertSame($columnNumber, $this->subject->getColumnNumber()); + } + + /** + * @test + * + * @dataProvider provideColumnNumber + */ + public function setPositionSetsNewColumnNumber(int $columnNumber): void + { + $this->subject->setPosition(1, 99); + + $this->subject->setPosition(2, $columnNumber); + + self::assertSame($columnNumber, $this->subject->getColumnNumber()); + } + + /** + * @test + */ + public function setPositionWithoutColumnNumberClearsColumnNumber(): void + { + $this->subject->setPosition(1, 99); + + $this->subject->setPosition(2); + + self::assertNull($this->subject->getColumnNumber()); + } + + /** + * @test + */ + public function setPositionWithNullForColumnNumberClearsColumnNumber(): void + { + $this->subject->setPosition(1, 99); + + $this->subject->setPosition(2, null); + + self::assertNull($this->subject->getColumnNumber()); + } + + /** + * @return array, 1: int<0, max>}> + */ + public function provideLineAndColumnNumber(): array + { + return DataProviders::cross($this->provideLineNumber(), $this->provideColumnNumber()); + } + + /** + * @test + * + * @dataProvider provideLineAndColumnNumber + */ + public function setPositionOnVirginSetsLineAndColumnNumber(int $lineNumber, int $columnNumber): void + { + $this->subject->setPosition($lineNumber, $columnNumber); + + self::assertSame($lineNumber, $this->subject->getLineNumber()); + self::assertSame($columnNumber, $this->subject->getColumnNumber()); + } + + /** + * @test + * + * @dataProvider provideLineAndColumnNumber + */ + public function setPositionSetsNewLineAndColumnNumber(int $lineNumber, int $columnNumber): void + { + $this->subject->setPosition(98, 99); + + $this->subject->setPosition($lineNumber, $columnNumber); + + self::assertSame($lineNumber, $this->subject->getLineNumber()); + self::assertSame($columnNumber, $this->subject->getColumnNumber()); + } + + // Testing deprecated functionality + + /** + * @test + */ + public function getLineNoInitiallyReturnsZero(): void + { + self::assertSame(0, $this->subject->getLineNo()); + } + + /** + * @test + * + * @dataProvider provideLineNumber + */ + public function getLineNoReturnsLineNumberSet(int $lineNumber): void + { + $this->subject->setPosition($lineNumber); + + self::assertSame($lineNumber, $this->subject->getLineNo()); + } + + /** + * @test + */ + public function getLineNoReturnsZeroAfterLineNumberCleared(): void + { + $this->subject->setPosition(99); + + $this->subject->setPosition(null); + + self::assertSame(0, $this->subject->getLineNo()); + } + + /** + * @test + */ + public function getColNoInitiallyReturnsZero(): void + { + self::assertSame(0, $this->subject->getColNo()); + } + + /** + * @test + * + * @dataProvider provideColumnNumber + */ + public function getColNoReturnsColumnNumberSet(int $columnNumber): void + { + $this->subject->setPosition(1, $columnNumber); + + self::assertSame($columnNumber, $this->subject->getColNo()); + } + + /** + * @test + */ + public function getColNoReturnsZeroAfterColumnNumberCleared(): void + { + $this->subject->setPosition(1, 99); + + $this->subject->setPosition(2); + + self::assertSame(0, $this->subject->getColNo()); + } + + /** + * @test + */ + public function setPositionWithZeroClearsLineNumber(): void + { + $this->subject->setPosition(99); + + $this->subject->setPosition(0); + + self::assertNull($this->subject->getLineNumber()); + } + + /** + * @test + */ + public function getLineNoAfterSetPositionWithZeroReturnsZero(): void + { + $this->subject->setPosition(99); + + $this->subject->setPosition(0); + + self::assertSame(0, $this->subject->getLineNo()); + } +} From f38c273cdf9d65a337cc55992a86059337e65985 Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Sat, 29 Mar 2025 23:10:31 +0000 Subject: [PATCH 2/2] Move tests for deprecated functionality to `UnitDeprecated` --- tests/Unit/Position/PositionTest.php | 90 ------------ tests/UnitDeprecated/.gitkeep | 0 .../UnitDeprecated/Position/PositionTest.php | 135 ++++++++++++++++++ 3 files changed, 135 insertions(+), 90 deletions(-) delete mode 100644 tests/UnitDeprecated/.gitkeep create mode 100644 tests/UnitDeprecated/Position/PositionTest.php diff --git a/tests/Unit/Position/PositionTest.php b/tests/Unit/Position/PositionTest.php index a53d6678b..86233d7c9 100644 --- a/tests/Unit/Position/PositionTest.php +++ b/tests/Unit/Position/PositionTest.php @@ -191,94 +191,4 @@ public function setPositionSetsNewLineAndColumnNumber(int $lineNumber, int $colu self::assertSame($lineNumber, $this->subject->getLineNumber()); self::assertSame($columnNumber, $this->subject->getColumnNumber()); } - - // Testing deprecated functionality - - /** - * @test - */ - public function getLineNoInitiallyReturnsZero(): void - { - self::assertSame(0, $this->subject->getLineNo()); - } - - /** - * @test - * - * @dataProvider provideLineNumber - */ - public function getLineNoReturnsLineNumberSet(int $lineNumber): void - { - $this->subject->setPosition($lineNumber); - - self::assertSame($lineNumber, $this->subject->getLineNo()); - } - - /** - * @test - */ - public function getLineNoReturnsZeroAfterLineNumberCleared(): void - { - $this->subject->setPosition(99); - - $this->subject->setPosition(null); - - self::assertSame(0, $this->subject->getLineNo()); - } - - /** - * @test - */ - public function getColNoInitiallyReturnsZero(): void - { - self::assertSame(0, $this->subject->getColNo()); - } - - /** - * @test - * - * @dataProvider provideColumnNumber - */ - public function getColNoReturnsColumnNumberSet(int $columnNumber): void - { - $this->subject->setPosition(1, $columnNumber); - - self::assertSame($columnNumber, $this->subject->getColNo()); - } - - /** - * @test - */ - public function getColNoReturnsZeroAfterColumnNumberCleared(): void - { - $this->subject->setPosition(1, 99); - - $this->subject->setPosition(2); - - self::assertSame(0, $this->subject->getColNo()); - } - - /** - * @test - */ - public function setPositionWithZeroClearsLineNumber(): void - { - $this->subject->setPosition(99); - - $this->subject->setPosition(0); - - self::assertNull($this->subject->getLineNumber()); - } - - /** - * @test - */ - public function getLineNoAfterSetPositionWithZeroReturnsZero(): void - { - $this->subject->setPosition(99); - - $this->subject->setPosition(0); - - self::assertSame(0, $this->subject->getLineNo()); - } } diff --git a/tests/UnitDeprecated/.gitkeep b/tests/UnitDeprecated/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/UnitDeprecated/Position/PositionTest.php b/tests/UnitDeprecated/Position/PositionTest.php new file mode 100644 index 000000000..2e06cc6d8 --- /dev/null +++ b/tests/UnitDeprecated/Position/PositionTest.php @@ -0,0 +1,135 @@ +subject = new ConcretePosition(); + } + + /** + * @return array}> + */ + public function provideLineNumber(): array + { + return [ + 'line 1' => [1], + 'line 42' => [42], + ]; + } + + /** + * @return array}> + */ + public function provideColumnNumber(): array + { + return [ + 'column 0' => [0], + 'column 14' => [14], + 'column 39' => [39], + ]; + } + + /** + * @test + */ + public function getLineNoInitiallyReturnsZero(): void + { + self::assertSame(0, $this->subject->getLineNo()); + } + + /** + * @test + * + * @dataProvider provideLineNumber + */ + public function getLineNoReturnsLineNumberSet(int $lineNumber): void + { + $this->subject->setPosition($lineNumber); + + self::assertSame($lineNumber, $this->subject->getLineNo()); + } + + /** + * @test + */ + public function getLineNoReturnsZeroAfterLineNumberCleared(): void + { + $this->subject->setPosition(99); + + $this->subject->setPosition(null); + + self::assertSame(0, $this->subject->getLineNo()); + } + + /** + * @test + */ + public function getColNoInitiallyReturnsZero(): void + { + self::assertSame(0, $this->subject->getColNo()); + } + + /** + * @test + * + * @dataProvider provideColumnNumber + */ + public function getColNoReturnsColumnNumberSet(int $columnNumber): void + { + $this->subject->setPosition(1, $columnNumber); + + self::assertSame($columnNumber, $this->subject->getColNo()); + } + + /** + * @test + */ + public function getColNoReturnsZeroAfterColumnNumberCleared(): void + { + $this->subject->setPosition(1, 99); + + $this->subject->setPosition(2); + + self::assertSame(0, $this->subject->getColNo()); + } + + /** + * @test + */ + public function setPositionWithZeroClearsLineNumber(): void + { + $this->subject->setPosition(99); + + $this->subject->setPosition(0); + + self::assertNull($this->subject->getLineNumber()); + } + + /** + * @test + */ + public function getLineNoAfterSetPositionWithZeroReturnsZero(): void + { + $this->subject->setPosition(99); + + $this->subject->setPosition(0); + + self::assertSame(0, $this->subject->getLineNo()); + } +}