Completed
Push — master ( 33cd17...4bddcc )
by Arnold
02:40
created

Write::put()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 34
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 20
c 1
b 0
f 0
nc 9
nop 5
dl 0
loc 34
ccs 19
cts 19
cp 1
crap 7
rs 8.6666
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Jasny\DotKey\Internal;
6
7
use Jasny\DotKey\ResolveException;
8
9
/**
10
 * Static class with get and put methods.
11
 * @internal
12
 */
13
final class Write
14
{
15
    /**
16
     * Set a value within the subject by path.
17
     *
18
     * @param object|array<string,mixed> $subject
19
     * @param string                     $path
20
     * @param mixed                      $value
21
     * @param string                     $delimiter
22
     * @return object|array<string,mixed>
23
     * @throws ResolveException
24
     *
25
     * @template TSubject
26
     * @phpstan-param TSubject&(object|array<string,mixed>) $subject
27
     * @phpstan-param string                                $path
28
     * @phpstan-param mixed                                 $value
29
     * @phpstan-param string                                $delimiter
30
     * @phpstan-return TSubject&(object|array<string,mixed>)
31
     */
32 23
    public static function set($subject, string $path, $value, string $delimiter = '.')
33
    {
34 23
        $result =& $subject;
35
36 23
        $index = Helpers::splitPath($path, $delimiter);
37
38 22
        while (count($index) > 1) {
39 21
            $key = \array_shift($index);
40
41 21
            if (!\is_array($subject) && !\is_object($subject)) {
42 5
                $msg = "Unable to set '$path': '%s' is of type " . \gettype($subject);
43 5
                throw ResolveException::create($msg, $path, $delimiter, $index, $key);
44
            }
45
46
            try {
47 21
                $subject =& Helpers::descend($subject, $key, $exists);
48 1
            } catch (\Error $error) {
49 1
                $msg = "Unable to set '$path': error at '%s'";
50 1
                throw ResolveException::create($msg, $path, $delimiter, $index, null, $error);
51
            }
52
53 20
            if (!$exists) {
54 5
                throw ResolveException::create("Unable to set '$path': '%s' doesn't exist", $path, $delimiter, $index);
55
            }
56
        }
57
58 11
        if (\is_array($subject) || $subject instanceof \ArrayAccess) {
59 8
            $subject[$index[0]] = $value;
60
        } else {
61
            try {
62 4
                $subject->{$index[0]} = $value;
63 2
            } catch (\Error $error) {
64 2
                throw new ResolveException("Unable to set '$path': error at '$path'", 0, $error);
65
            }
66
        }
67
68 9
        return $result;
69
    }
70
71
72
    /**
73
     * Set a value, creating a structure if needed.
74
     *
75
     * @param object|array<string,mixed> $subject
76
     * @param string                     $path
77
     * @param mixed                      $value
78
     * @param string                     $delimiter
79
     * @param bool                       $assoc     Create new structure as array. Omit to base upon subject type.
80
     * @return array|object
81
     * @throws ResolveException
82
     *
83
     * @template TSubject
84
     * @phpstan-param TSubject&(object|array<string,mixed>) $subject
85
     * @phpstan-param string                                $path
86
     * @phpstan-param mixed                                 $value
87
     * @phpstan-param string                                $delimiter
88
     * @phpstan-param bool                                  $assoc
89
     * @phpstan-return TSubject&(object|array<string,mixed>)
90
     */
91 30
    public static function put($subject, string $path, $value, string $delimiter = '.', bool $assoc = false)
92
    {
93 30
        $result =& $subject;
94
95 30
        $index = Helpers::splitPath($path, $delimiter);
96
97 29
        while (count($index) > 1) {
98 28
            $key = \array_shift($index);
99
100
            try {
101 28
                $subject =& Helpers::descend($subject, $key, $exists);
102 1
            } catch (\Error $error) {
103 1
                $msg = "Unable to put '$path': error at '%s'";
104 1
                throw ResolveException::create($msg, $path, $delimiter, $index, null, $error);
105
            }
106
107 27
            if (!$exists) {
108 13
                array_unshift($index, $key);
109 13
                break;
110
            }
111
112 27
            if (!\is_array($subject) && !\is_object($subject)) {
113 4
                break;
114
            }
115
        }
116
117
        try {
118 28
            self::setValueCreate($subject, $index, $value, $assoc);
119 2
        } catch (\Error $error) {
120 2
            $msg = "Unable to put '$path': error at '%s'";
121 2
            throw ResolveException::create($msg, $path, $delimiter, array_slice($index, 0, -1), null, $error);
122
        }
123
124 26
        return $result;
125
    }
126
127
    /**
128
     * Create property and set the value.
129
     *
130
     * @param mixed     $subject
131
     * @param string[]  $index    Part or the path that doesn't exist
132
     * @param mixed     $value
133
     * @param bool      $assoc
134
     */
135 28
    protected static function setValueCreate(&$subject, array $index, $value, bool $assoc): void
136
    {
137 28
        if (is_array($subject) || $subject instanceof \ArrayAccess) {
138 15
            $key = \array_shift($index);
139 15
            $subject[$key] = null;
140 15
            $subject =& $subject[$key];
141 14
        } elseif (is_object($subject)) {
142 10
            $key = \array_shift($index);
143 10
            $subject->{$key} = null;
144 8
            $subject =& $subject->{$key};
145
        }
146
147 26
        while ($index !== []) {
148 17
            $key = \array_pop($index);
149 17
            $value = $assoc ? [$key => $value] : (object)[$key => $value];
150
        }
151
152 26
        $subject = $value;
153 26
    }
154
}
155