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

DerManoMann / json-object-mapper / 3868897722

pending completion
3868897722

push

github

GitHub
Merge pull request #33 from DerManoMann/refresh

357 of 409 relevant lines covered (87.29%)

33.96 hits per line

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

78.75
/src/TypeMapper/ObjectTypeMapper.php
1
<?php declare(strict_types=1);
2

3
/*
4
* This file is part of the ObjectMapper library.
5
*
6
* (c) Martin Rademacher <mano@radebatz.net>
7
*
8
* For the full copyright and license information, please view the LICENSE
9
* file that was distributed with this source code.
10
*/
11

12
namespace Radebatz\ObjectMapper\TypeMapper;
13

14
use Radebatz\ObjectMapper\NamingMapperInterface;
15
use Radebatz\ObjectMapper\ObjectMapper;
16
use Radebatz\ObjectMapper\ObjectMapperException;
17
use Radebatz\ObjectMapper\DeserializerAwareInterface;
18
use Radebatz\ObjectMapper\TypeReference\ClassTypeReference;
19
use Radebatz\ObjectMapper\TypeReference\ObjectTypeReference;
20
use Radebatz\ObjectMapper\TypeReference\TypeReferenceFactory;
21
use Radebatz\ObjectMapper\TypeReferenceInterface;
22
use Radebatz\PropertyInfoExtras\PropertyInfoExtraExtractorInterface;
23
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
24

25
/**
26
 * Generic object type mapper.
27
 */
