Load::getName()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
/**
4
 * This file is part of Cecil.
5
 *
6
 * (c) Arnaud Ligny <[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
declare(strict_types=1);
13
14
namespace Cecil\Step\Data;
15
16
use Cecil\Collection\Page\PrefixSuffix;
17
use Cecil\Step\AbstractStep;
18
use Cecil\Util;
19
use Symfony\Component\Finder\Finder;
20
use Symfony\Component\Serializer\Encoder\CsvEncoder;
21
use Symfony\Component\Serializer\Encoder\JsonEncoder;
22
use Symfony\Component\Serializer\Encoder\XmlEncoder;
23
use Symfony\Component\Serializer\Encoder\YamlEncoder;
24
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
25
use Symfony\Component\Serializer\Serializer;
26
27
/**
28
 * Load step class.
29
 *
30
 * This class is responsible for loading data files from a specified directory,
31
 * decoding their contents based on the file extension, and merging the data
32
 * into the builder's data collection. It supports various file formats such as
33
 * YAML, JSON, CSV, and XML. The loaded data is organized into a nested array
34
 * structure based on the file paths and language suffixes.
35
 */
36
class Load extends AbstractStep
37
{
38
    /**
39
     * {@inheritdoc}
40
     */
41
    public function getName(): string
42
    {
43
        return 'Loading data';
44
    }
45
46
    /**
47
     * {@inheritdoc}
48
     */
49
    public function init(array $options): void
50
    {
51
        if (is_dir($this->config->getDataPath()) && $this->config->isEnabled('data.load')) {
52
            $this->canProcess = true;
53
        }
54
    }
55
56
    /**
57
     * {@inheritdoc}
58
     */
59
    public function process(): void
60
    {
61
        $files = Finder::create()
62
            ->files()
63
            ->in($this->config->getDataPath())
64
            ->name('/\.(' . implode('|', (array) $this->config->get('data.ext')) . ')$/')
65
            ->sortByName(true);
66
67
        if ($this->config->hasTheme()) {
68
            $themes = $this->config->getTheme();
69
            foreach ($themes ?? [] as $theme) {
70
                if (Util\File::getFS()->exists($this->config->getThemeDirPath($theme, 'data'))) {
71
                    $files->in($this->config->getThemeDirPath($theme, 'data'));
72
                }
73
            }
74
        }
75
76
        $total = \count($files);
77
78
        if ($total < 1) {
79
            $message = 'No files';
80
            $this->builder->getLogger()->info($message);
81
82
            return;
83
        }
84
85
        $serializerYaml = new Serializer([new ObjectNormalizer()], [new YamlEncoder()]);
86
        $serializerJson = new Serializer([new ObjectNormalizer()], [new JsonEncoder()]);
87
        $serializerCsv = new Serializer([new ObjectNormalizer()], [new CsvEncoder()]);
88
        $serializerXml = new Serializer([new ObjectNormalizer()], [new XmlEncoder()]);
89
        $count = 0;
90
91
        /** @var \Symfony\Component\Finder\SplFileInfo $file */
92
        foreach ($files as $file) {
93
            $count++;
94
            set_error_handler(
95
                function ($severity, $message, $file, $line) {
96
                    throw new \ErrorException($message, 0, $severity, $file, $line, null);
97
                }
98
            );
99
            $data = $file->getContents();
100
            restore_error_handler();
101
102
            switch ($file->getExtension()) {
103
                case 'yml':
104
                case 'yaml':
105
                    $dataAsArray = $serializerYaml->decode($data, 'yaml');
106
                    break;
107
                case 'json':
108
                    $dataAsArray = $serializerJson->decode($data, 'json');
109
                    break;
110
                case 'csv':
111
                    $dataAsArray = $serializerCsv->decode($data, 'csv');
112
                    break;
113
                case 'xml':
114
                    $dataAsArray = $serializerXml->decode($data, 'xml');
115
                    break;
116
                default:
117
                    return;
118
            }
119
120
            $lang = $this->config->getLanguageDefault();
121
            if (PrefixSuffix::hasSuffix($file->getFilenameWithoutExtension())) {
122
                $lang = PrefixSuffix::getSuffix($file->getFilenameWithoutExtension());
123
            }
124
            $array = [];
125
            $path = Util::joinFile((string) pathinfo($file->getRelativePathname(), \PATHINFO_DIRNAME), (string) pathinfo($file->getRelativePathname(), \PATHINFO_FILENAME));
126
            $path = trim($path, './');
127
            $localizedPath = Util::joinFile((string) $lang, PrefixSuffix::sub($path));
128
            $this->pathToArray($array, $localizedPath, $dataAsArray);
129
130
            $dataAsArray = array_merge_recursive(
131
                $this->builder->getData(),
132
                $array
133
            );
134
            $this->builder->setData($dataAsArray);
135
136
            $message = \sprintf('File "%s" loaded', $file->getRelativePathname());
137
            $this->builder->getLogger()->info($message, ['progress' => [$count, $total]]);
138
        }
139
    }
140
141
    /**
142
     * Puts a path/value couple into an array.
143
     *
144
     * @param array  $arr       Target array
145
     * @param string $path      Source path
146
     * @param array  $value     Source values
147
     * @param string $separator Path separator (ie: '/')
148
     */
149
    private function pathToArray(array &$arr, string $path, array $value, string $separator = DIRECTORY_SEPARATOR): void
150
    {
151
        $keys = explode($separator, $path);
152
        foreach ($keys as $key) {
153
            $arr = &$arr[$key];
154
        }
155
        $arr = $value;
156
    }
157
}
158