Completed
Push — master ( e54d7a...9456c1 )
by Sullivan
02:56
created

src/ConfigBridge.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace SLLH\StyleCIBridge;
4
5
use Composer\Semver\Semver;
6
use SLLH\StyleCIBridge\Exception\LevelConfigException;
7
use SLLH\StyleCIBridge\StyleCI\Configuration;
8
use SLLH\StyleCIFixers\Fixers;
9
use Symfony\Component\Config\Definition\Processor;
10
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
11
use Symfony\Component\Console\Output\ConsoleOutput;
12
use Symfony\Component\Console\Output\OutputInterface;
13
use Symfony\Component\DependencyInjection\Container;
14
use Symfony\Component\Finder\Finder;
15
use Symfony\Component\Yaml\Yaml;
16
use Symfony\CS\Config\Config;
17
use Symfony\CS\Finder\DefaultFinder;
18
use Symfony\CS\Fixer;
19
use Symfony\CS\Fixer\Contrib\HeaderCommentFixer;
20
use Symfony\CS\FixerFactory;
21
use Symfony\CS\FixerInterface;
22
23
/**
24
 * @author Sullivan Senechal <[email protected]>
25
 */
26
final class ConfigBridge
27
{
28
    const CS_FIXER_MIN_VERSION = '1.6.1';
29
30
    /**
31
     * @var OutputInterface
32
     */
33
    private $output;
34
35
    /**
36
     * @var FixerFactory
37
     */
38
    private $fixerFactory = null;
39
40
    /**
41
     * @var string
42
     */
43
    private $styleCIConfigDir;
44
45
    /**
46
     * @var array|null
47
     */
48
    private $styleCIConfig = null;
49
50
    /**
51
     * @var string|array
52
     */
53
    private $finderDirs;
54
55
    /**
56
     * @param string|null       $styleCIConfigDir StyleCI config directory. Called script dir as default.
57
     * @param string|array|null $finderDirs       A directory path or an array of directories for Finder. Called script dir as default.
58
     */
59
    public function __construct($styleCIConfigDir = null, $finderDirs = null)
60
    {
61
        if (!Semver::satisfies(Fixer::VERSION, sprintf('>=%s', self::CS_FIXER_MIN_VERSION))) {
62
            throw new \RuntimeException(sprintf(
63
                'PHP-CS-Fixer v%s is not supported, please upgrade to v%s or higher.',
64
                Fixer::VERSION,
65
                self::CS_FIXER_MIN_VERSION
66
            ));
67
        }
68
69
        // Guess config files path if not specified.
70
        // getcwd function is not enough. See: https://github.com/Soullivaneuh/php-cs-fixer-styleci-bridge/issues/46
71
        if (null === $styleCIConfigDir || null === $finderDirs) {
72
            $dbt = version_compare(PHP_VERSION, '5.4.0', '>=') ? debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2) : debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
73
74
            // Static call
75
            if (isset($dbt[1]['class']) && 'SLLH\StyleCIBridge\ConfigBridge' === $dbt[1]['class'] && 'create' === $dbt[1]['function']) {
76
                $configsPath = dirname($dbt[1]['file']);
77
            } elseif (isset($dbt[0]['class']) && 'SLLH\StyleCIBridge\ConfigBridge' === $dbt[0]['class'] && '__construct' === $dbt[0]['function']) { // Manual instance
78
                $configsPath = dirname($dbt[0]['file']);
79
            } else { // If no case found, fallback to not reliable getcwd method.
80
                $configsPath = getcwd();
81
            }
82
83
            $this->styleCIConfigDir = $styleCIConfigDir ?: $configsPath;
84
            $this->finderDirs = $finderDirs ?: $configsPath;
85
        }
86
87
        $this->output = new ConsoleOutput();
88
        $this->output->getFormatter()->setStyle('warning', new OutputFormatterStyle('black', 'yellow'));
89
        // PHP-CS-Fixer 1.x BC
90
        if (class_exists('Symfony\CS\FixerFactory')) {
91
            $this->fixerFactory = FixerFactory::create();
92
            $this->fixerFactory->registerBuiltInFixers();
93
        }
94
95
        $this->parseStyleCIConfig();
96
    }
97
98
    /**
99
     * @param string       $styleCIConfigDir
100
     * @param string|array $finderDirs       A directory path or an array of directories for Finder
101
     *
102
     * @return Config
103
     */
104
    public static function create($styleCIConfigDir = null, $finderDirs = null)
105
    {
106
        $bridge = new static($styleCIConfigDir, $finderDirs);
107
108
        $config = Config::create();
109
110
        // PHP-CS-Fixer 1.x BC
111
        if (method_exists($config, 'level')) {
112
            $config->level(FixerInterface::NONE_LEVEL);
113
        }
114
115
        if (method_exists($config, 'setRules')) {
116
            $config->setRules($bridge->getRules());
117
        } else { // PHP-CS-Fixer 1.x BC
118
            $config->fixers($bridge->getFixers());
119
        }
120
121
        // PHP-CS-Fixer 1.x BC
122
        if (method_exists($config, 'setRiskyAllowed')) {
123
            $config->setRiskyAllowed($bridge->getRisky());
0 ignored issues
show
The method setRiskyAllowed() does not seem to exist on object<Symfony\CS\Config\Config>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
124
        }
125
126
        return $config
127
            ->finder($bridge->getFinder())
128
        ;
129
    }
130
131
    /**
132
     * @return Finder
133
     */
134
    public function getFinder()
