Completed
Push — master ( 7f958e...5fff41 )
by Bernhard
05:20
created

MigratingConverter::assertObject()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
ccs 6
cts 6
cp 1
rs 9.6666
cc 3
eloc 5
nc 2
nop 1
crap 3
1
<?php
2
3
/*
4
 * This file is part of the webmozart/json package.
5
 *
6
 * (c) Bernhard Schussek <[email protected]>
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 Webmozart\Json\Migration;
13
14
use stdClass;
15
use Webmozart\Assert\Assert;
16
use Webmozart\Json\Conversion\ConversionException;
17
use Webmozart\Json\Conversion\JsonConverter;
18
19
/**
20
 * A decorator for a {@link JsonCoverter} that migrates JSON objects.
21
 *
22
 * This decorator supports JSON objects in different versions. The decorated
23
 * converter can be written for a specific version. Any other version can be
24
 * supported by supplying a {@link MigrationManager} that is able to migrate
25
 * a JSON object in that version to the version required by the decorated
26
 * converter.
27
 *
28
 * You need to pass the decorated converter and the migration manager to the
29
 * constructor:
30
 *
31
 * ~~~php
32
 * // Written for version 3.0
33
 * $converter = ConfigFileConverter();
34
 *
35
 * // Support older versions of the file
36
 * $migrationManager = new MigrationManager(array(
37
 *     new ConfigFile10To20Migration(),
38
 *     new ConfigFile20To30Migration(),
39
 * ));
40
 *
41
 * // Decorate the converter
42
 * $converter = new MigratingConverter($converter, '3.0', $migrationManager);
43
 * ~~~
44
 *
45
 * You can load JSON data in any version with the method {@link fromJson()}. If
46
 * the "version" property of the JSON object is different than the version
47
 * supported by the decorated converter, the JSON object is migrated to the
48
 * required version.
49
 *
50
 * ~~~php
51
 * $jsonDecoder = new JsonDecoder();
52
 * $configFile = $converter->fromJson($jsonDecoder->decode($json));
53
 * ~~~
54
 *
55
 * You can also dump data as JSON object with {@link toJson()}:
56
 *
57
 * ~~~php
58
 * $jsonEncoder = new JsonEncoder();
59
 * $jsonEncoder->encode($converter->toJson($configFile));
60
 * ~~~
61
 *
62
 * By default, data is dumped in the current version. If you want to dump the
63
 * data in a specific version, pass the "target_version" option:
64
 *
65
 * ~~~php
66
 * $jsonEncoder->encode($converter->toJson($configFile, array(
67
 *     'target_version' => '2.0',
68
 * )));
69
 * ~~~
70
 *
71
 * @since  1.3
72
 *
73
 * @author Bernhard Schussek <[email protected]>
74
 */
75
class MigratingConverter implements JsonConverter
76
{
77
    /**
78
     * @var JsonConverter
79
     */
80
    private $innerConverter;
81
82
    /**
83
     * @var MigrationManager
84
     */
85
    private $migrationManager;
86
87
    /**
88
     * @var string
89
     */
90
    private $currentVersion;
91
92
    /**
93
     * @var string[]
94
     */
95
    private $knownVersions;
96
97
    /**
98
     * Creates the converter.
99
     *
100
     * @param JsonConverter    $innerConverter   The decorated converter.
101
     * @param string           $currentVersion   The version that the decorated
102
     *                                           converter is compatible with.
103
     * @param MigrationManager $migrationManager The manager for migrating JSON data.
104
     */
105 10
    public function __construct(JsonConverter $innerConverter, $currentVersion, MigrationManager $migrationManager)
106
    {
107 10
        $this->innerConverter = $innerConverter;
108 10
        $this->migrationManager = $migrationManager;
109 10
        $this->currentVersion = $currentVersion;
110 10
        $this->knownVersions = $this->migrationManager->getKnownVersions();
111
112 10
        if (!in_array($currentVersion, $this->knownVersions, true)) {
113
            $this->knownVersions[] = $currentVersion;
114
            usort($this->knownVersions, 'version_compare');
115
        }
116 10
    }
117
118
    /**
119
     * @param mixed $data
120
     * @param array     $options
121
     *
122
     * @return stdClass
123
     *
124
     * @throws ConversionException
125
     * @throws UnsupportedVersionException
126
     */
127 5
    public function toJson($data, array $options = array())
128
    {
129 5
        $targetVersion = isset($options['target_version'])
130 4
            ? $options['target_version']
131 5
            : $this->currentVersion;
132
133 5
        $this->assertVersionSupported($targetVersion);
134
135 4
        $jsonData = $this->innerConverter->toJson($data, $options);
136
137 4
        $this->assertObject($jsonData);
138
139 3
        $jsonData->version = $this->currentVersion;
140
141 3
        if ($jsonData->version !== $targetVersion) {
142 1
            $this->migrate($jsonData, $targetVersion);
143 1
            $jsonData->version = $targetVersion;
144
        }
145
146 3
        return $jsonData;
147
    }
148
149
    /**
150
     * @param stdClass $jsonData
151
     * @param array $options
152
     *
153
     * @return mixed
154
     *
155
     * @throws ConversionException
156
     * @throws UnsupportedVersionException
157
     */
158 5
    public function fromJson($jsonData, array $options = array())
159
    {
160 5
        $this->assertObject($jsonData);
161 4
        $this->assertVersionIsset($jsonData);
162 3
        $this->assertVersionSupported($jsonData->version);
163
164 2
        if ($jsonData->version !== $this->currentVersion) {
165 1
            $this->migrationManager->migrate($jsonData, $this->currentVersion);
166
        }
167
168 2
        return $this->innerConverter->fromJson($jsonData, $options);
169
    }
170
171 1
    private function migrate(stdClass $jsonData, $targetVersion)
172
    {
173
        try {
174 1
            $this->migrationManager->migrate($jsonData, $targetVersion);
175
        } catch (MigrationException $e) {
176
            throw new ConversionException();
177
        }
178 1
    }
179
180 8
    private function assertVersionSupported($version)
181
    {
182 8
        if (!in_array($version, $this->knownVersions, true)) {
183 2
            throw UnsupportedVersionException::forVersion($version, $this->knownVersions);
184
        }
185 6
    }
186
187 9
    private function assertObject($jsonData)
188
    {
189 9
        if (!$jsonData instanceof stdClass) {
190 2
            throw new ConversionException(sprintf(
191 2
                'Expected an instance of stdClass, got: %s',
192 2
                is_object($jsonData) ? get_class($jsonData) : gettype($jsonData)
193
            ));
194
        }
195 7
    }
196
197 4
    private function assertVersionIsset(stdClass $jsonData)
198
    {
199 4
        if (!isset($jsonData->version)) {
200 1
            throw new ConversionException('Could not find a "version" property.');
201
        }
202 3
    }
203
}
204