28
class ObjectTypeMapper extends AbstractTypeMapper
29
{
30
    public function map($value, ?TypeReferenceInterface $typeReference = null, $key = null)
31
    {
32
        if (!$typeReference || null === $value) {
67✔
33
            return $value;
42✔
34
        }
35

36
        $propertyInfoExtractor = $this->getObjectMapper()->getPropertyInfoExtractor();
67✔
37
        $propertyAccessor = $this->getObjectMapper()->getPropertyAccessor();
67✔
38

39
        $obj = null;
67✔
40
        $resolvedTypeClassName = null;
67✔
41
        $ctorArg = false;
67✔
42
        if ($typeReference instanceof ObjectTypeReference) {
67✔
43
            $obj = $typeReference->getObject();
9✔
44
        } elseif ($typeReference instanceof ClassTypeReference) {
58✔
45
            $typeClassName = $typeReference->getClassName();
58✔
46

47
            $resolvedTypeClassName = $this->resolveValueType($typeClassName, $value);
58✔
48

49
            if (is_object($value) && $value instanceof $resolvedTypeClassName) {
58✔
50
                return $value;
1✔
51
            }
52

53
            if (in_array($resolvedTypeClassName, ['integer', 'float', 'string', 'boolean'])) {
58✔
54
                settype($value, $resolvedTypeClassName);
×
55

56
                return $value;
×
57
            }
58

59
            list($obj, $ctorArg) = $this->instantiate($value, $resolvedTypeClassName);
58✔
60
        } else {
61
            throw new ObjectMapperException(sprintf('Unexpected type reference: %s', get_class($typeReference)));
×
62
        }
63

64
        if (!is_object($value)) {
65✔
65
            if (($obj instanceof \UnitEnum)) {
2✔
66
                return $obj;
2✔
67
            }
68

69
            if ($this->getObjectMapper()->isOption(ObjectMapper::OPTION_STRICT_TYPES)) {
×
70
                throw new ObjectMapperException(sprintf('Incompatible data type; name=%s, class=%s, type=%s, expected=object', $key, $resolvedTypeClassName ?: 'N/A', gettype($value)));
×
71
            }
72

73
            if ($ctorArg) {
×
74
                if ($obj instanceof DeserializerAwareInterface) {
×
75
                    $obj->deserialized($this->getObjectMapper());
×
76
                }
77

78
                return $obj;
×
79
            }
80
        }
81

82
        // keep track of mapped properties in case we want to verify required ones later
83
        $mappedProperties = [];
65✔
84

85
        $properties = (array) $propertyInfoExtractor->getAllProperties(get_class($obj));
65✔
86

87
        foreach ((array) $value as $valueKey => $val) {
65✔
88
            $keys = array_map(function (NamingMapperInterface $namingMapper) use ($valueKey) {
65✔
89
                return $namingMapper->resolve($valueKey);
65✔
90
            }, $this->getObjectMapper()->getNamingMappers());
65✔
91

92
            $mapped = false;
65✔
93
            foreach ($keys as $key) {
65✔
94
                if (null === $key) {
65✔
95
                    continue;
×
96
                }
97

98
                if (in_array($key, $properties)) {
65✔
99
                    $mappedValue = $this->mapValue($obj, $key, $val, $propertyInfoExtractor);
64✔
100
                    try {
101
                        $propertyAccessor->setValue($obj, $key, $mappedValue);
62✔
102
                        $mapped = true;
62✔
103
                    } catch (NoSuchPropertyException $e) {
1✔
104
                        // ignore
105
                    } catch (\Throwable $t) {
×
106
                        throw new ObjectMapperException($t->getMessage(), $t->getCode(), $t);
×
107
                    }
108
                    break;
62✔
109
                } elseif (get_class($obj) === \stdClass::class) {
36✔
110
                    $mappedValue = $this->mapValue($obj, $key, $val, $propertyInfoExtractor);
×
111
                    $obj->{$key} = $mappedValue;
×
112
                    $mapped = true;
×
113
                    break;
×
114
                }
115
            }
116

117
            $mappedProperties[] = $mapped ? $key : $this->handleUnmappedProperty($obj, $key, $val);
63✔
118
        }
119

120
        if ($this->getObjectMapper()->isOption(ObjectMapper::OPTION_VERIFY_REQUIRED)) {
62✔
121
            $this->verifyRequiredProperties(get_class($obj), $properties, $mappedProperties);
2✔
122
        }
123

124
        if ($obj instanceof DeserializerAwareInterface) {
61✔
125
            $obj->deserialized($this->getObjectMapper());
1✔
126
        }
127

128
        return $obj;
61✔
129
    }
130

131
    protected function mapValue($obj, $key, $val, PropertyInfoExtraExtractorInterface $propertyInfoExtractor)
132
    {
133
        // default for untyped data
134
        $valueTypeReferences = [null];
64✔
135
        if ($types = $propertyInfoExtractor->getAllTypes(get_class($obj), $key)) {
64✔
136
            $valueTypeReferences = array_map(function ($type) {
63✔
137
                return TypeReferenceFactory::getTypeReferenceForType($type);
63✔
138
            }, $types);
63✔
139
        }
140

141
        if (null === $val && $valueTypeReferences
64✔
142
            && !array_reduce($valueTypeReferences, function ($carry, $item) {
64✔
143
                return $carry || null === $item || $item->isNullable();
45✔
144
            }, false)
64✔
145
            && $this->getObjectMapper()->isOption(ObjectMapper::OPTION_STRICT_NULL)) {
64✔
146
            throw new ObjectMapperException(sprintf('Unmappable null value; name=%s, class=%s', $key, get_class($obj)));
×
147
        }
148

149
        foreach ($valueTypeReferences as $valueTypeReference) {
64✔
150
            try {
151
                $valueTypeMapper = $this->getObjectMapper()->getTypeMapper($val, $valueTypeReference);
64✔
152

153
                return $valueTypeMapper->map($val, $valueTypeReference, $key);
64✔
154
            } catch (ObjectMapperException $e) {
5✔
155
                // ignore
156
            }
157
        }
158

159
        throw new ObjectMapperException(sprintf(
2✔
160
            'Incompatible value type; key=%s, type=%s, expected=%s',
2✔
161
            $key,
2✔
162
            gettype($val),
2✔
163
            implode(', ', array_map(function ($valueTypeReference) {
2✔
164
                return $valueTypeReference->getType();
2✔
165
            }, $valueTypeReferences))
2✔
166
        ), 0, 1 === count($valueTypeReferences) ? $e : null);
2✔
167
    }
168
}
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

© 2025 Coveralls, Inc