JsonSerializer::serialize()   B
last analyzed

Complexity

Conditions 5
Paths 8

Size

Total Lines 37
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 37
rs 8.439
cc 5
eloc 24
nc 8
nop 1
1
<?php
2
/**
3
 * NextFlow (http://github.com/nextflow)
4
 *
5
 * @link http://github.com/nextflow/nextflow-php for the canonical source repository
6
 * @copyright Copyright (c) 2014-2016 NextFlow (http://github.com/nextflow)
7
 * @license https://raw.github.com/nextflow/nextflow-php/master/LICENSE MIT
8
 */
9
10
namespace NextFlow\Core\Serializer;
11
12
use NextFlow\Core\Scene\SceneInterface;
13
14
/**
15
 * A serializer that serializes or unserializes a scene to JSON.
16
 */
17
final class JsonSerializer implements SerializerInterface
18
{
19
    /**
20
     * A map with id's that are serialized so we don't serialize a node twice.
21
     *
22
     * @var array
23
     */
24
    private $cachedIds;
25
26
    /**
27
     * A map with nodes that are serialized so we don't serialize a node twice.
28
     *
29
     * @var array
30
     */
31
    private $cachedNodes;
32
33
    /**
34
     * Initializes a new instance of this class.
35
     */
36
    public function __construct()
37
    {
38
        $this->resetCache();
39
    }
40
41
    /**
42
     * Serializes the given scene.
43
     *
44
     * @param $scene The scene to serialize.
45
     * @return string
46
     */
47
    public function serialize(SceneInterface $scene)
48
    {
49
        $this->resetCache();
50
        $this->cacheNodes($scene->getEvents());
51
52
        $data = array();
53
        $data['type'] = get_class($scene);
54
        $data['name'] = $scene->getName();
55
        $data['nodes'] = new \stdClass();
56
57
        $nodeIndex = 0;
58
        foreach ($this->cachedNodes as $id => $node) {
59
            $nodeSockets = new \stdClass();
60
            foreach ($node->getSockets() as $socketName => $socket) {
61
                $nodes = array();
62
                foreach ($socket->getNodes() as $connectedNode) {
63
                    $nodes[] = $this->cachedIds[$connectedNode->getId()];
64
                }
65
                $nodeSockets->$socketName = $nodes;
66
            }
67
68
            $data['nodes']->$nodeIndex = array(
69
                'type' => get_class($node),
70
                'parameters' => $node->getParams(),
71
                'sockets' => $nodeSockets,
72
            );
73
            $nodeIndex++;
74
        }
75
76
77
        $data['events'] = array();
78
        foreach ($scene->getEvents() as $event) {
79
            $data['events'][] = $this->cachedIds[$event->getId()];
80
        }
81
82
        return json_encode($data);
83
    }
84
85
    /**
86
     * Unserializes the given data.
87
     *
88
     * @param string $data The data to unserialize.
89
     * @return SceneInterface
90
     */
91
    public function unserialize($data)
92
    {
93
        $this->resetCache();
94
        $json = json_decode($data);
95
96
        $scene = null;
97
98
        if (isset($json->type)) {
99
            $sceneClass = $json->type;
100
            if (!class_exists($sceneClass)) {
101
                throw new \RuntimeException('Invalid scene final class unserialized: ' . $sceneClass);
102
            }
103
104
            $scene = new $sceneClass();
105
            if (!$scene instanceof SceneInterface) {
106
                throw new \RuntimeException(sprintf('The class %s should implement SceneInterface.', $sceneClass));
107
            }
108
            $scene->setName($json->name);
109
110
            foreach ($json->nodes as $nodeJson) {
111
                $this->unserializeNode($nodeJson);
112
            }
113
114
            foreach ($json->nodes as $nodeId => $nodeJson) {
115
                $this->unserializeConnections($nodeId, (array)$nodeJson->sockets);
116
            }
117
118
            foreach ($json->events as $eventId) {
119
                $event = $this->cachedNodes[$eventId];
120
121
                $scene->addEvent($event);
122
            }
123
        }
124
125
        $this->resetCache();
126
127
        return $scene;
128
    }
129
130
    /**
131
     * Unserializes a node.
132
     *
133
     * @param array $json The json value to unserialize.
134
     */
135
    private function unserializeNode($json)
136
    {
137
        $type = $json->type;
138
139
        if ($type === 'NextFlow\\Core\\Event\\NamedEvent') {
140
            $instance = new $type(null);
141
        } else {
142
            $instance = new $type();
143
        }
144
145
        foreach ($json->parameters as $name => $value) {
146
            $instance->setParam($name, $value);
147
        }
148
149
        $this->cachedNodes[] = $instance;
150
    }
151
152
    /**
153
     * Unserializes the connections of the node with the given id.
154
     *
155
     * @param string $id The id of the node.
156
     * @param array $sockets The sockets to unserialize.
157
     */
158
    private function unserializeConnections($id, array $sockets)
159
    {
160
        foreach ($sockets as $socket => $nodes) {
161
            foreach ($nodes as $nodeId) {
162
                $connectedNode = $this->cachedNodes[$nodeId];
163
164
                $this->cachedNodes[$id]->bind($socket, $connectedNode);
165
            }
166
        }
167
    }
168
169
    /**
170
     * Resets the internal cache.
171
     */
172
    private function resetCache()
173
    {
174
        $this->cachedIds = array();
175
        $this->cachedNodes = array();
176
    }
177
178
    /**
179
     * Caches the given list with nodes.
180
     *
181
     * @param array $nodes A list with nodes to cache.
182
     */
183
    private function cacheNodes($nodes)
184
    {
185
        foreach ($nodes as $node) {
186
            $this->cachedIds[$node->getId()] = count($this->cachedIds);
187
            $this->cachedNodes[] = $node;
188
189
            foreach ($node->getSockets() as $socket) {
190
                $this->cacheNodes($socket->getNodes());
191
            }
192
        }
193
    }
194
}
195