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

diego-ninja / granite / 16609029965

29 Jul 2025 10:42PM UTC coverage: 50.423%. First build
16609029965

Pull #5

github

web-flow
Merge 722918909 into 6a6caca51
Pull Request #5: code: adds phpstan level max, pint with per and github actions

321 of 632 new or added lines in 77 files covered. (50.79%)

1132 of 2245 relevant lines covered (50.42%)

9.88 hits per line

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

49.4
/src/Mapping/ObjectMapper.php
1
<?php
2

3
namespace Ninja\Granite\Mapping;
4

5
use Ninja\Granite\Enums\CacheType;
6
use Ninja\Granite\Exceptions\GraniteException;
7
use Ninja\Granite\Exceptions\ReflectionException;
8
use Ninja\Granite\Mapping\Cache\CacheFactory;
9
use Ninja\Granite\Mapping\Contracts\Mapper;
10
use Ninja\Granite\Mapping\Contracts\MappingCache;
11
use Ninja\Granite\Mapping\Contracts\MappingStorage;
12
use Ninja\Granite\Mapping\Contracts\NamingConvention;
13
use Ninja\Granite\Mapping\Core\ConfigurationBuilder;
14
use Ninja\Granite\Mapping\Core\MappingEngine;
15
use Ninja\Granite\Mapping\Exceptions\MappingException;
16

17
/**
18
 * Main ObjectMapper facade providing a clean, fluent API.
19
 * Delegates actual work to specialized components.
20
 */
