Skip to content

Commit 0553f9d

Browse files
committed
[TASK] Add Positionalable 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.
1 parent 993d6b5 commit 0553f9d

File tree

4 files changed

+383
-0
lines changed

4 files changed

+383
-0
lines changed

src/Position/Position.php

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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<1, max>|null $lineNumber
60+
* @param int<0, max>|null $columnNumber
61+
*/
62+
public function setPosition(?int $lineNumber, ?int $columnNumber = null): void
63+
{
64+
$this->lineNumber = $lineNumber;
65+
$this->columnNumber = $columnNumber;
66+
}
67+
}

src/Position/Positionable.php

+45
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<1, 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+
}
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

+258
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
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+
195+
/**
196+
* @test
197+
*/
198+
public function getLineNoInitiallyReturnsZero(): void
199+
{
200+
self::assertSame(0, $this->subject->getLineNo());
201+
}
202+
203+
/**
204+
* @test
205+
*
206+
* @dataProvider provideLineNumber
207+
*/
208+
public function getLineNoReturnsLineNumberSet(int $lineNumber): void
209+
{
210+
$this->subject->setPosition($lineNumber);
211+
212+
self::assertSame($lineNumber, $this->subject->getLineNo());
213+
}
214+
215+
/**
216+
* @test
217+
*/
218+
public function getLineNoReturnsZeroAfterLineNumberCleared(): void
219+
{
220+
$this->subject->setPosition(99);
221+
222+
$this->subject->setPosition(null);
223+
224+
self::assertSame(0, $this->subject->getLineNo());
225+
}
226+
227+
/**
228+
* @test
229+
*/
230+
public function getColNoInitiallyReturnsZero(): void
231+
{
232+
self::assertSame(0, $this->subject->getColNo());
233+
}
234+
235+
/**
236+
* @test
237+
*
238+
* @dataProvider provideColumnNumber
239+
*/
240+
public function getColNoReturnsColumnNumberSet(int $columnNumber): void
241+
{
242+
$this->subject->setPosition(1, $columnNumber);
243+
244+
self::assertSame($columnNumber, $this->subject->getColNo());
245+
}
246+
247+
/**
248+
* @test
249+
*/
250+
public function getColNoReturnsZeroAfterColumnNumberCleared(): void
251+
{
252+
$this->subject->setPosition(1, 99);
253+
254+
$this->subject->setPosition(2);
255+
256+
self::assertSame(0, $this->subject->getColNo());
257+
}
258+
}

0 commit comments

Comments
 (0)