Completed
Push — master ( 4fa802...7f958e )
by Bernhard
08:14
created

MigratingConverter::assertVersionSupported()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

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