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

tempestphp / tempest-framework / 14024978163

23 Mar 2025 05:55PM UTC coverage: 79.391% (-0.05%) from 79.441%
14024978163

push

github

web-flow
feat(view): cache Blade and Twig templates in internal storage (#1061)

2 of 2 new or added lines in 2 files covered. (100.0%)

912 existing lines in 110 files now uncovered.

10478 of 13198 relevant lines covered (79.39%)

91.09 hits per line

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

88.17
/src/Tempest/Mapper/src/ObjectFactory.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Tempest\Mapper;
6

7
use Closure;
8
use Tempest\Container\Container;
9
use Tempest\Mapper\Exceptions\CannotMapDataException;
10
use Tempest\Mapper\Exceptions\MissingMapperException;
11
use Tempest\Mapper\Mappers\ArrayToJsonMapper;
12
use Tempest\Mapper\Mappers\JsonToArrayMapper;
13
use Tempest\Mapper\Mappers\ObjectToArrayMapper;
14
use Tempest\Mapper\Mappers\ObjectToJsonMapper;
15
use Tempest\Reflection\FunctionReflector;
16

17
/** @template ClassType */
18
final class ObjectFactory
19
{
20
    private mixed $from;
21

22
    private mixed $to;
23

24
    private array $with = [];
25

26
    private bool $isCollection = false;
27

28
    public function __construct(
143✔
29
        private readonly MapperConfig $config,
30
        private readonly Container $container,
31
    ) {
32
    }
143✔
33

34
    /**
35
     * @template T of object
36
     * @param T|class-string<T> $objectOrClass
37
     * @return self<T>
38
     */
39
    public function forClass(mixed $objectOrClass): self
71✔
40
    {
41
        $this->to = $objectOrClass;
71✔
42

43
        return $this;
71✔
44
    }
45

46
    public function withData(mixed $data): self
122✔
47
    {
48
        $this->from = $data;
122✔
49

50
        return $this;
122✔
51
    }
52

53
    /**
54
     * @return self<ClassType[]>
55
     */
56
    public function collection(): self
54✔
57
    {
58
        $this->isCollection = true;
54✔
59

60
        return $this;
54✔
61
    }
62

63
    /**
64
     * @return ClassType
65
     */
66
    public function from(mixed $data): mixed
71✔
67
    {
68
        return $this->mapObject(
71✔
69
            from: $data,
71✔
70
            to: $this->to,
71✔
71
            isCollection: $this->isCollection,
71✔
72
        );
71✔
73
    }
74

75
    /**
76
     * @template MapperType of \Tempest\Mapper\Mapper
77
     * @param Closure(MapperType $mapper, mixed $from): mixed|class-string<\Tempest\Mapper\Mapper> ...$mappers
78
     * @return self<ClassType>
79
     */
80
    public function with(Closure|string ...$mappers): self
59✔
81
    {
82
        $this->with = [...$this->with, ...$mappers];
59✔
83

84
        return $this;
59✔
85
    }
86

87
    /**
88
     * @template T of object
89
     * @param T|class-string<T>|string $to
90
     * @return T|T[]|mixed
91
     */
92
    public function to(mixed $to): mixed
105✔
93
    {
94
        return $this->mapObject(
105✔
95
            from: $this->from,
105✔
96
            to: $to,
105✔
97
            isCollection: $this->isCollection,
105✔
98
        );
105✔
99
    }
100

101
    public function do(): mixed
53✔
102
    {
103
        if ($this->with === []) {
53✔
104
            throw new MissingMapperException();
1✔
105
        }
106

107
        $result = $this->from;
52✔
108

109
        foreach ($this->with as $mapper) {
52✔
110
            $result = $this->mapWith(
52✔
111
                mapper: $mapper,
52✔
112
                from: $result,
52✔
113
                to: null,
52✔
114
            );
52✔
115
        }
116

117
        return $result;
52✔
118
    }
119

120
    public function toArray(): array
4✔
121
    {
122
        if (is_object($this->from)) {
4✔
123
            return $this->with(ObjectToArrayMapper::class)->do();
4✔
124
        }
125

UNCOV
126
        if (is_array($this->from)) {
×
UNCOV
127
            return $this->from;
×
128
        }
129

UNCOV
130
        if (is_string($this->from) && json_validate($this->from)) {
×
UNCOV
131
            return $this->with(JsonToArrayMapper::class)->do();
×
132
        }
133

UNCOV
134
        throw new CannotMapDataException($this->from, 'array');
×
135
    }
136

UNCOV
137
    public function toJson(): string
×
138
    {
UNCOV
139
        if (is_object($this->from)) {
×
UNCOV
140
            return $this->with(ObjectToJsonMapper::class)->do();
×
141
        }
142

UNCOV
143
        if (is_array($this->from)) {
×
UNCOV
144
            return $this->with(ArrayToJsonMapper::class)->do();
×
145
        }
146

UNCOV
147
        throw new CannotMapDataException($this->from, 'json');
×
148
    }
149

150
    /**
151
     * @template T of object
152
     * @param T|class-string<T>|string $to
153
     * @return T|mixed
154
     */
155
    public function map(mixed $from, mixed $to): mixed
2✔
156
    {
157
        return $this->mapObject(
2✔
158
            from: $from,
2✔
159
            to: $to,
2✔
160
            isCollection: $this->isCollection,
2✔
161
        );
2✔
162
    }
163

164
    private function mapObject(
126✔
165
        mixed $from,
166
        mixed $to,
167
        bool $isCollection,
168
    ): mixed {
169
        // Map collections
170
        if ($isCollection && is_array($from)) {
126✔
171
            return array_map(
5✔
172
                fn (mixed $item) => $this->mapObject(
5✔
173
                    from: $item,
5✔
174
                    to: $to,
5✔
175
                    isCollection: false,
5✔
176
                ),
5✔
177
                $from,
5✔
178
            );
5✔
179
        }
180

181
        // Map using explicitly defined mappers
182
        if ($this->with) {
126✔
183
            $result = $from;
13✔
184

185
            foreach ($this->with as $mapper) {
13✔
186
                $result = $this->mapWith(
13✔
187
                    mapper: $mapper,
13✔
188
                    from: $result,
13✔
189
                    to: $to,
13✔
190
                );
13✔
191
            }
192

193
            return $result;
11✔
194
        }
195

196
        // Map using an inferred mapper
197
        $mappers = $this->config->mappers;
123✔
198

199
        foreach ($mappers as $mapperClass) {
123✔
200
            /** @var Mapper $mapper */
201
            $mapper = $this->container->get($mapperClass);
123✔
202

203
            if ($mapper->canMap(from: $from, to: $to)) {
123✔
204
                return $mapper->map(from: $from, to: $to);
122✔
205
            }
206
        }
207

208
        throw new CannotMapDataException($from, $to);
1✔
209
    }
210

211
    /**
212
     * @template MapperType of \Tempest\Mapper\Mapper
213
     * @param Closure(MapperType $mapper, mixed $from): mixed|class-string<\Tempest\Mapper\Mapper> $mapper
214
     */
215
    private function mapWith(
59✔
216
        mixed $mapper,
217
        mixed $from,
218
        mixed $to,
219
    ): mixed {
220
        if ($mapper instanceof Closure) {
59✔
221
            $function = new FunctionReflector($mapper);
1✔
222

223
            $data = [
1✔
224
                'from' => $from,
1✔
225
            ];
1✔
226

227
            foreach ($function->getParameters() as $parameter) {
1✔
228
                $data[$parameter->getName()] ??= $this->container->get($parameter->getType()->getName());
1✔
229
            }
230

231
            return $mapper(...$data);
1✔
232
        }
233

234
        $mapper = $this->container->get($mapper);
59✔
235

236
        /** @var Mapper $mapper */
237
        return $mapper->map($from, $to);
59✔
238
    }
239
}
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