Passed
Pull Request — master (#451)
by Kirill
10:12 queued 03:27
created

SourceMap::__serialize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 5
rs 10
c 1
b 1
f 0
1
<?php
2
3
/**
4
 * Spiral Framework.
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
declare(strict_types=1);
11
12
namespace Spiral\Stempler\Compiler;
13
14
use Spiral\Stempler\Loader\LoaderInterface;
15
use Spiral\Stempler\Loader\Source;
16
17
/**
18
 * Stores and resolves offsets and line numbers between templates.
19
 */
20
final class SourceMap
21
{
22
    /** @var array */
23
    private $paths = [];
24
25
    /** @var array */
26
    private $lines = [];
27
28
    /** @var Source[] */
29
    private $sourceCache = null;
30
31
    /**
32
     * @return array
33
     */
34
    public function __serialize(): array
35
    {
36
        return [
37
            'paths' => $this->paths,
38
            'lines' => $this->lines,
39
        ];
40
    }
41
42
    /**
43
     * @param array $data
44
     */
45
    public function __unserialize(array $data): void
46
    {
47
        $this->paths = $data['paths'];
48
        $this->lines = $data['lines'];
49
    }
50
51
    /**
52
     * Get all template paths involved in final template.
53
     *
54
     * @return array
55
     */
56
    public function getPaths(): array
57
    {
58
        $paths = [];
59
60
        // We can scan top level only
61
62
        /** @var Location $loc */
63
        foreach ($this->lines as $line) {
64
            if (!in_array($this->paths[$line[0]], $paths, true)) {
65
                $paths[] = $this->paths[$line[0]];
66
            }
67
        }
68
69
        return $paths;
70
    }
71
72
    /**
73
     * Calculate the location of all closest nodes based on a line number in generated source. Recursive until top root
74
     * template.
75
     *
76
     * @param int $line
77
     * @return array
78
     */
79
    public function getStack(int $line): array
80
    {
81
        $found = null;
82
        foreach ($this->lines as $linen => $ctx) {
83
            if ($linen <= $line) {
84
                $found = $ctx;
85
            }
86
        }
87
88
        if ($found === null) {
89
            return [];
90
        }
91
92
        $result = [];
93
        $this->unpack($result, $found);
94
95
        return $result;
96
    }
97
98
    /**
99
     * Compress.
100
     *
101
     * @return false|string
102
     */
103
    public function serialize()
104
    {
105
        return json_encode($this->__serialize());
106
    }
107
108
    /**
109
     * @param string $serialized
110
     */
111
    public function unserialize($serialized): void
112
    {
113
        $this->__unserialize(json_decode($serialized, true));
114
    }
115
116
    /**
117
     * @param string          $content
118
     * @param array           $locations
119
     * @param LoaderInterface $loader
120
     * @return SourceMap
121
     */
122
    public static function calculate(string $content, array $locations, LoaderInterface $loader): SourceMap
123
    {
124
        $map = new self();
125
126
        foreach ($locations as $offset => $location) {
127
            $line = Source::resolveLine($content, $offset);
128
            if (!isset($map->lines[$line])) {
129
                $map->lines[$line] = $map->calculateLine($location, $loader);
130
            }
131
        }
132
133
        $map->sourceCache = null;
134
135
        return $map;
136
    }
137
138
    /**
139
     * @param array $result
140
     * @param array $line
141
     */
142
    private function unpack(array &$result, array $line): void
143
    {
144
        $result[] = [
145
            'file' => $this->paths[$line[0]],
146
            'line' => $line[1],
147
        ];
148
149
        if ($line[2] !== null) {
150
            $this->unpack($result, $line[2]);
151
        }
152
    }
153
154
    /**
155
     * @param Location        $location
156
     * @param LoaderInterface $loader
157
     * @return array
158
     */
159
    private function calculateLine(Location $location, LoaderInterface $loader): array
160
    {
161
        if (!isset($this->sourceCache[$location->path])) {
162
            $this->sourceCache[$location->path] = $loader->load($location->path);
163
        }
164
        $path = $this->sourceCache[$location->path]->getFilename();
165
166
        if (!in_array($path, $this->paths, true)) {
167
            $this->paths[] = $path;
168
        }
169
170
        return [
171
            array_search($path, $this->paths),
172
            Source::resolveLine($this->sourceCache[$location->path]->getContent(), $location->offset),
173
            $location->parent === null ? null : $this->calculateLine($location->parent, $loader),
174
        ];
175
    }
176
}
177