Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
58.82% covered (warning)
58.82%
10 / 17
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
AnPlusB
58.82% covered (warning)
58.82%
10 / 17
80.00% covered (warning)
80.00%
4 / 5
22.05
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 odd
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 even
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 matches
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 toString
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2
3declare(strict_types=1);
4
5namespace Phpdftk\Css\Selector;
6
7/**
8 * An+B notation per CSS Syntax 3 §6 / Selectors 4 §11.1, used by
9 * `:nth-child(...)` and friends.
10 *
11 * Indices are 1-based per spec — `:nth-child(1)` matches the first child.
12 */
13final readonly class AnPlusB
14{
15    public function __construct(public int $a, public int $b) {}
16
17    public static function odd(): self
18    {
19        return new self(2, 1);
20    }
21
22    public static function even(): self
23    {
24        return new self(2, 0);
25    }
26
27    /**
28     * Does the (1-based) index match this An+B expression? Per spec:
29     * exists integer n ≥ 0 such that a·n + b = index.
30     */
31    public function matches(int $index): bool
32    {
33        if ($this->a === 0) {
34            return $index === $this->b;
35        }
36        $diff = $index - $this->b;
37        if (intdiv($diff, $this->a) * $this->a !== $diff) {
38            return false;
39        }
40        $n = intdiv($diff, $this->a);
41        return $n >= 0;
42    }
43
44    public function toString(): string
45    {
46        if ($this->a === 0) {
47            return (string) $this->b;
48        }
49        $sign = $this->b >= 0 ? '+' : '-';
50        $coefficient = match (true) {
51            $this->a === 1 => '',
52            $this->a === -1 => '-',
53            default => (string) $this->a,
54        };
55        return $coefficient . 'n' . $sign . abs($this->b);
56    }
57}