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

tylernathanreed / jira-client-php / 14084284872

26 Mar 2025 01:17PM UTC coverage: 81.757% (+0.6%) from 81.124%
14084284872

push

github

tylernathanreed
~ Fixed getUserEmail test

5620 of 6874 relevant lines covered (81.76%)

10.48 hits per line

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

82.72
/src/Deserializer.php
1
<?php
2

3
namespace Jira\Client;
4

5
use DateTimeImmutable;
6
use Jira\Client\Exceptions\DeserializationException;
7
use ReflectionClass;
8
use ReflectionNamedType;
9
use ReflectionProperty;
10
use TypeError;
11

12
class Deserializer
13
{
14
    /**
15
     * @phpstan-template TDto of Dto
16
     * @param ($array is true ? list<array<string,mixed>> : array<string,mixed>) $data
17
     * @param class-string<TDto> $class
18
     * @return ($array is true ? (TDto is PolymorphicDto ? list<Dto> : list<TDto>) : (TDto is PolymorphicDto ? Dto : TDto))
19
     */
20
    public function deserialize(array $data, string $class, bool $array = false)
425✔
21
    {
22
        if ($array) {
425✔
23
            $values = [];
243✔
24

25
            foreach ($data as $value) {
243✔
26
                $values[] = self::deserialize($value, $class);
243✔
27
            }
28

29
            return $values;
243✔
30
        }
31

32
        if (is_subclass_of($class, PolymorphicDto::class)) {
425✔
33
            $class = $class::discriminateFromData($data);
1✔
34
        }
35

36
        return $this->from($class, $data);
425✔
37
    }
38

39
    /**
40
     * @phpstan-template T of Dto
41
     * @param class-string<T> $class
42
     * @param array<string,mixed> $data
43
     * @return T
44
     */
45
    public function from(string $class, array $data): Dto
425✔
46
    {
47
        try {
48
            return $this->resolve($class, $data);
425✔
49
        } catch (TypeError $e) {
×
50
            throw new DeserializationException(sprintf(
×
51
                'Failed to deserialize [%s]: %s (Data: %s)',
×
52
                $class,
×
53
                substr($e->getMessage(), strlen($class . '::__construct(): ')),
×
54
                json_encode($data),
×
55
            ), previous: $e);
×
56
        }
57
    }
58

59
    /**
60
     * @phpstan-template T of Dto
61
     * @param class-string<T> $class
62
     * @param array<string,mixed> $data
63
     * @return T
64
     */
65
    protected function resolve(string $class, array $data): Dto
425✔
66
    {
67
        $reflector = new ReflectionClass($class);
425✔
68

69
        $parameters = $reflector->getConstructor()?->getParameters() ?? [];
425✔
70

71
        $args = [];
425✔
72

73
        foreach ($parameters as $parameter) {
425✔
74
            $name = $parameter->getName();
425✔
75
            $key = str_starts_with($name, '_')
425✔
76
                ? substr($name, 1)
51✔
77
                : $name;
425✔
78

79
            $property = $reflector->getProperty($name);
425✔
80

81
            $value = array_key_exists($key, $data)
425✔
82
                ? $data[$key]
423✔
83
                : (
425✔
84
                    $parameter->isDefaultValueAvailable()
280✔
85
                    ? $parameter->getDefaultValue()
280✔
86
                    : null
280✔
87
                );
425✔
88

89
            $type = $parameter->getType();
425✔
90

91
            if (is_null($value) && $type instanceof ReflectionNamedType && ! $type->allowsNull()) {
425✔
92
                if ($type->getName() === 'array') {
1✔
93
                    $value = [];
1✔
94
                }
95
            }
96

97
            if (is_null($value)) {
425✔
98
                $args[] = $value;
280✔
99
            } elseif (is_null($type)) {
423✔
100
                $args[] = $value;
×
101
            } elseif (! $type instanceof ReflectionNamedType) {
423✔
102
                $args[] = $value;
×
103
            } elseif (is_array($value) && empty($value) && $type->allowsNull()) {
423✔
104
                $args[] = null;
51✔
105
            } elseif ($type->getName() === 'array' && is_array($value)) {
423✔
106
                $args[] = $this->fromArray($class, $property, $value);
267✔
107
            } elseif ($type->getName() === DateTimeImmutable::class && is_string($value)) {
413✔
108
                $args[] = new DateTimeImmutable($value);
33✔
109
            } elseif ($type->getName() === DateTimeImmutable::class && is_int($value)) {
412✔
110
                $args[] = (new DateTimeImmutable)->setTimestamp($value);
5✔
111
            } elseif (! $type->isBuiltin() && is_subclass_of($type->getName(), Dto::class) && is_array($value)) {
412✔
112
                // @phpstan-ignore argument.type
113
                $args[] = $this->from($type->getName(), $value);
161✔
114
            } else {
115
                $args[] = $value;
412✔
116
            }
117
        }
118

119
        return $reflector->newInstanceArgs($args);
425✔
120
    }
121

122
    /**
123
     * @param class-string<Dto> $class
124
     * @param array<mixed,mixed> $array
125
     * @return array<mixed,mixed>
126
     */
127
    protected function fromArray(string $class, ReflectionProperty $property, array $array): array
267✔
128
    {
129
        $doc = $property->getDocComment();
267✔
130

131
        if (! $doc) {
267✔
132
            return $array;
×
133
        }
134

135
        $var = preg_match('/@var \??([^ ]+)(?:\n| \*)/', $doc, $matches)
267✔
136
            ? $matches[1]
267✔
137
            : null;
×
138

139
        if (! $var) {
267✔
140
            return $array;
×
141
        }
142

143
        if (str_starts_with($var, 'list')) {
267✔
144
            $type = substr($var, strlen('list<'), -strlen('>'));
243✔
145
        } else {
146
            if (! preg_match('/^array<(?<key>[^>]+), ?(?<type>.+)>$/', $var, $matches)) {
63✔
147
                return $array;
×
148
            }
149

150
            $type = $matches['type'];
63✔
151
        }
152

153
        if ($type === 'mixed' || str_starts_with($type, 'list')) {
267✔
154
            return $array;
26✔
155
        }
156

157
        if (in_array($type, ['int', 'float', 'string', 'boolean', 'bool'])) {
265✔
158
            return array_map(function ($v) use ($type) {
127✔
159
                settype($v, $type);
123✔
160
                return $v;
123✔
161
            }, $array);
127✔
162
        }
163

164
        if (class_exists($subclass = 'Jira\\Client\\Schema\\' . $type) && is_subclass_of($subclass, Dto::class)) {
207✔
165
            // @phpstan-ignore argument.type
166
            return $this->deserialize($array, $subclass, array: true);
207✔
167
        }
168

169
        throw new DeserializationException("Unknown class [{$subclass}] when deserializing [{$class}].");
×
170
    }
171
}
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