Passed
Pull Request — master (#13)
by mon
02:39
created

UpdateCommand::compareMaps()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 9.552
c 0
b 0
f 0
cc 3
nc 3
nop 3
1
<?php
2
3
namespace FileEye\MimeMap\Command;
4
5
use SebastianBergmann\Comparator\ComparisonFailure;
6
use SebastianBergmann\Comparator\Factory;
7
use SebastianBergmann\Diff\Differ;
8
use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder;
9
use Symfony\Component\Console\Command\Command;
10
use Symfony\Component\Console\Input\InputOption;
11
use Symfony\Component\Console\Input\InputInterface;
12
use Symfony\Component\Console\Output\OutputInterface;
13
use Symfony\Component\Yaml\Yaml;
14
use FileEye\MimeMap\Map\AbstractMap;
15
use FileEye\MimeMap\MapHandler;
16
use FileEye\MimeMap\MapUpdater;
17
18
/**
19
 * A Symfony application command to update the MIME type to extension map.
20
 */
21
class UpdateCommand extends Command
22
{
23
    /**
24
     * {@inheritdoc}
25
     */
26
    protected function configure()
27
    {
28
        $this
29
            ->setName('update')
30
            ->setDescription('Updates the MIME-type-to-extension map. Reads the source file specified by --source, applies any overrides specified in the file at --override, then writes the map to the PHP file where the PHP --class is defined.')
31
            ->addOption(
32
                'source',
33
                null,
34
                InputOption::VALUE_REQUIRED,
35
                'URL or filename of the source map',
36
                MapUpdater::DEFAULT_SOURCE_FILE
37
            )
38
            ->addOption(
39
                'override',
40
                null,
41
                InputOption::VALUE_REQUIRED,
42
                'URL or filename of the override commands to execute',
43
                MapUpdater::getDefaultOverrideFile()
44
            )
45
            ->addOption(
46
                'class',
47
                null,
48
                InputOption::VALUE_REQUIRED,
49
                'The Fully Qualified Class Name of the PHP class storing the map',
50
                MapHandler::DEFAULT_MAP_CLASS
51
            )
52
        ;
53
    }
54
55
    /**
56
     * {@inheritdoc}
57
     */
58
    protected function execute(InputInterface $input, OutputInterface $output)
59
    {
60
        MapHandler::setDefaultMapClass($input->getOption('class'));
61
        $current_map = MapHandler::map();
62
        $updater = new MapUpdater();
63
64
        // Loads the map from the source file.
65
        try {
66
            $new_map = $updater->createMapFromSourceFile($input->getOption('source'));
67
        } catch (\RuntimeException $e) {
68
            $output->writeln('<error>' . $e->getMessage() . '</error>');
69
            exit(2);
70
        }
71
72
73
74
75
76
77
78
        $xml = simplexml_load_string(file_get_contents('https://raw.github.com/minad/mimemagic/master/script/freedesktop.org.xml'));
79
        foreach ($xml as $node) {
80
            $exts = [];
81
            foreach ($node->glob as $glob) {
82
                $pattern = (string) $glob['pattern'];
83
                if ('*' != $pattern[0] || '.' != $pattern[1]) {
84
                    continue;
85
                }
86
                $exts[] = substr($pattern, 2);
87
            }
88
            if (!$exts) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $exts of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
89
                continue;
90
            }
91
            $mt = (string) $node['type'];
92
            foreach ($exts as $ext) {
93
                $new_map->addMapping($mt, $ext);
94
            }
95
            //$new[$mt] = $exts;
96
/*            foreach ($node->alias as $alias) {
97
                $mt = strtolower((string) $alias['type']);
98
                $new[$mt] = array_merge($new[$mt] ?? [], $exts);
99
            }*/
100
        }
101
//dump($new);
102
103
104
105
106
107
108
109
110
111
112
113
114
        // Applies the overrides.
115
        try {
116
            $content = file_get_contents($input->getOption('override'));
117
            $updater->applyOverrides($new_map, Yaml::parse($content));
118
        } catch (\Exception $e) {
119
            $output->writeln('<error>' . $e->getMessage() . '</error>');
120
            exit(2);
121
        }
122
123
        // Check if anything got changed.
124
        $write = false;
125
        try {
126
            $this->compareMaps($current_map, $new_map, 'types');
127
        } catch (\RuntimeException $e) {
128
            $output->writeln('<comment>Changes to MIME types mapping</comment>');
129
            $output->writeln($e->getMessage());
130
            $write = true;
131
        }
132
        try {
133
            $this->compareMaps($current_map, $new_map, 'extensions');
134
        } catch (\RuntimeException $e) {
135
            $output->writeln('<comment>Changes to extensions mapping</comment>');
136
            $output->writeln($e->getMessage());
137
            $write = true;
138
        }
139
140
        // If changed, save the new map to the PHP file.
141
        if ($write) {
142
            $updater->writeMapToPhpClassFile($new_map, $current_map->getFileName());
143
            $output->writeln('<comment>Code updated.</comment>');
144
        } else {
145
            $output->writeln('<info>No changes to mapping.</info>');
146
        }
147
148
        // Reset the new map's map array.
149
        $new_map->reset();
150
    }
151
152
    /**
153
     * Compares two type-to-extension maps by section.
154
     *
155
     * @param AbstractMap $old_map
156
     *   The first map to compare.
157
     * @param AbstractMap $new_map
158
     *   The second map to compare.
159
     * @param string $section
160
     *   The first-level array key to compare: 'types' or 'extensions'.
161
     *
162
     * @throws \RuntimeException with diff details if the maps differ.
163
     *
164
     * @return bool
165
     *   True if the maps are equal.
166
     */
167
    protected function compareMaps(AbstractMap $old_map, AbstractMap $new_map, $section)
168
    {
169
        $old_map->sort();
170
        $new_map->sort();
171
        $old = $old_map->getMapArray();
172
        $new = $new_map->getMapArray();
173
174
        $factory = new Factory;
175
        $comparator = $factory->getComparatorFor($old[$section], $new[$section]);
176
        try {
177
            $comparator->assertEquals($old[$section], $new[$section]);
178
            return true;
179
        } catch (ComparisonFailure $failure) {
180
            $old_string = var_export($old[$section], true);
181
            $new_string = var_export($new[$section], true);
182
            if (class_exists('\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder')) {
183
                $differ = new Differ(new UnifiedDiffOutputBuilder("--- Removed\n+++ Added\n"));
184
                throw new \RuntimeException($differ->diff($old_string, $new_string));
185
            } else {
186
                throw new \RuntimeException(' ');
187
            }
188
        }
189
    }
190
}
191