• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

azjezz / psl / 22680098232

04 Mar 2026 05:02PM UTC coverage: 97.34% (-1.0%) from 98.375%
22680098232

push

github

azjezz
perf: replace PSL calls with native PHP builtins and fix O(n2) algorithms

Signed-off-by: azjezz <azjezz@protonmail.com>

319 of 355 new or added lines in 90 files covered. (89.86%)

64 existing lines in 12 files now uncovered.

9258 of 9511 relevant lines covered (97.34%)

34.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.41
/src/Psl/Type/Internal/MapType.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Psl\Type\Internal;
6

7
use Override;
8
use Psl\Collection;
9
use Psl\Dict;
10
use Psl\Str;
11
use Psl\Type;
12
use Psl\Type\Exception\AssertException;
13
use Psl\Type\Exception\CoercionException;
14
use Throwable;
15

16
use function is_iterable;
17
use function is_object;
18

19
/**
20
 * @template Tk of array-key
21
 * @template Tv
22
 *
23
 * @extends Type\Type<Collection\MapInterface<Tk, Tv>>
24
 *
25
 * @internal
26
 */
27
final readonly class MapType extends Type\Type
28
{
29
    /**
30
     * @psalm-mutation-free
31
     *
32
     * @param Type\TypeInterface<Tk> $key_type
33
     * @param Type\TypeInterface<Tv> $value_type
34
     */
35
    public function __construct(
36
        private Type\TypeInterface $key_type,
37
        private Type\TypeInterface $value_type,
38
    ) {}
47✔
39

40
    /**
41
     * @psalm-assert-if-true Collection\MapInterface<Tk, Tv> $value
42
     */
43
    #[Override]
44
    public function matches(mixed $value): bool
45
    {
46
        if (!is_object($value) || !$value instanceof Collection\MapInterface) {
15✔
47
            return false;
9✔
48
        }
49

50
        // @mago-expect analysis:mixed-assignment
51
        foreach ($value as $k => $v) {
6✔
52
            if (!$this->key_type->matches($k) || !$this->value_type->matches($v)) {
6✔
NEW
53
                return false;
×
54
            }
55
        }
56

57
        return true;
6✔
58
    }
59

60
    /**
61
     * @throws CoercionException
62
     *
63
     * @return Collection\MapInterface<Tk, Tv>
64
     */
65
    #[Override]
66
    public function coerce(mixed $value): Collection\MapInterface
67
    {
68
        if (is_iterable($value)) {
24✔
69
            /** @var Type\Type<Tk> $key_type */
70
            $key_type = $this->key_type;
17✔
71
            /** @var Type\Type<Tv> $value_type */
72
            $value_type = $this->value_type;
17✔
73

74
            /** @var list<array{Tk, Tv}> $entries */
75
            $entries = [];
17✔
76

77
            $k = null;
17✔
78
            $v = null;
17✔
79
            /** @var bool $trying_key */
80
            $trying_key = true;
17✔
81
            /** @var bool $iterating */
82
            $iterating = true;
17✔
83

84
            try {
85
                /**
86
                 * @var Tk $k
87
                 * @var Tv $v
88
                 */
89
                foreach ($value as $k => $v) {
17✔
90
                    $iterating = false;
15✔
91
                    $trying_key = true;
15✔
92
                    $k_result = $key_type->coerce($k);
15✔
93
                    $trying_key = false;
12✔
94
                    $v_result = $value_type->coerce($v);
12✔
95

96
                    $entries[] = [$k_result, $v_result];
10✔
97
                    $iterating = true;
10✔
98
                }
99
            } catch (Throwable $e) {
8✔
100
                throw match (true) {
101
                    $iterating => CoercionException::withValue(
8✔
102
                        null,
8✔
103
                        $this->toString(),
8✔
104
                        PathExpression::iteratorError($k),
8✔
105
                        $e,
8✔
106
                    ),
8✔
107
                    $trying_key => CoercionException::withValue(
5✔
108
                        $k,
5✔
109
                        $this->toString(),
5✔
110
                        PathExpression::iteratorKey($k),
5✔
111
                        $e,
5✔
112
                    ),
5✔
113
                    !$trying_key => CoercionException::withValue($v, $this->toString(), PathExpression::path($k), $e),
2✔
114
                };
115
            }
116

117
            return new Collection\Map(Dict\from_entries($entries));
9✔
118
        }
119

120
        throw CoercionException::withValue($value, $this->toString());
7✔
121
    }
122

123
    /**
124
     * @throws AssertException
125
     *
126
     * @return Collection\MapInterface<Tk, Tv>
127
     *
128
     * @psalm-assert Collection\MapInterface<Tk, Tv> $value
129
     */
130
    #[Override]
131
    public function assert(mixed $value): Collection\MapInterface
132
    {
133
        if (is_object($value) && $value instanceof Collection\MapInterface) {
18✔
134
            /** @var Type\Type<Tk> $key_type */
135
            $key_type = $this->key_type;
9✔
136
            /** @var Type\Type<Tv> $value_type */
137
            $value_type = $this->value_type;
9✔
138

139
            /** @var list<array{Tk, Tv}> $entries */
140
            $entries = [];
9✔
141

142
            $k = null;
9✔
143
            $v = null;
9✔
144
            $trying_key = true;
9✔
145

146
            try {
147
                /**
148
                 * @var Tk $k
149
                 * @var Tv $v
150
                 */
151
                foreach ($value as $k => $v) {
9✔
152
                    $trying_key = true;
9✔
153
                    $k_result = $key_type->assert($k);
9✔
154
                    $trying_key = false;
8✔
155
                    $v_result = $value_type->assert($v);
8✔
156

157
                    $entries[] = [$k_result, $v_result];
6✔
158
                }
159
            } catch (AssertException $e) {
3✔
160
                throw match ($trying_key) {
161
                    true => AssertException::withValue($k, $this->toString(), PathExpression::iteratorKey($k), $e),
2✔
162
                    false => AssertException::withValue($v, $this->toString(), PathExpression::path($k), $e),
2✔
163
                };
164
            }
165

166
            return new Collection\Map(Dict\from_entries($entries));
6✔
167
        }
168

169
        throw AssertException::withValue($value, $this->toString());
9✔
170
    }
171

172
    #[Override]
173
    public function toString(): string
174
    {
175
        return Str\format(
31✔
176
            '%s<%s, %s>',
31✔
177
            Collection\MapInterface::class,
31✔
178
            $this->key_type->toString(),
31✔
179
            $this->value_type->toString(),
31✔
180
        );
31✔
181
    }
182
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc