├── tests ├── Fixtures │ └── configs │ │ ├── none │ │ └── .styleci.yml │ │ ├── none_with_rules │ │ └── .styleci.yml │ │ └── default │ │ └── .styleci.yml ├── ConfigBridgeTest.php └── StyleCI │ └── ConfigurationTest.php ├── .gitignore ├── .editorconfig ├── UPGRADE-1.1.md ├── UPGRADE-2.0.md ├── .php_cs ├── .styleci.yml ├── phpunit.xml.dist ├── LICENSE ├── autoload.php ├── composer.json ├── .travis.yml ├── CHANGELOG.md ├── src ├── StyleCI │ └── Configuration.php └── ConfigBridge.php └── README.md /tests/Fixtures/configs/none/.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: none 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /composer.lock 3 | .php_cs.cache 4 | /build 5 | /*.phar 6 | -------------------------------------------------------------------------------- /tests/Fixtures/configs/none_with_rules/.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: none 2 | 3 | enabled: 4 | - align_double_arrow 5 | - ordered_use 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [composer.json] 8 | indent_style = space 9 | indent_size = 4 10 | 11 | [Makefile] 12 | indent_style = tab 13 | -------------------------------------------------------------------------------- /UPGRADE-1.1.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 1.0 to 1.1 2 | 3 | ## ConfigBridge::getLevel deprecated 4 | 5 | Because StyleCI does not follow PHP-CS-Fixer levels, this method is deprecated. 6 | 7 | Use directly `ConfigBridge::getFixers()` to get the whole set based on StyleCI preset and manual configuration. 8 | -------------------------------------------------------------------------------- /UPGRADE-2.0.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 1.x to 2.0 2 | 3 | ## Deprecations 4 | 5 | All the deprecated code introduced on 1.x is removed on 2.0. 6 | 7 | Please read 1.x upgrade guides for more information. 8 | 9 | See also the [diff code](https://github.com/Soullivaneuh/php-cs-fixer-styleci-bridge/compare/1.x...v2.0.0). 10 | -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | setUsingCache(true); 9 | 10 | if (method_exists($config, 'setRiskyAllowed')) { 11 | $config->setRiskyAllowed(true); 12 | } 13 | 14 | return $config; 15 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: symfony 2 | 3 | enabled: 4 | - newline_after_open_tag 5 | - ordered_use 6 | - long_array_syntax 7 | - php_unit_construct 8 | - php_unit_strict 9 | 10 | disabled: 11 | - psr0 12 | 13 | finder: 14 | not-name: 15 | - "*.php.twig" 16 | not-path: 17 | - "src/StyleCI/Fixers.php" 18 | -------------------------------------------------------------------------------- /tests/Fixtures/configs/default/.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: symfony 2 | 3 | enabled: 4 | - align_double_arrow 5 | - newline_after_open_tag 6 | - ordered_use 7 | - long_array_syntax 8 | 9 | disabled: 10 | - psr0 11 | - unalign_double_arrow 12 | - unalign_equals 13 | 14 | finder: 15 | exclude: 16 | - tmp 17 | not-name: 18 | - autoload.php 19 | - skip_*_test.php 20 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | 19 | 20 | 21 | ./src 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Sullivan Senechal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /autoload.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * @see https://github.com/composer/composer/issues/1493#issuecomment-12492276 7 | */ 8 | class CustomLoader 9 | { 10 | /** 11 | * @var \Composer\Autoload\ClassLoader 12 | */ 13 | private $loader; 14 | 15 | /** 16 | * @param \Composer\Autoload\ClassLoader $loader 17 | */ 18 | public function __construct($loader) 19 | { 20 | $this->loader = $loader; 21 | } 22 | 23 | /** 24 | * @param string $class The class name 25 | * 26 | * @return bool|null 27 | */ 28 | public function loadClass($class) 29 | { 30 | $result = $this->loader->loadClass($class); 31 | 32 | if ($result && method_exists($class, '__static')) { 33 | call_user_func(array($class, '__static')); 34 | } 35 | 36 | return $result; 37 | } 38 | } 39 | 40 | if (file_exists(__DIR__.'/../../autoload.php')) { 41 | $loader = require __DIR__.'/../../autoload.php'; 42 | } else { 43 | $loader = require __DIR__.'/vendor/autoload.php'; 44 | } 45 | $loader->unregister(); 46 | spl_autoload_register(array(new CustomLoader($loader), 'loadClass')); 47 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sllh/php-cs-fixer-styleci-bridge", 3 | "description": "Auto configure PHP-CS-Fixer from StyleCI config file", 4 | "keywords": ["PHP-CS-Fixer", "StyleCI", "PSR", "PSR-1", "PSR-2", "PSR-4", "Symfony", "Laravel", "configuration"], 5 | "type": "library", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Sullivan SENECHAL", 10 | "email": "soullivaneuh@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": "^5.3 || ^7.0", 15 | "composer/semver": "^1.0", 16 | "doctrine/inflector": "^1.0", 17 | "sllh/styleci-fixers": "^3.0 || ^4.0", 18 | "symfony/config": "^2.3 || ^3.0", 19 | "symfony/console": "^2.3 || ^3.0", 20 | "symfony/yaml": "^2.3 || ^3.0" 21 | }, 22 | "require-dev": { 23 | "friendsofphp/php-cs-fixer": "^1.6.1", 24 | "matthiasnoback/symfony-config-test": "^1.2", 25 | "symfony/phpunit-bridge": "^2.7.4 || ^3.0", 26 | "twig/twig": "^1.22" 27 | }, 28 | "autoload": { 29 | "psr-4": { "SLLH\\StyleCIBridge\\": "src" } 30 | }, 31 | "autoload-dev": { 32 | "psr-4": { "SLLH\\StyleCIBridge\\Tests\\": "tests" } 33 | }, 34 | "config": { 35 | "sort-packages": true 36 | }, 37 | "extra": { 38 | "branch-alias": { 39 | "dev-master": "2.x-dev" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | only: 3 | - master 4 | - 1.x 5 | 6 | language: php 7 | 8 | php: 9 | - 5.4 10 | - 5.5 11 | - 5.6 12 | - 7.0 13 | - 7.1 14 | - 7.2 15 | - nightly 16 | - hhvm 17 | 18 | env: 19 | global: 20 | - PATH="$HOME/.composer/vendor/bin:$PATH" 21 | 22 | matrix: 23 | fast_finish: true 24 | include: 25 | - php: 5.3 26 | dist: precise 27 | - php: 5.3 28 | dist: precise 29 | env: COMPOSER_FLAGS="--prefer-lowest" 30 | - php: 5.6 31 | env: PHP_CS_FIXER_VERSION=1.* 32 | - php: 5.6 33 | env: PHP_CS_FIXER_VERSION=2.*@dev 34 | - php: 5.6 35 | env: SYMFONY_VERSION=2.7.* 36 | - php: 5.6 37 | env: SYMFONY_VERSION=2.8.* 38 | - php: 5.6 39 | env: SYMFONY_VERSION=3.0.* 40 | - php: 5.6 41 | env: SYMFONY_VERSION=3.1.* 42 | - php: 5.6 43 | env: SYMFONY_VERSION=3.2.*@dev 44 | allow_failures: 45 | - php: nightly 46 | - env: PHP_CS_FIXER_VERSION=2.*@dev 47 | - env: SYMFONY_VERSION=3.2.*@dev 48 | 49 | sudo: false 50 | 51 | cache: 52 | directories: 53 | - $HOME/.composer/cache/files 54 | 55 | before_install: 56 | - if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then phpenv config-rm xdebug.ini; fi; 57 | 58 | install: 59 | - composer global require 'phpunit/phpunit:^4.0 || ^5.0' satooshi/php-coveralls:@stable codeclimate/php-test-reporter:@stable sllh/composer-lint:@stable --prefer-dist --no-interaction 60 | - if [ "$PHP_CS_FIXER_VERSION" != "" ]; then composer require "friendsofphp/php-cs-fixer:${PHP_CS_FIXER_VERSION}" --no-update; fi; 61 | - if [ "$SYMFONY_VERSION" != "" ]; then composer require "symfony/symfony:${SYMFONY_VERSION}" --no-update; fi; 62 | - composer update --prefer-dist --no-interaction $COMPOSER_FLAGS 63 | 64 | before_script: 65 | - if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then echo "zend_extension=xdebug.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi; 66 | 67 | script: 68 | - composer validate 69 | - phpunit -c phpunit.xml.dist --coverage-clover build/logs/clover.xml 70 | 71 | after_script: 72 | - coveralls -v 73 | - test-reporter 74 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | * 2.1.1 (2016-06-22) 4 | 5 | * Fix missing PHPDoc class for `php-cs-fixer` v1 compatibility. 6 | * Use `doctine/inflector` for finder method camelize 7 | 8 | * 2.1.0 (2016-06-10) 9 | 10 | * Manage `none` preset of configuration file. 11 | 12 | * 2.0.2 (2016-06-01) 13 | 14 | * Use new `friendsofphp` vendor name for `php-cs-fixer`. 15 | 16 | * 2.0.1 (2016-04-18) 17 | 18 | * Enable `sllh/styleci-fixers` 4.0. 19 | 20 | * 2.0.0 (2016-04-17) 21 | 22 | * Remove all 1.x deprecated stuff. 23 | 24 | * 1.6.2 (2016-04-18) 25 | 26 | * Enable `sllh/styleci-fixers` 4.0. 27 | 28 | * 1.6.1 (2016-04-17) 29 | 30 | * Fix bad namespace issue with php-cs-fixer 2.x. 31 | 32 | * 1.6.0 (2016-04-17) 33 | 34 | * StyleCI fixers updated (`sllh/styleci-fixers` 3.0). 35 | * Fix some compatibility issues with php-cs-fixer 2.x. 36 | 37 | * 1.5.0 (2015-12-06) 38 | 39 | * Manage StyleCI `risky` option. 40 | 41 | * 1.4.1 (2015-11-19) 42 | 43 | * Manage single scalar values for `enabled`, `disabled` and `finder` configuration keys. 44 | 45 | * 1.4.0 (2015-11-10) 46 | 47 | * Extract StyleCI fixers generator outside of this project, 48 | using [sllh/styleci-fixers](https://github.com/Soullivaneuh/styleci-fixers). 49 | * Allow Symfony 3 dependencies. 50 | 51 | * 1.3.3 (2015-10-16) 52 | 53 | * Use a custom psr-4 loader to avoid code conflict. 54 | * Guess config files path using php backtrace. This avoid issues on subdirectories. 55 | 56 | * 1.3.2 (2015-10-05) 57 | 58 | * Use [composer/semver](https://packagist.org/packages/composer/semver) for better compatibility check. 59 | 60 | * 1.3.1 (2015-09-30) 61 | 62 | * Use `FixerFactory::hasRule` instead of building our own fixers array by name. 63 | 64 | * 1.3.0 (2015-09-29) 65 | 66 | * PHP-CS-Fixer version check. 67 | * Use Symfony Config component for `.styleci.yml` parsing. 68 | * Manage fixers conflicts. 69 | 70 | * 1.2.1 (2015-09-28) 71 | 72 | * Fix some BC breaks with PHP-CS-Fixer 2.0. 73 | 74 | * 1.2.0 (2015-09-25) 75 | 76 | * Resolve fixer aliases for better compatibility with PHP-CS-Fixer 1.x and 2.x. 77 | 78 | * 1.1.0 (2015-09-24) 79 | 80 | * Load fixers from StyleCI config instead of PHP-CS-Fixer. 81 | * Deprecate `ConfigBridge::getFixers`. 82 | -------------------------------------------------------------------------------- /tests/ConfigBridgeTest.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class ConfigBridgeTest extends \PHPUnit_Framework_TestCase 15 | { 16 | public function testDefaultConfig() 17 | { 18 | $config = ConfigBridge::create(__DIR__.'/Fixtures/configs/default'); 19 | 20 | if (method_exists($config, 'getRules')) { 21 | $this->assertArraySubset(array( 22 | 'align_double_arrow' => true, 23 | 'long_array_syntax' => true, 24 | 'linebreak_after_opening_tag' => true, 25 | 'ordered_imports' => true, 26 | 'psr0' => false, 27 | 'unalign_double_arrow' => false, 28 | 'unalign_equals' => false, 29 | ), $config->getRules()); 30 | } else { // PHP-CS-Fixer 1.x BC 31 | $expectedFixers = array( 32 | 'align_double_arrow', 33 | 'newline_after_open_tag', 34 | 'ordered_use', 35 | 'long_array_syntax', 36 | 'linebreak_after_opening_tag', 37 | '-psr0', 38 | '-unalign_double_arrow', 39 | '-unalign_equals', 40 | ); 41 | foreach ($expectedFixers as $expectedFixer) { 42 | $this->assertTrue( 43 | in_array($expectedFixer, $config->getFixers()), 44 | 'The configuration must have the following fixer: "'.$expectedFixer.'".' 45 | ); 46 | } 47 | } 48 | 49 | $this->assertAttributeContains('tmp', 'exclude', $config->getFinder()); 50 | $this->assertAttributeContains('autoload.php', 'notNames', $config->getFinder()); 51 | } 52 | 53 | public function testNonePreset() 54 | { 55 | $config = ConfigBridge::create(__DIR__.'/Fixtures/configs/none'); 56 | 57 | if (method_exists($config, 'getRules')) { 58 | $this->assertSame(array(), $config->getRules()); 59 | } else { // PHP-CS-Fixer 1.x BC 60 | $this->assertArraySubset(array(), $config->getFixers()); 61 | } 62 | } 63 | 64 | public function testNonePresetWithRules() 65 | { 66 | $config = ConfigBridge::create(__DIR__.'/Fixtures/configs/none_with_rules'); 67 | 68 | if (method_exists($config, 'getRules')) { 69 | $this->assertSame(array( 70 | 'align_double_arrow' => true, 71 | 'ordered_imports' => true, 72 | ), $config->getRules()); 73 | } else { // PHP-CS-Fixer 1.x BC 74 | $this->assertArraySubset(array( 75 | 'align_double_arrow', 76 | 'ordered_use', 77 | ), $config->getFixers()); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/StyleCI/Configuration.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | final class Configuration implements ConfigurationInterface 16 | { 17 | /** 18 | * {@inheritdoc} 19 | */ 20 | public function getConfigTreeBuilder() 21 | { 22 | $treeBuilder = new TreeBuilder(); 23 | $rootNode = $treeBuilder->root('styleci'); 24 | 25 | $validFixers = array_merge(Fixers::$valid, array_keys(Fixers::$aliases)); 26 | 27 | $rootNode 28 | ->children() 29 | ->enumNode('preset') 30 | ->isRequired() 31 | ->values(array_merge(array_keys(Fixers::getPresets()), array(ConfigBridge::PRESET_NONE))) 32 | ->end() 33 | ->booleanNode('linting') 34 | ->defaultTrue() 35 | ->end() 36 | ->arrayNode('enabled') 37 | ->beforeNormalization() 38 | ->ifString() 39 | ->then(function ($v) { 40 | return array($v); 41 | }) 42 | ->end() 43 | ->prototype('scalar') 44 | ->validate() 45 | ->ifNotInArray($validFixers) 46 | ->thenInvalid('Invalid enabled fixer %s.') 47 | ->end() 48 | ->end() 49 | ->end() 50 | ->arrayNode('disabled') 51 | ->beforeNormalization() 52 | ->ifString() 53 | ->then(function ($v) { 54 | return array($v); 55 | }) 56 | ->end() 57 | ->prototype('scalar') 58 | ->validate() 59 | ->ifNotInArray($validFixers) 60 | ->thenInvalid('Invalid disabled fixer %s.') 61 | ->end() 62 | ->end() 63 | ->end() 64 | ->append($this->getFinderConfigurationNode()) 65 | ->booleanNode('risky') 66 | ->defaultTrue() 67 | ->end() 68 | ->end() 69 | ->validate() 70 | ->ifTrue(function ($config) { 71 | $presets = Fixers::getPresets(); 72 | $enabledFixers = ConfigBridge::PRESET_NONE === $config['preset'] 73 | ? $config['enabled'] 74 | : array_merge($presets[$config['preset']], $config['enabled']); 75 | $disabledFixers = $config['disabled']; 76 | $fixers = array_diff($enabledFixers, $disabledFixers); 77 | 78 | // See: https://github.com/StyleCI/Config/blob/f9747aba632aa4d272f212b5b9c9942234f4f074/src/Config.php#L549-L553 79 | foreach (Fixers::$conflicts as $first => $second) { 80 | if (in_array($first, $fixers, true) && in_array($second, $fixers, true)) { 81 | return true; 82 | } 83 | } 84 | 85 | return false; 86 | }) 87 | ->thenInvalid('Conflicted fixers. Check conflicts definition.') 88 | ->end() 89 | ; 90 | 91 | return $treeBuilder; 92 | } 93 | 94 | /** 95 | * @return ArrayNodeDefinition|NodeDefinition 96 | */ 97 | private function getFinderConfigurationNode() 98 | { 99 | $treeBuilder = new TreeBuilder(); 100 | $node = $treeBuilder->root('finder'); 101 | 102 | $node 103 | ->beforeNormalization() 104 | ->always(function ($v) { 105 | foreach ($v as $option => $value) { 106 | $v[$option] = (array) $value; 107 | } 108 | 109 | return $v; 110 | }) 111 | ->end() 112 | ->children() 113 | ->arrayNode('exclude') 114 | ->prototype('scalar')->end() 115 | ->end() 116 | ->arrayNode('name') 117 | ->prototype('scalar')->end() 118 | ->end() 119 | ->arrayNode('not_name') 120 | ->prototype('scalar')->end() 121 | ->end() 122 | ->arrayNode('contains') 123 | ->prototype('scalar')->end() 124 | ->end() 125 | ->arrayNode('not_contains') 126 | ->prototype('scalar')->end() 127 | ->end() 128 | ->arrayNode('path') 129 | ->prototype('scalar')->end() 130 | ->end() 131 | ->arrayNode('not_path') 132 | ->prototype('scalar')->end() 133 | ->end() 134 | ->arrayNode('depth') 135 | ->prototype('scalar')->end() 136 | ->end() 137 | ->end() 138 | ; 139 | 140 | return $node; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /tests/StyleCI/ConfigurationTest.php: -------------------------------------------------------------------------------- 1 | assertConfigurationIsValid(array('styleci' => $configuration)); 27 | 28 | // Set default expected configuration 29 | $expectedProcessedConfiguration = array_merge(array( 30 | 'linting' => true, 31 | 'enabled' => array(), 32 | 'disabled' => array(), 33 | 'risky' => true, 34 | ), $expectedProcessedConfiguration ?: $configuration); 35 | 36 | if (isset($configuration['finder'])) { 37 | $expectedFinderProcessedConfiguration = array_merge_recursive(array( 38 | 'exclude' => array(), 39 | 'name' => array(), 40 | 'not_name' => array(), 41 | 'contains' => array(), 42 | 'not_contains' => array(), 43 | 'path' => array(), 44 | 'not_path' => array(), 45 | 'depth' => array(), 46 | ), !empty($expectedProcessedConfiguration) && isset($expectedProcessedConfiguration['finder']) 47 | ? $expectedProcessedConfiguration['finder'] 48 | : $configuration['finder'] 49 | ); 50 | 51 | $expectedProcessedConfiguration['finder'] = $expectedFinderProcessedConfiguration; 52 | } 53 | 54 | $this->assertProcessedConfigurationEquals(array('styleci' => $configuration), $expectedProcessedConfiguration); 55 | } 56 | 57 | public function validConfigurations() 58 | { 59 | return array( 60 | array( 61 | array( 62 | 'preset' => 'none', 63 | ), 64 | ), 65 | array( 66 | array( 67 | 'preset' => 'psr1', 68 | ), 69 | ), 70 | array( 71 | array( 72 | 'preset' => 'psr2', 73 | ), 74 | ), 75 | array( 76 | array( 77 | 'preset' => 'symfony', 78 | ), 79 | ), 80 | array( 81 | array( 82 | 'preset' => 'laravel', 83 | ), 84 | ), 85 | array( 86 | array( 87 | 'preset' => 'recommended', 88 | ), 89 | ), 90 | array( 91 | array( 92 | 'preset' => 'symfony', 93 | 'linting' => false, 94 | 'enabled' => array( 95 | 'return', 96 | 'phpdoc_params', 97 | ), 98 | 'disabled' => array( 99 | 'short_array_syntax', 100 | ), 101 | 'finder' => array( 102 | 'not_name' => array('*.dummy'), 103 | ), 104 | ), 105 | ), 106 | array( 107 | array( 108 | 'preset' => 'symfony', 109 | 'finder' => array( 110 | 'not-name' => array('*.dummy'), 111 | ), 112 | ), 113 | array( 114 | 'preset' => 'symfony', 115 | 'finder' => array( 116 | 'not_name' => array('*.dummy'), 117 | ), 118 | ), 119 | ), 120 | array( 121 | array( 122 | 'preset' => 'symfony', 123 | 'enabled' => array( 124 | 'align_double_arrow', 125 | ), 126 | 'disabled' => array( 127 | 'unalign_double_arrow', 128 | ), 129 | ), 130 | ), 131 | // Scalar values 132 | array( 133 | array( 134 | 'preset' => 'symfony', 135 | 'enabled' => 'return', 136 | 'disabled' => 'long_array_syntax', 137 | 'finder' => array( 138 | 'exclude' => 'foo', 139 | 'name' => 'foo', 140 | 'not_name' => 'foo', 141 | 'contains' => 'foo', 142 | 'not_contains' => 'foo', 143 | 'path' => 'foo', 144 | 'not_path' => 'foo', 145 | 'depth' => 'foo', 146 | ), 147 | ), 148 | array( 149 | 'preset' => 'symfony', 150 | 'enabled' => array('return'), 151 | 'disabled' => array('long_array_syntax'), 152 | 'finder' => array( 153 | 'exclude' => array('foo'), 154 | 'name' => array('foo'), 155 | 'not_name' => array('foo'), 156 | 'contains' => array('foo'), 157 | 'not_contains' => array('foo'), 158 | 'path' => array('foo'), 159 | 'not_path' => array('foo'), 160 | 'depth' => array('foo'), 161 | ), 162 | ), 163 | ), 164 | ); 165 | } 166 | 167 | /** 168 | * @dataProvider invalidConfigurations 169 | * 170 | * @param array $configuration 171 | */ 172 | public function testInvalidConfiguration(array $configuration) 173 | { 174 | $this->assertConfigurationIsInvalid(array('styleci' => $configuration)); 175 | } 176 | 177 | public function invalidConfigurations() 178 | { 179 | return array( 180 | array( 181 | array(), 182 | ), 183 | array( 184 | array( 185 | 'preset' => 'dummy', 186 | ), 187 | ), 188 | array( 189 | array( 190 | 'preset' => 'symfony', 191 | 'linting' => 42, 192 | ), 193 | ), 194 | array( 195 | array( 196 | 'preset' => 'symfony', 197 | 'linting' => false, 198 | 'enabled' => false, 199 | ), 200 | ), 201 | array( 202 | array( 203 | 'preset' => 'symfony', 204 | 'disabled' => false, 205 | ), 206 | ), 207 | array( 208 | array( 209 | 'preset' => 'symfony', 210 | 'enabled' => array( 211 | 'dummy', 212 | 'phpdoc_params', 213 | ), 214 | ), 215 | ), 216 | array( 217 | array( 218 | 'preset' => 'symfony', 219 | 'disabled' => array( 220 | 'dummy', 221 | 'short_array_syntax', 222 | ), 223 | ), 224 | ), 225 | array( 226 | array( 227 | 'preset' => 'symfony', 228 | 'finder' => array( 229 | 'not-existing-method' => array('*.dummy'), 230 | ), 231 | ), 232 | ), 233 | array( 234 | array( 235 | 'preset' => 'symfony', 236 | 'enabled' => array( 237 | 'align_double_arrow', 238 | ), 239 | ), 240 | ), 241 | array( 242 | array( 243 | 'preset' => 'psr1', 244 | 'enabled' => array( 245 | 'no_blank_lines_before_namespace', 246 | 'single_blank_line_before_namespace', 247 | ), 248 | ), 249 | ), 250 | ); 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP-CS-Fixer StyleCI bridge 2 | 3 | :warning: **This package is not maintained anymore.** :warning: 4 | 5 | **PHP-CS-Fixer v2 came with a brand new configuration structure but StyleCI decided to keep the old way.** 6 | 7 | **Because of that, it's now very hard to maintain this bridge and it's still not compatible with PHP-CS-Fixer v2.** 8 | 9 | **This is also why I decided to abandon this package and write [FlintCI](https://flintci.io), 10 | my own Code Review service for multiple fixers and linters without any required configuration bridge.** 11 | 12 | **You can try it [here](https://flintci.io) (flintci.io), or keep using StyleCI without this bridge.** 13 | 14 | Auto configure [PHP-CS-Fixer](http://cs.sensiolabs.org/) from [StyleCI](https://styleci.io/) config file. 15 | 16 | This library permits to generate php-cs-fixer configuration directly from your `.styleci.yml` config file. 17 | 18 | With that, you will avoid the pain of both config files maintenance. 19 | 20 | [![Latest Stable Version](https://poser.pugx.org/sllh/php-cs-fixer-styleci-bridge/v/stable)](https://packagist.org/packages/sllh/php-cs-fixer-styleci-bridge) 21 | [![Latest Unstable Version](https://poser.pugx.org/sllh/php-cs-fixer-styleci-bridge/v/unstable)](https://packagist.org/packages/sllh/php-cs-fixer-styleci-bridge) 22 | [![License](https://poser.pugx.org/sllh/php-cs-fixer-styleci-bridge/license)](https://packagist.org/packages/sllh/php-cs-fixer-styleci-bridge) 23 | [![Dependency Status](https://www.versioneye.com/php/sllh:php-cs-fixer-styleci-bridge/badge.svg)](https://www.versioneye.com/php/sllh:php-cs-fixer-styleci-bridge) 24 | [![Reference Status](https://www.versioneye.com/php/sllh:php-cs-fixer-styleci-bridge/reference_badge.svg)](https://www.versioneye.com/php/sllh:php-cs-fixer-styleci-bridge/references) 25 | 26 | [![Total Downloads](https://poser.pugx.org/sllh/php-cs-fixer-styleci-bridge/downloads)](https://packagist.org/packages/sllh/php-cs-fixer-styleci-bridge) 27 | [![Monthly Downloads](https://poser.pugx.org/sllh/php-cs-fixer-styleci-bridge/d/monthly)](https://packagist.org/packages/sllh/php-cs-fixer-styleci-bridge) 28 | [![Daily Downloads](https://poser.pugx.org/sllh/php-cs-fixer-styleci-bridge/d/daily)](https://packagist.org/packages/sllh/php-cs-fixer-styleci-bridge) 29 | 30 | [![Build Status](https://travis-ci.org/Soullivaneuh/php-cs-fixer-styleci-bridge.svg?branch=master)](https://travis-ci.org/Soullivaneuh/php-cs-fixer-styleci-bridge) 31 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Soullivaneuh/php-cs-fixer-styleci-bridge/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/Soullivaneuh/php-cs-fixer-styleci-bridge/?branch=master) 32 | [![Code Climate](https://codeclimate.com/github/Soullivaneuh/php-cs-fixer-styleci-bridge/badges/gpa.svg)](https://codeclimate.com/github/Soullivaneuh/php-cs-fixer-styleci-bridge) 33 | [![Coverage Status](https://coveralls.io/repos/Soullivaneuh/php-cs-fixer-styleci-bridge/badge.svg?branch=master)](https://coveralls.io/r/Soullivaneuh/php-cs-fixer-styleci-bridge?branch=master) 34 | [![SensioLabsInsight](https://insight.sensiolabs.com/projects/f04c9b72-91a8-4ad7-9e7f-ce6dfc66df78/mini.png)](https://insight.sensiolabs.com/projects/f04c9b72-91a8-4ad7-9e7f-ce6dfc66df78) 35 | 36 | ## Who is using this? 37 | 38 | You can see which projects are using this package on the dedicated [Packagist page](https://packagist.org/packages/sllh/php-cs-fixer-styleci-bridge/dependents). 39 | 40 | ## Installation 41 | 42 | Include this library on your dev dependencies: 43 | 44 | ```bash 45 | composer require --dev sllh/php-cs-fixer-styleci-bridge 46 | ``` 47 | 48 | ## Usage 49 | 50 | You can use this bridge with several manners. 51 | 52 | ### Basic usage 53 | 54 | Put the following config on your `.php_cs` file: 55 | 56 | ```php 57 | setUsingCache(true) // Enable the cache 112 | ; 113 | ``` 114 | 115 | ### Using the bridge 116 | 117 | You can also using bridge method, part by part. 118 | 119 | ```php 120 | finder($bridge->getFinder()) 131 | ->fixers(['dummy', 'foo', '-bar']) 132 | ->setUsingCache(true) 133 | ; 134 | ``` 135 | 136 | ### Manually enable or disable fixers 137 | 138 | To enable or disable some fixers manually on the `.php_cs` file, 139 | you will have to use merge system to keep fixers defined by the bridge: 140 | 141 | ```php 142 | require_once __DIR__.'/vendor/sllh/php-cs-fixer-styleci-bridge/autoload.php'; 143 | 144 | use SLLH\StyleCIBridge\ConfigBridge; 145 | 146 | $config = ConfigBridge::create(); 147 | 148 | return $config 149 | ->setUsingCache(true) 150 | ->fixers(array_merge($config->getFixers(), ['-psr0', 'custom', 'foo', '-bar'])) 151 | ; 152 | ``` 153 | 154 | ### Header comment 155 | 156 | Unfortunately, header comment option [is not available](https://twitter.com/soullivaneuh/status/644795113399582720) on StyleCI config file. 157 | 158 | You will have to copy it from StyleCI web interface and set it manually. 159 | 160 | The config bridge will automatically detect the fixer and add it on CS configuration. 161 | 162 | #### PHP-CS-Fixer 1.x 163 | 164 | ```php 165 | 176 | 177 | This source file is subject to the MIT license that is bundled 178 | with this source code in the file LICENSE. 179 | EOF; 180 | 181 | HeaderCommentFixer::setHeader($header); 182 | 183 | return ConfigBridge::create(); 184 | ``` 185 | 186 | #### PHP-CS-Fixer 2.x 187 | 188 | ```php 189 | 199 | 200 | This source file is subject to the MIT license that is bundled 201 | with this source code in the file LICENSE. 202 | EOF; 203 | 204 | $config = ConfigBridge::create(); 205 | 206 | return $config 207 | ->setRules(array_merge($config->getRules(), array( 208 | 'header_comment' => array('header' => $header) 209 | ))) 210 | ; 211 | ``` 212 | 213 | #### Both versions 214 | 215 | You can handle both versions easily with some magic `method_exists` tricks: 216 | 217 | ```php 218 | 229 | 230 | This source file is subject to the MIT license that is bundled 231 | with this source code in the file LICENSE. 232 | EOF; 233 | 234 | // PHP-CS-Fixer 1.x 235 | if (method_exists('Symfony\CS\Fixer\Contrib\HeaderCommentFixer', 'getHeader')) { 236 | HeaderCommentFixer::setHeader($header); 237 | } 238 | 239 | $config = ConfigBridge::create(); 240 | 241 | // PHP-CS-Fixer 2.x 242 | if (method_exists($config, 'setRules')) { 243 | $config->setRules(array_merge($config->getRules(), array( 244 | 'header_comment' => array('header' => $header) 245 | ))); 246 | } 247 | 248 | return $config; 249 | ``` 250 | 251 | ## Troubleshooting 252 | 253 | ### Conflict with code or vendor library 254 | 255 | In some edge cases, the bridge code may conflict with your code or your included vendor. 256 | 257 | This kind of issue was discovered in [puli/cli#21 (comment)](https://github.com/puli/cli/pull/21#issuecomment-148438983) 258 | and fixed since `v1.3.3` in [#47](https://github.com/Soullivaneuh/php-cs-fixer-styleci-bridge/pull/47). 259 | 260 | Before that, you had to require the vendor autoload like this: 261 | 262 | ```php 263 | require __DIR__.'/vendor/autoload.php'; 264 | ``` 265 | 266 | This is not the secured way. Make sure to require our custom loader instead: 267 | 268 | ```php 269 | require __DIR__.'/vendor/sllh/php-cs-fixer-styleci-bridge/autoload.php'; 270 | ``` 271 | -------------------------------------------------------------------------------- /src/ConfigBridge.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | final class ConfigBridge 26 | { 27 | const CS_FIXER_MIN_VERSION = '1.6.1'; 28 | 29 | const PRESET_NONE = 'none'; 30 | 31 | /** 32 | * @var OutputInterface 33 | */ 34 | private $output; 35 | 36 | /** 37 | * @var FixerFactory 38 | */ 39 | private $fixerFactory = null; 40 | 41 | /** 42 | * @var string 43 | */ 44 | private $styleCIConfigDir; 45 | 46 | /** 47 | * @var array|null 48 | */ 49 | private $styleCIConfig = null; 50 | 51 | /** 52 | * @var string|array 53 | */ 54 | private $finderDirs; 55 | 56 | /** 57 | * @param string|null $styleCIConfigDir StyleCI config directory. Called script dir as default 58 | * @param string|array|null $finderDirs A directory path or an array of directories for Finder. Called script dir as default 59 | */ 60 | public function __construct($styleCIConfigDir = null, $finderDirs = null) 61 | { 62 | if (!Semver::satisfies( 63 | class_exists('Symfony\CS\Fixer') ? Fixer::VERSION : Application::VERSION, // PHP-CS-Fixer 1.x BC 64 | sprintf('>=%s', self::CS_FIXER_MIN_VERSION) 65 | )) { 66 | throw new \RuntimeException(sprintf( 67 | 'PHP-CS-Fixer v%s is not supported, please upgrade to v%s or higher.', 68 | Fixer::VERSION, 69 | self::CS_FIXER_MIN_VERSION 70 | )); 71 | } 72 | 73 | // Guess config files path if not specified. 74 | // getcwd function is not enough. See: https://github.com/Soullivaneuh/php-cs-fixer-styleci-bridge/issues/46 75 | if (null === $styleCIConfigDir || null === $finderDirs) { 76 | $dbt = version_compare(PHP_VERSION, '5.4.0', '>=') ? debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2) : debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); 77 | 78 | // Static call 79 | if (isset($dbt[1]['class']) && 'SLLH\StyleCIBridge\ConfigBridge' === $dbt[1]['class'] && 'create' === $dbt[1]['function']) { 80 | $configsPath = dirname($dbt[1]['file']); 81 | } elseif (isset($dbt[0]['class']) && 'SLLH\StyleCIBridge\ConfigBridge' === $dbt[0]['class'] && '__construct' === $dbt[0]['function']) { // Manual instance 82 | $configsPath = dirname($dbt[0]['file']); 83 | } else { // If no case found, fallback to not reliable getcwd method. 84 | $configsPath = getcwd(); 85 | } 86 | 87 | $this->styleCIConfigDir = $styleCIConfigDir ?: $configsPath; 88 | $this->finderDirs = $finderDirs ?: $configsPath; 89 | } 90 | 91 | $this->output = new ConsoleOutput(); 92 | $this->output->getFormatter()->setStyle('warning', new OutputFormatterStyle('black', 'yellow')); 93 | // PHP-CS-Fixer 1.x BC 94 | if (class_exists('PhpCsFixer\FixerFactory')) { // PHP-CS-Fixer 2.x only 95 | $this->fixerFactory = FixerFactory::create(); 96 | $this->fixerFactory->registerBuiltInFixers(); 97 | } 98 | 99 | $this->parseStyleCIConfig(); 100 | } 101 | 102 | /** 103 | * @param string $styleCIConfigDir 104 | * @param string|array $finderDirs A directory path or an array of directories for Finder 105 | * 106 | * @return Config|\Symfony\CS\Config|\Symfony\CS\Config\Config 107 | */ 108 | public static function create($styleCIConfigDir = null, $finderDirs = null) 109 | { 110 | $bridge = new static($styleCIConfigDir, $finderDirs); 111 | 112 | if (class_exists('\Symfony\CS\Config')) { // PHP-CS-Fixer >=1.12,<2.0 113 | $config = \Symfony\CS\Config::create(); 114 | } elseif (class_exists('\Symfony\CS\Config\Config')) { // PHP-CS-Fixer 1.x 115 | $config = \Symfony\CS\Config\Config::create(); 116 | } else { // PHP-CS-Fixer 2.x 117 | $config = Config::create(); 118 | } 119 | 120 | // PHP-CS-Fixer 1.x BC 121 | if (method_exists($config, 'level')) { 122 | $config->level(FixerInterface::NONE_LEVEL); 123 | } 124 | 125 | if (method_exists($config, 'setRules')) { 126 | $config->setRules($bridge->getRules()); 127 | } else { // PHP-CS-Fixer 1.x BC 128 | $config->fixers($bridge->getFixers()); 129 | } 130 | 131 | // PHP-CS-Fixer 1.x BC 132 | if (method_exists($config, 'setRiskyAllowed')) { 133 | $config->setRiskyAllowed($bridge->getRisky()); 134 | } 135 | 136 | // PHP-CS-Fixer 1.x BC 137 | if (method_exists($config, 'setFinder')) { 138 | $config->setFinder($bridge->getFinder()); 139 | } else { 140 | $config->finder($bridge->getFinder()); 141 | } 142 | 143 | return $config; 144 | } 145 | 146 | /** 147 | * @return Finder|\Symfony\CS\Finder|\Symfony\CS\Finder\DefaultFinder 148 | */ 149 | public function getFinder() 150 | { 151 | // PHP-CS-Fixer 1.x BC 152 | if (class_exists('\Symfony\CS\Finder')) { // PHP-CS-Fixer >=1.12,<2.0 153 | $finder = \Symfony\CS\Finder::create()->in($this->finderDirs); 154 | } elseif (class_exists('\Symfony\CS\Finder\DefaultFinder')) { // PHP-CS-Fixer 1.x 155 | $finder = \Symfony\CS\Finder\DefaultFinder::create()->in($this->finderDirs); 156 | } else { // PHP-CS-Fixer 2.x 157 | $finder = Finder::create()->in($this->finderDirs); 158 | } 159 | 160 | if (isset($this->styleCIConfig['finder'])) { 161 | $finderConfig = $this->styleCIConfig['finder']; 162 | foreach ($finderConfig as $key => $values) { 163 | $finderMethod = Inflector::camelize($key); 164 | foreach ($values as $value) { 165 | if (method_exists($finder, $finderMethod)) { 166 | $finder->$finderMethod($value); 167 | } else { 168 | $this->output->writeln(sprintf( 169 | 'Can not apply "%s" finder option with PHP-CS-Fixer v%s. You fixer config may be erroneous. Consider upgrading to fix it.', 170 | str_replace('_', '-', $key), 171 | Fixer::VERSION 172 | )); 173 | } 174 | } 175 | } 176 | } 177 | 178 | return $finder; 179 | } 180 | 181 | /** 182 | * @return string[] 183 | */ 184 | public function getFixers() 185 | { 186 | $presetFixers = $this->resolveAliases($this->getPresetFixers()); 187 | $enabledFixers = $this->resolveAliases($this->styleCIConfig['enabled']); 188 | $disabledFixers = $this->resolveAliases($this->styleCIConfig['disabled']); 189 | 190 | $fixers = array_merge( 191 | $enabledFixers, 192 | array_map(function ($disabledFixer) { 193 | return '-'.$disabledFixer; 194 | }, $disabledFixers), 195 | array_diff($presetFixers, $disabledFixers) // Remove disabled fixers from preset 196 | ); 197 | 198 | // PHP-CS-Fixer 1.x BC 199 | if (method_exists('Symfony\CS\Fixer\Contrib\HeaderCommentFixer', 'getHeader') && HeaderCommentFixer::getHeader()) { 200 | array_push($fixers, 'header_comment'); 201 | } 202 | 203 | return $fixers; 204 | } 205 | 206 | /** 207 | * Returns fixers converted to rules for PHP-CS-Fixer 2.x. 208 | * 209 | * @return array 210 | */ 211 | public function getRules() 212 | { 213 | $fixers = $this->getFixers(); 214 | 215 | $rules = array(); 216 | foreach ($fixers as $fixer) { 217 | if ('-' === $fixer[0]) { 218 | $name = substr($fixer, 1); 219 | $enabled = false; 220 | } else { 221 | $name = $fixer; 222 | $enabled = true; 223 | } 224 | 225 | if ($this->isFixerAvailable($name)) { 226 | $rules[$name] = $enabled; 227 | } else { 228 | $this->output->writeln(sprintf('Fixer "%s" does not exist, skipping.', $name)); 229 | } 230 | } 231 | 232 | return $rules; 233 | } 234 | 235 | /** 236 | * @return bool 237 | */ 238 | public function getRisky() 239 | { 240 | return $this->styleCIConfig['risky']; 241 | } 242 | 243 | /** 244 | * @return string[] 245 | */ 246 | private function getPresetFixers() 247 | { 248 | if (static::PRESET_NONE === $this->styleCIConfig['preset']) { 249 | return array(); 250 | } 251 | $validPresets = Fixers::getPresets(); 252 | 253 | return $validPresets[$this->styleCIConfig['preset']]; 254 | } 255 | 256 | /** 257 | * Adds both aliases and real fixers if set. PHP-CS-Fixer would not take care if not existing. 258 | * Better compatibility between PHP-CS-Fixer 1.x and 2.x. 259 | * 260 | * @param string[] $fixers 261 | * 262 | * @return string[] 263 | */ 264 | private function resolveAliases(array $fixers) 265 | { 266 | foreach (Fixers::$aliases as $alias => $name) { 267 | if (in_array($alias, $fixers, true) && !in_array($name, $fixers, true) && $this->isFixerAvailable($name)) { 268 | array_push($fixers, $name); 269 | } 270 | if (in_array($name, $fixers, true) && !in_array($alias, $fixers, true) && $this->isFixerAvailable($alias)) { 271 | array_push($fixers, $alias); 272 | } 273 | } 274 | 275 | return $fixers; 276 | } 277 | 278 | /** 279 | * @param string $name 280 | * 281 | * @return bool 282 | */ 283 | private function isFixerAvailable($name) 284 | { 285 | // PHP-CS-Fixer 1.x BC 286 | if (null === $this->fixerFactory) { 287 | return true; 288 | } 289 | 290 | return $this->fixerFactory->hasRule($name); 291 | } 292 | 293 | private function parseStyleCIConfig() 294 | { 295 | if (null === $this->styleCIConfig) { 296 | $config = Yaml::parse(file_get_contents(sprintf('%s/.styleci.yml', $this->styleCIConfigDir))); 297 | $processor = new Processor(); 298 | $this->styleCIConfig = $processor->processConfiguration(new Configuration(), array('styleci' => $config)); 299 | } 300 | } 301 | } 302 | --------------------------------------------------------------------------------