Completed
Push — master ( 92313d...544a4c )
by Bernhard
03:43
created

MigratingConverter::migrate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.9765

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 11
ccs 3
cts 8
cp 0.375
rs 9.4285
cc 2
eloc 8
nc 2
nop 2
crap 2.9765
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\ConversionFailedException;
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 "targetVersion" option:
63
 *
64
 * ~~~php
65
 * $jsonEncoder->encode($converter->toJson($configFile, array(
66
 *     'targetVersion' => '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 10
    public function __construct(JsonConverter $innerConverter, $currentVersion, MigrationManager $migrationManager)
105
    {
106 10
        $this->innerConverter = $innerConverter;
107 10
        $this->migrationManager = $migrationManager;
108 10
        $this->currentVersion = $currentVersion;
109 10
        $this->knownVersions = $this->migrationManager->getKnownVersions();
110
111 10
        if (!in_array($currentVersion, $this->knownVersions, true)) {
112
            $this->knownVersions[] = $currentVersion;
113
            usort($this->knownVersions, 'version_compare');
114
        }
115 10
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120 5
    public function toJson($data, array $options = array())
121
    {
122 5
        $targetVersion = isset($options['targetVersion'])
123 4
            ? $options['targetVersion']
124 5
            : $this->currentVersion;
125
126 5
        $this->assertVersionSupported($targetVersion);
127
128 4
        $jsonData = $this->innerConverter->toJson($data, $options);
129
130 4
        $this->assertObject($jsonData);
131
132 3
        $jsonData->version = $this->currentVersion;
133
134 3
        if ($jsonData->version !== $targetVersion) {
135 1
            $this->migrate($jsonData, $targetVersion);
136 1
            $jsonData->version = $targetVersion;
137
        }
138
139 3
        return $jsonData;
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145 5
    public function fromJson($jsonData, array $options = array())
146
    {
147 5
        $this->assertObject($jsonData);
148 4
        $this->assertVersionIsset($jsonData);
149 3
        $this->assertVersionSupported($jsonData->version);
150
151 2
        if ($jsonData->version !== $this->currentVersion) {
152 1
            $this->migrationManager->migrate($jsonData, $this->currentVersion);
153
        }
154
155 2
        return $this->innerConverter->fromJson($jsonData, $options);
156
    }
157
158 1
    private function migrate(stdClass $jsonData, $targetVersion)
159
    {
160
        try {
161 1
            $this->migrationManager->migrate($jsonData, $targetVersion);
162
        } catch (MigrationFailedException $e) {
163
            throw new ConversionFailedException(sprintf(
164
                'Could not migrate the JSON data: %s',
165
                $e->getMessage()
166
            ), 0, $e);
167
        }
168 1
    }
169
170 8
    private function assertVersionSupported($version)
171
    {
172 8
        if (!in_array($version, $this->knownVersions, true)) {
173 2
            throw UnsupportedVersionException::forVersion($version, $this->knownVersions);
174
        }
175 6
    }
176
177 9
    private function assertObject($jsonData)
178
    {
179 9
        if (!$jsonData instanceof stdClass) {
180 2
            throw new ConversionFailedException(sprintf(
181 2
                'Expected an instance of stdClass, got: %s',
182 2
                is_object($jsonData) ? get_class($jsonData) : gettype($jsonData)
183
            ));
184
        }
185 7
    }
186
187 4
    private function assertVersionIsset(stdClass $jsonData)
188
    {
189 4
        if (!isset($jsonData->version)) {
190 1
            throw new ConversionFailedException('Could not find a "version" property.');
191
        }
192 3
    }
193
}
194