21
final class ObjectMapper implements Mapper, MappingStorage
22
{
23
    private MappingEngine $engine;
24
    private ConfigurationBuilder $configBuilder;
25
    private MappingCache $cache;
26
    private array $profiles = [];
27

28
    // Singleton support
29
    private static ?self $globalInstance = null;
30
    private static bool $isConfigured = false;
31

32
    /**
33
     * @throws ReflectionException
34
     */
35
    public function __construct(?MapperConfig $config = null)
86✔
36
    {
37
        $config ??= MapperConfig::default();
86✔
38

39
        $this->cache = CacheFactory::create($config->cacheType);
86✔
40
        $this->configBuilder = new ConfigurationBuilder(
86✔
41
            $this->cache,
86✔
42
            $config->useConventions,
86✔
43
            $config->conventionThreshold,
86✔
44
        );
86✔
45
        $this->engine = new MappingEngine($this->configBuilder);
86✔
46

47
        $this->registerProfiles($config->profiles);
86✔
48

49
        if ($config->warmupCache) {
86✔
50
            $this->warmupCache();
85✔
51
        }
52
    }
53

54
    // ================
55
    // Global Instance
56
    // ================
57

58
    /**
59
     * @throws ReflectionException
60
     */
61
    public static function getInstance(): self
40✔
62
    {
63
        if (null === self::$globalInstance) {
40✔
64
            self::$globalInstance = new self(
1✔
65
                MapperConfig::default()
1✔
66
                    ->withCacheType(CacheType::Shared)
1✔
67
                    ->withConventions(true, 0.75)
1✔
68
                    ->withoutWarmup(),
1✔
69
            );
1✔
70
        }
71

72
        return self::$globalInstance;
40✔
73
    }
74

75
    /**
76
     * @throws ReflectionException
77
     */
NEW
78
    public static function configure(callable $configurator): void
×
79
    {
NEW
80
        $config = MapperConfig::default();
×
NEW
81
        $configurator($config);
×
82

NEW
83
        self::$globalInstance = new self($config);
×
NEW
84
        self::$isConfigured = true;
×
85
    }
86

NEW
87
    public static function isConfigured(): bool
×
88
    {
NEW
89
        return self::$isConfigured;
×
90
    }
91

NEW
92
    public static function reset(): void
×
93
    {
NEW
94
        self::$globalInstance = null;
×
NEW
95
        self::$isConfigured = false;
×
96
    }
97

NEW
98
    public static function setGlobalInstance(self $instance): void
×
99
    {
NEW
100
        self::$globalInstance = $instance;
×
NEW
101
        self::$isConfigured = true;
×
102
    }
103

104
    // =================
105
    // Core Mapping API
106
    // =================
107

108
    /**
109
     * @template T of object
110
     * @param mixed $source
111
     * @param class-string<T> $destinationType
112
     * @return T
113
     * @throws GraniteException
114
     * @throws MappingException
115
     */
116
    public function map(mixed $source, string $destinationType): object
96✔
117
    {
118
        /** @var T */
119
        return $this->engine->map($source, $destinationType);
96✔
120
    }
121

122
    /**
123
     * @throws MappingException
124
     */
125
    public function mapTo(mixed $source, object $destination): object
2✔
126
    {
127
        return $this->engine->mapTo($source, $destination);
2✔
128
    }
129

130
    /**
131
     * @template T of object
132
     * @param array $source Source array
133
     * @param class-string<T> $destinationType Destination type
134
     * @return array<T> Array of mapped objects
135
     * @throws GraniteException
136
     * @throws MappingException
137
     */
138
    public function mapArray(array $source, string $destinationType): array
9✔
139
    {
140
        return array_map(
9✔
141
            fn($item) => $this->map($item, $destinationType),
9✔
142
            $source,
9✔
143
        );
9✔
144
    }
145

146
    // ======================
147
    // Mapping Configuration
148
    // ======================
149

150
    /**
151
     * @param class-string $sourceType Source type
152
     * @param class-string $destinationType Destination type
153
     */
154
    public function createMap(string $sourceType, string $destinationType): TypeMapping
×
155
    {
156
        return new TypeMapping($this, $sourceType, $destinationType);
×
157
    }
158

159
    /**
160
     * @param class-string $typeA Type A
161
     * @param class-string $typeB Type B
162
     */
163
    public function createMapBidirectional(string $typeA, string $typeB): BidirectionalTypeMapping
×
164
    {
165
        return new BidirectionalTypeMapping($this, $typeA, $typeB);
×
166
    }
167

168
    /**
169
     * @param class-string $sourceType
170
     * @param class-string $destinationType
171
     * @throws MappingException
172
     */
173
    public function createReverseMap(string $sourceType, string $destinationType): TypeMapping
×
174
    {
175
        $reverseMapping = $this->createMap($destinationType, $sourceType);
×
176
        $this->configBuilder->createReverseConfiguration($sourceType, $destinationType, $reverseMapping);
×
177
        return $reverseMapping;
×
178
    }
179

180
    // ==================
181
    // Profile Management
182
    // ==================
183

184
    public function addProfile(MappingProfile $profile): self
58✔
185
    {
186
        $this->profiles[] = $profile;
58✔
187
        $this->configBuilder->addProfile($profile);
58✔
188
        return $this;
58✔
189
    }
190

191
    // =================
192
    // Convention System
193
    // =================
194

195
    public function useConventions(bool $enabled = true): self
×
196
    {
197
        $this->configBuilder->enableConventions($enabled);
×
198
        return $this;
×
199
    }
200

201
    public function setConventionThreshold(float $threshold): self
×
202
    {
203
        $this->configBuilder->setConventionThreshold($threshold);
×
204
        return $this;
×
205
    }
206

207
    public function registerConvention(NamingConvention $convention): self
×
208
    {
209
        $this->configBuilder->registerConvention($convention);
×
210
        return $this;
×
211
    }
212

213
    // ==============
214
    // Cache Management
215
    // ==============
216

217
    public function clearCache(): self
×
218
    {
219
        $this->cache->clear();
×
220
        $this->configBuilder->clearCache();
×
221
        return $this;
×
222
    }
223

224
    public function warmupCache(): self
85✔
225
    {
226
        $this->configBuilder->warmupCache($this->profiles);
85✔
227
        return $this;
85✔
228
    }
229

230
    public function getCache(): MappingCache
×
231
    {
232
        return $this->cache;
×
233
    }
234

235
    // ======================
236
    // MappingStorage Interface
237
    // ======================
238

239
    public function addPropertyMapping(string $sourceType, string $destinationType, string $property, PropertyMapping $mapping): void
×
240
    {
241
        $this->configBuilder->addPropertyMapping($sourceType, $destinationType, $property, $mapping);
×
242
    }
243

244
    public function getMapping(string $sourceType, string $destinationType, string $property): ?PropertyMapping
×
245
    {
246
        return $this->configBuilder->getMapping($sourceType, $destinationType, $property);
×
247
    }
248

249
    public function getMappingsForTypes(string $sourceType, string $destinationType): array
×
250
    {
251
        return $this->configBuilder->getMappingsForTypes($sourceType, $destinationType);
×
252
    }
253

254
    private function registerProfiles(array $profiles): void
86✔
255
    {
256
        foreach ($profiles as $profile) {
86✔
257
            if ($profile instanceof MappingProfile) {
57✔
258
                $this->addProfile($profile);
57✔
259
            }
260
        }
261
    }
262
}
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