135
    {
136
        $finder = DefaultFinder::create()->in($this->finderDirs);
137
        if (isset($this->styleCIConfig['finder'])) {
138
            $finderConfig = $this->styleCIConfig['finder'];
139
            foreach ($finderConfig as $key => $values) {
140
                $finderMethod = Container::camelize($key);
141
                foreach ($values as $value) {
142
                    if (method_exists($finder, $finderMethod)) {
143
                        $finder->$finderMethod($value);
144
                    } else {
145
                        $this->output->writeln(sprintf(
146
                            '<warning>Can not apply "%s" finder option with PHP-CS-Fixer v%s. You fixer config may be erroneous. Consider upgrading to fix it.</warning>',
147
                            str_replace('_', '-', $key),
148
                            Fixer::VERSION
149
                        ));
150
                    }
151
                }
152
            }
153
        }
154
155
        return $finder;
156
    }
157
158
    /**
159
     * @return int
160
     *
161
     * @deprecated since 1.1, to be removed in 2.0
162
     */
163
    public function getLevel()
164
    {
165
        @trigger_error('The '.__METHOD__.' method is deprecated since version 1.1 and will be removed in 2.0.', E_USER_DEPRECATED);
166
167
        $preset = $this->styleCIConfig['preset'];
168
        $validPresets = array(
169
            'psr1'    => FixerInterface::PSR1_LEVEL,
170
            'psr2'    => FixerInterface::PSR1_LEVEL,
171
            'symfony' => FixerInterface::SYMFONY_LEVEL,
172
        );
173
        if (!in_array($preset, array_keys($validPresets))) {
174
            throw new LevelConfigException(sprintf('Invalid preset "%s". Must be one of "%s".', $preset, implode('", "', array_keys($validPresets))));
175
        }
176
177
        return $validPresets[$preset];
178
    }
179
180
    /**
181
     * @return string[]
182
     */
183
    public function getFixers()
184
    {
185
        $presetFixers = $this->resolveAliases($this->getPresetFixers());
186
        $enabledFixers = $this->resolveAliases($this->styleCIConfig['enabled']);
187
        $disabledFixers = $this->resolveAliases($this->styleCIConfig['disabled']);
188
189
        $fixers = array_merge(
190
            $enabledFixers,
191
            array_map(function ($disabledFixer) {
192
                return '-'.$disabledFixer;
193
            }, $disabledFixers),
194
            array_diff($presetFixers, $disabledFixers) // Remove disabled fixers from preset
195
        );
196
197
        // PHP-CS-Fixer 1.x BC
198
        if (method_exists('Symfony\CS\Fixer\Contrib\HeaderCommentFixer', 'getHeader') && HeaderCommentFixer::getHeader()) {
199
            array_push($fixers, 'header_comment');
200
        }
201
202
        return $fixers;
203
    }
204
205
    /**
206
     * Returns fixers converted to rules for PHP-CS-Fixer 2.x.
207
     *
208
     * @return array
209
     */
210
    public function getRules()
211
    {
212
        $fixers = $this->getFixers();
213
214
        $rules = array();
215
        foreach ($fixers as $fixer) {
216
            if ('-' === $fixer[0]) {
217
                $name = substr($fixer, 1);
218
                $enabled = false;
219
            } else {
220
                $name = $fixer;
221
                $enabled = true;
222
            }
223
224
            if ($this->isFixerAvailable($name)) {
225
                $rules[$name] = $enabled;
226
            } else {
227
                $this->output->writeln(sprintf('<warning>Fixer "%s" does not exist, skipping.</warning>', $name));
228
            }
229
        }
230
231
        return $rules;
232
    }
233
234
    /**
235
     * @return bool
236
     */
237
    public function getRisky()
0 ignored issues
show
function getRisky() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
238
    {
239
        return $this->styleCIConfig['risky'];
240
    }
241
242
    /**
243
     * @return string[]
244
     */
245
    private function getPresetFixers()
246
    {
247
        $preset = $this->styleCIConfig['preset'];
248
        $validPresets = Fixers::getPresets();
249
250
        return $validPresets[$preset];
251
    }
252
253
    /**
254
     * Adds both aliases and real fixers if set. PHP-CS-Fixer would not take care if not existing.
255
     * Better compatibility between PHP-CS-Fixer 1.x and 2.x.
256
     *
257
     * @param string[] $fixers
258
     *
259
     * @return string[]
260
     */
261
    private function resolveAliases(array $fixers)
262
    {
263
        foreach (Fixers::$aliases as $alias => $name) {
264 View Code Duplication
            if (in_array($alias, $fixers, true) && !in_array($name, $fixers, true) && $this->isFixerAvailable($name)) {
265
                array_push($fixers, $name);
266
            }
267 View Code Duplication
            if (in_array($name, $fixers, true) && !in_array($alias, $fixers, true) && $this->isFixerAvailable($alias)) {
268
                array_push($fixers, $alias);
269
            }
270
        }
271
272
        return $fixers;
273
    }
274
275
    /**
276
     * @param string $name
277
     *
278
     * @return bool
279
     */
280
    private function isFixerAvailable($name)
281
    {
282
        // PHP-CS-Fixer 1.x BC
283
        if (null === $this->fixerFactory) {
284
            return true;
285
        }
286
287
        return $this->fixerFactory->hasRule($name);
288
    }
289
290
    private function parseStyleCIConfig()
291
    {
292
        if (null === $this->styleCIConfig) {
293
            $config = Yaml::parse(file_get_contents(sprintf('%s/.styleci.yml', $this->styleCIConfigDir)));
294
            $processor = new Processor();
295
            $this->styleCIConfig = $processor->processConfiguration(new Configuration(), array('styleci' => $config));
296
        }
297
    }
298
}
299