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

azjezz / psl / 22680190548

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

Pull #610

github

web-flow
Merge 54e750308 into caa083fec
Pull Request #610: perf: replace PSL calls with native PHP builtins and fix O(n2) algorithms

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.46
/src/Psl/Type/Internal/MutableMapType.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\MutableMapInterface<Tk, Tv>>
24
 *
25
 * @internal
26
 */
27
final readonly class MutableMapType 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
    ) {}
48✔
39

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

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

57
        return true;
7✔
58
    }
59

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

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

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

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

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

117
            $dict = Dict\from_entries($entries);
7✔
118

119
            return new Collection\MutableMap($dict);
7✔
120
        }
121

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

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

141
            /** @var list<list{Tk, Tv}> $entries */
142
            $entries = [];
10✔
143

144
            $k = null;
10✔
145
            $v = null;
10✔
146
            /** @var bool $trying_key */
147
            $trying_key = true;
10✔
148

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

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

169
            $dict = Dict\from_entries($entries);
7✔
170

171
            return new Collection\MutableMap($dict);
7✔
172
        }
173

174
        throw AssertException::withValue($value, $this->toString());
10✔
175
    }
176

177
    #[Override]
178
    public function toString(): string
179
    {
180
        return Str\format(
31✔
181
            '%s<%s, %s>',
31✔
182
            Collection\MutableMapInterface::class,
31✔
183
            $this->key_type->toString(),
31✔
184
            $this->value_type->toString(),
31✔
185
        );
31✔
186
    }
187
}
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