Skip to content

Commit ddd999e

Browse files
committed
[TASK] Add and implement Positionable interface
This the backport of #1221 and #1225.
1 parent aa12119 commit ddd999e

File tree

5 files changed

+455
-0
lines changed

5 files changed

+455
-0
lines changed

src/Position/Position.php

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sabberworm\CSS\Position;
6+
7+
/**
8+
* Provides a standard reusable implementation of `Positionable`.
9+
*
10+
* @internal
11+
*
12+
* @phpstan-require-implements Positionable
13+
*/
14+
trait Position
15+
{
16+
/**
17+
* @var int<1, max>|null
18+
*/
19+
protected $lineNumber;
20+
21+
/**
22+
* @var int<0, max>|null
23+
*/
24+
protected $columnNumber;
25+
26+
/**
27+
* @return int<1, max>|null
28+
*/
29+
public function getLineNumber(): ?int
30+
{
31+
return $this->lineNumber;
32+
}
33+
34+
/**
35+
* @return int<0, max>
36+
*/
37+
public function getLineNo(): int
38+
{
39+
return $this->getLineNumber() ?? 0;
40+
}
41+
42+
/**
43+
* @return int<0, max>|null
44+
*/
45+
public function getColumnNumber(): ?int
46+
{
47+
return $this->columnNumber;
48+
}
49+
50+
/**
51+
* @return int<0, max>
52+
*/
53+
public function getColNo(): int
54+
{
55+
return $this->getColumnNumber() ?? 0;
56+
}
57+
58+
/**
59+
* @param int<0, max>|null $lineNumber
60+
* @param int<0, max>|null $columnNumber
61+
*/
62+
public function setPosition(?int $lineNumber, ?int $columnNumber = null): void
63+
{
64+
// The conditional is for backwards compatibility (backcompat); `0` will not be allowed in future.
65+
$this->lineNumber = $lineNumber !== 0 ? $lineNumber : null;
66+
$this->columnNumber = $columnNumber;
67+
}
68+
}

src/Position/Positionable.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sabberworm\CSS\Position;
6+
7+
/**
8+
* Represents a CSS item that may have a position in the source CSS document (line number and possibly column number).
9+
*
10+
* A standard implementation of this interface is available in the `Position` trait.
11+
*/
12+
interface Positionable
13+
{
14+
/**
15+
* @return int<1, max>|null
16+
*/
17+
public function getLineNumber(): ?int;
18+
19+
/**
20+
* @deprecated in version 9.0.0, will be removed in v10.0. Use `getLineNumber()` instead.
21+
*
22+
* @return int<0, max>
23+
*/
24+
public function getLineNo(): int;
25+
26+
/**
27+
* @return int<0, max>|null
28+
*/
29+
public function getColumnNumber(): ?int;
30+
31+
/**
32+
* @deprecated in version 9.0.0, will be removed in v10.0. Use `getColumnNumber()` instead.
33+
*
34+
* @return int<0, max>
35+
*/
36+
public function getColNo(): int;
37+
38+
/**
39+
* @param int<0, max>|null $lineNumber
40+
* Providing zero for this parameter is deprecated in version 9.0.0, and will not be supported from v10.0.
41+
* Use `null` instead when no line number is available.
42+
* @param int<0, max>|null $columnNumber
43+
*/
44+
public function setPosition(?int $lineNumber, ?int $columnNumber = null): void;
45+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sabberworm\CSS\Tests\Unit\Position\Fixtures;
6+
7+
use Sabberworm\CSS\Position\Position;
8+
use Sabberworm\CSS\Position\Positionable;
9+
10+
final class ConcretePosition implements Positionable
11+
{
12+
use Position;
13+
}

tests/Unit/Position/PositionTest.php

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sabberworm\CSS\Tests\Unit\Position;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Sabberworm\CSS\Tests\Unit\Position\Fixtures\ConcretePosition;
9+
use TRegx\DataProvider\DataProviders;
10+
11+
/**
12+
* @covers \Sabberworm\CSS\Position\Position
13+
*/
14+
final class PositionTest extends TestCase
15+
{
16+
/**
17+
* @var ConcretePosition
18+
*/
19+
private $subject;
20+
21+
protected function setUp(): void
22+
{
23+
$this->subject = new ConcretePosition();
24+
}
25+
26+
/**
27+
* @test
28+
*/
29+
public function getLineNumberInitiallyReturnsNull(): void
30+
{
31+
self::assertNull($this->subject->getLineNumber());
32+
}
33+
34+
/**
35+
* @test
36+
*/
37+
public function getColumnNumberInitiallyReturnsNull(): void
38+
{
39+
self::assertNull($this->subject->getColumnNumber());
40+
}
41+
42+
/**
43+
* @return array<non-empty-string, array{0: int<1, max>}>
44+
*/
45+
public function provideLineNumber(): array
46+
{
47+
return [
48+
'line 1' => [1],
49+
'line 42' => [42],
50+
];
51+
}
52+
53+
/**
54+
* @test
55+
*
56+
* @param int<1, max> $lineNumber
57+
*
58+
* @dataProvider provideLineNumber
59+
*/
60+
public function setPositionOnVirginSetsLineNumber(int $lineNumber): void
61+
{
62+
$this->subject->setPosition($lineNumber);
63+
64+
self::assertSame($lineNumber, $this->subject->getLineNumber());
65+
}
66+
67+
/**
68+
* @test
69+
*
70+
* @param int<1, max> $lineNumber
71+
*
72+
* @dataProvider provideLineNumber
73+
*/
74+
public function setPositionSetsNewLineNumber(int $lineNumber): void
75+
{
76+
$this->subject->setPosition(99);
77+
78+
$this->subject->setPosition($lineNumber);
79+
80+
self::assertSame($lineNumber, $this->subject->getLineNumber());
81+
}
82+
83+
/**
84+
* @test
85+
*/
86+
public function setPositionWithNullClearsLineNumber(): void
87+
{
88+
$this->subject->setPosition(99);
89+
90+
$this->subject->setPosition(null);
91+
92+
self::assertNull($this->subject->getLineNumber());
93+
}
94+
95+
/**
96+
* @return array<non-empty-string, array{0: int<0, max>}>
97+
*/
98+
public function provideColumnNumber(): array
99+
{
100+
return [
101+
'column 0' => [0],
102+
'column 14' => [14],
103+
'column 39' => [39],
104+
];
105+
}
106+
107+
/**
108+
* @test
109+
*
110+
* @param int<0, max> $columnNumber
111+
*
112+
* @dataProvider provideColumnNumber
113+
*/
114+
public function setPositionOnVirginSetsColumnNumber(int $columnNumber): void
115+
{
116+
$this->subject->setPosition(1, $columnNumber);
117+
118+
self::assertSame($columnNumber, $this->subject->getColumnNumber());
119+
}
120+
121+
/**
122+
* @test
123+
*
124+
* @dataProvider provideColumnNumber
125+
*/
126+
public function setPositionSetsNewColumnNumber(int $columnNumber): void
127+
{
128+
$this->subject->setPosition(1, 99);
129+
130+
$this->subject->setPosition(2, $columnNumber);
131+
132+
self::assertSame($columnNumber, $this->subject->getColumnNumber());
133+
}
134+
135+
/**
136+
* @test
137+
*/
138+
public function setPositionWithoutColumnNumberClearsColumnNumber(): void
139+
{
140+
$this->subject->setPosition(1, 99);
141+
142+
$this->subject->setPosition(2);
143+
144+
self::assertNull($this->subject->getColumnNumber());
145+
}
146+
147+
/**
148+
* @test
149+
*/
150+
public function setPositionWithNullForColumnNumberClearsColumnNumber(): void
151+
{
152+
$this->subject->setPosition(1, 99);
153+
154+
$this->subject->setPosition(2, null);
155+
156+
self::assertNull($this->subject->getColumnNumber());
157+
}
158+
159+
/**
160+
* @return array<non-empty-string, array{0: int<1, max>, 1: int<0, max>}>
161+
*/
162+
public function provideLineAndColumnNumber(): array
163+
{
164+
return DataProviders::cross($this->provideLineNumber(), $this->provideColumnNumber());
165+
}
166+
167+
/**
168+
* @test
169+
*
170+
* @dataProvider provideLineAndColumnNumber
171+
*/
172+
public function setPositionOnVirginSetsLineAndColumnNumber(int $lineNumber, int $columnNumber): void
173+
{
174+
$this->subject->setPosition($lineNumber, $columnNumber);
175+
176+
self::assertSame($lineNumber, $this->subject->getLineNumber());
177+
self::assertSame($columnNumber, $this->subject->getColumnNumber());
178+
}
179+
180+
/**
181+
* @test
182+
*
183+
* @dataProvider provideLineAndColumnNumber
184+
*/
185+
public function setPositionSetsNewLineAndColumnNumber(int $lineNumber, int $columnNumber): void
186+
{
187+
$this->subject->setPosition(98, 99);
188+
189+
$this->subject->setPosition($lineNumber, $columnNumber);
190+
191+
self::assertSame($lineNumber, $this->subject->getLineNumber());
192+
self::assertSame($columnNumber, $this->subject->getColumnNumber());
193+
}
194+
}

0 commit comments

Comments
 (0)