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

diego-ninja / granite / 16608963009

29 Jul 2025 10:37PM UTC coverage: 50.423%. First build
16608963009

Pull #5

github

web-flow
Merge 43d8840d7 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

6.9
/src/Mapping/Core/ObjectFactory.php
1
<?php
2

3
namespace Ninja\Granite\Mapping\Core;
4

5
use Exception;
6
use Ninja\Granite\Contracts\GraniteObject;
7
use Ninja\Granite\Mapping\Exceptions\MappingException;
8
use Ninja\Granite\Support\ReflectionCache;
9
use ReflectionClass;
10
use ReflectionException;
11
use ReflectionMethod;
12
use ReflectionNamedType;
13
use ReflectionType;
14
use Throwable;
15

16
final readonly class ObjectFactory
17
{
18
    /**
19
     * @param array $data
20
     * @param class-string $className
21
     * @throws MappingException
22
     */
23
    public function create(array $data, string $className): object
20✔
24
    {
25
        if ('stdClass' === $className) {
20✔
26
            return (object) $data;
×
27
        }
28

29
        if (is_subclass_of($className, GraniteObject::class)) {
20✔
30
            return $className::from($data);
20✔
31
        }
32

33
        return $this->createFromReflection($data, $className);
×
34
    }
35

36
    /**
37
     * @throws MappingException
38
     */
39
    public function populate(object $object, array $data): object
×
40
    {
41
        try {
42
            $reflection = ReflectionCache::getClass(get_class($object));
×
43

44
            foreach ($data as $propName => $propValue) {
×
45
                if ($reflection->hasProperty($propName)) {
×
46
                    $property = $reflection->getProperty($propName);
×
NEW
47
                    if ($property->isPublic() && ! $property->isReadOnly()) {
×
48
                        $property->setValue($object, $propValue);
×
49
                    }
50
                }
51
            }
52

53
            return $object;
×
54
        } catch (Exception $e) {
×
55
            throw new MappingException('array', get_class($object), "Failed to populate object: " . $e->getMessage());
×
56
        }
57
    }
58

59
    /**
60
     * @param array $data Source data
61
     * @param class-string $className Target class name
62
     * @throws MappingException
63
     */
64
    private function createFromReflection(array $data, string $className): object
×
65
    {
66
        try {
67
            $reflection = ReflectionCache::getClass($className);
×
68
            $constructor = $reflection->getConstructor();
×
69

70
            if ($constructor) {
×
71
                $instance = $this->createWithConstructor($reflection, $constructor, $data);
×
72
            } else {
73
                $instance = $reflection->newInstanceWithoutConstructor();
×
74
            }
75

76
            // Set remaining properties
77
            $this->setRemainingProperties($instance, $data, $reflection);
×
78

79
            return $instance;
×
80
        } catch (Exception $e) {
×
81
            throw new MappingException('array', $className, "Failed to create instance: " . $e->getMessage());
×
82
        }
83
    }
84

85
    /**
86
     * @throws ReflectionException
87
     */
NEW
88
    private function createWithConstructor(ReflectionClass $reflection, ReflectionMethod $constructor, array &$data): object
×
89
    {
90
        $args = [];
×
91
        $parameters = $constructor->getParameters();
×
92

93
        foreach ($parameters as $param) {
×
94
            $paramName = $param->getName();
×
95

96
            if (array_key_exists($paramName, $data)) {
×
97
                $args[] = $data[$paramName];
×
98
                unset($data[$paramName]);
×
99
            } elseif ($param->isDefaultValueAvailable()) {
×
100
                $args[] = $param->getDefaultValue();
×
101
            } elseif ($param->allowsNull()) {
×
102
                $args[] = null;
×
103
            } else {
104
                $args[] = $this->getDefaultValueForType($param->getType());
×
105
            }
106
        }
107

108
        return $reflection->newInstanceArgs($args);
×
109
    }
110

NEW
111
    private function setRemainingProperties(object $instance, array $data, ReflectionClass $reflection): void
×
112
    {
113
        foreach ($data as $propName => $propValue) {
×
114
            try {
115
                if ($reflection->hasProperty($propName)) {
×
116
                    $property = $reflection->getProperty($propName);
×
NEW
117
                    if ($property->isPublic() && ! $property->isReadOnly()) {
×
118
                        $property->setValue($instance, $propValue);
×
119
                    }
120
                }
NEW
121
            } catch (Throwable) {
×
122
                // Ignore errors when setting properties
123
            }
124
        }
125
    }
126

127
    private function getDefaultValueForType(?ReflectionType $type): mixed
×
128
    {
NEW
129
        if ( ! $type instanceof ReflectionNamedType) {
×
130
            return null;
×
131
        }
132

133
        return match ($type->getName()) {
×
134
            'int' => 0,
×
135
            'float' => 0.0,
×
136
            'bool' => false,
×
137
            'string' => '',
×
138
            'array' => [],
×
NEW
139
            default => null,
×
140
        };
×
141
    }
142
}
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