123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- <?php
- /*
- * This file is part of PHP CS Fixer.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- * Dariusz Rumiński <dariusz.ruminski@gmail.com>
- *
- * This source file is subject to the MIT license that is bundled
- * with this source code in the file LICENSE.
- */
- namespace PhpCsFixer;
- use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
- use PhpCsFixer\Fixer\ConfigurableFixerInterface;
- use PhpCsFixer\Fixer\FixerInterface;
- use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
- use Symfony\Component\Finder\Finder as SymfonyFinder;
- /**
- * Class provides a way to create a group of fixers.
- *
- * Fixers may be registered (made the factory aware of them) by
- * registering a custom fixer and default, built in fixers.
- * Then, one can attach Config instance to fixer instances.
- *
- * Finally factory creates a ready to use group of fixers.
- *
- * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
- *
- * @internal
- */
- final class FixerFactory
- {
- /**
- * @var FixerNameValidator
- */
- private $nameValidator;
- /**
- * @var FixerInterface[]
- */
- private $fixers = [];
- /**
- * @var FixerInterface[] Associative array of fixers with names as keys
- */
- private $fixersByName = [];
- public function __construct()
- {
- $this->nameValidator = new FixerNameValidator();
- }
- /**
- * Create instance.
- *
- * @return FixerFactory
- */
- public static function create()
- {
- return new self();
- }
- public function setWhitespacesConfig(WhitespacesFixerConfig $config)
- {
- foreach ($this->fixers as $fixer) {
- if ($fixer instanceof WhitespacesAwareFixerInterface) {
- $fixer->setWhitespacesConfig($config);
- }
- }
- return $this;
- }
- /**
- * @return FixerInterface[]
- */
- public function getFixers()
- {
- $this->sortFixers();
- return $this->fixers;
- }
- /**
- * @return $this
- */
- public function registerBuiltInFixers()
- {
- static $builtInFixers = null;
- if (null === $builtInFixers) {
- $builtInFixers = [];
- foreach (SymfonyFinder::create()->files()->in(__DIR__.'/Fixer') as $file) {
- $relativeNamespace = $file->getRelativePath();
- $fixerClass = 'PhpCsFixer\\Fixer\\'.($relativeNamespace ? $relativeNamespace.'\\' : '').$file->getBasename('.php');
- if ('Fixer' === substr($fixerClass, -5)) {
- $builtInFixers[] = $fixerClass;
- }
- }
- }
- foreach ($builtInFixers as $class) {
- $this->registerFixer(new $class(), false);
- }
- return $this;
- }
- /**
- * @param FixerInterface[] $fixers
- *
- * @return $this
- */
- public function registerCustomFixers(array $fixers)
- {
- foreach ($fixers as $fixer) {
- $this->registerFixer($fixer, true);
- }
- return $this;
- }
- /**
- * @param FixerInterface $fixer
- * @param bool $isCustom
- *
- * @return $this
- */
- public function registerFixer(FixerInterface $fixer, $isCustom)
- {
- $name = $fixer->getName();
- if (isset($this->fixersByName[$name])) {
- throw new \UnexpectedValueException(sprintf('Fixer named "%s" is already registered.', $name));
- }
- if (!$this->nameValidator->isValid($name, $isCustom)) {
- throw new \UnexpectedValueException(sprintf('Fixer named "%s" has invalid name.', $name));
- }
- $this->fixers[] = $fixer;
- $this->fixersByName[$name] = $fixer;
- return $this;
- }
- /**
- * Apply RuleSet on fixers to filter out all unwanted fixers.
- *
- * @param RuleSetInterface $ruleSet
- *
- * @return $this
- */
- public function useRuleSet(RuleSetInterface $ruleSet)
- {
- $fixers = [];
- $fixersByName = [];
- $fixerConflicts = [];
- $fixerNames = array_keys($ruleSet->getRules());
- foreach ($fixerNames as $name) {
- if (!array_key_exists($name, $this->fixersByName)) {
- throw new \UnexpectedValueException(sprintf('Rule "%s" does not exist.', $name));
- }
- $fixer = $this->fixersByName[$name];
- $config = $ruleSet->getRuleConfiguration($name);
- if (null !== $config) {
- if ($fixer instanceof ConfigurableFixerInterface) {
- if (!is_array($config) || !count($config)) {
- throw new InvalidFixerConfigurationException($fixer->getName(), 'Configuration must be an array and may not be empty.');
- }
- $fixer->configure($config);
- } else {
- throw new InvalidFixerConfigurationException($fixer->getName(), 'Is not configurable.');
- }
- }
- $fixers[] = $fixer;
- $fixersByName[$name] = $fixer;
- $conflicts = array_intersect($this->getFixersConflicts($fixer), $fixerNames);
- if (count($conflicts) > 0) {
- $fixerConflicts[$name] = $conflicts;
- }
- }
- if (count($fixerConflicts) > 0) {
- throw new \UnexpectedValueException($this->generateConflictMessage($fixerConflicts));
- }
- $this->fixers = $fixers;
- $this->fixersByName = $fixersByName;
- return $this;
- }
- /**
- * Check if fixer exists.
- *
- * @param string $name
- *
- * @return bool
- */
- public function hasRule($name)
- {
- return isset($this->fixersByName[$name]);
- }
- /**
- * Sort fixers by their priorities.
- *
- * @return $this
- */
- private function sortFixers()
- {
- // Schwartzian transform is used to improve the efficiency and avoid
- // `usort(): Array was modified by the user comparison function` warning for mocked objects.
- $data = array_map(function (FixerInterface $fixer) {
- return [$fixer, $fixer->getPriority()];
- }, $this->fixers);
- usort($data, function (array $a, array $b) {
- return Utils::cmpInt($b[1], $a[1]);
- });
- $this->fixers = array_map(function (array $item) {
- return $item[0];
- }, $data);
- return $this;
- }
- /**
- * @param FixerInterface $fixer
- *
- * @return string[]|null
- */
- private function getFixersConflicts(FixerInterface $fixer)
- {
- static $conflictMap = [
- 'no_blank_lines_before_namespace' => ['single_blank_line_before_namespace'],
- ];
- $fixerName = $fixer->getName();
- return array_key_exists($fixerName, $conflictMap) ? $conflictMap[$fixerName] : [];
- }
- /**
- * @param array<string, string[]> $fixerConflicts
- *
- * @return string
- */
- private function generateConflictMessage(array $fixerConflicts)
- {
- $message = 'Rule contains conflicting fixers:';
- $report = [];
- foreach ($fixerConflicts as $fixer => $fixers) {
- // filter mutual conflicts
- $report[$fixer] = array_filter(
- $fixers,
- function ($candidate) use ($report, $fixer) {
- return !array_key_exists($candidate, $report) || !in_array($fixer, $report[$candidate], true);
- }
- );
- if (count($report[$fixer]) > 0) {
- $message .= sprintf("\n- \"%s\" with \"%s\"", $fixer, implode('", "', $report[$fixer]));
- }
- }
- return $message;
- }
- }
|