├── .editorconfig ├── .formatter.yml ├── .gitignore ├── .php_cs ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bin └── php-formatter ├── box.json ├── build └── php-formatter.phar ├── composer.json ├── phpunit.xml.dist ├── src ├── PHPFormatter │ ├── Command │ │ ├── HeaderCommand.php │ │ ├── PHPFormatterCommand.php │ │ ├── StrictCommand.php │ │ └── UseSortCommand.php │ ├── Console │ │ └── Application.php │ ├── Finder │ │ ├── ConfigFinder.php │ │ └── FileFinder.php │ ├── Fixer │ │ ├── HeaderFixer.php │ │ ├── Interfaces │ │ │ └── FixerInterface.php │ │ ├── StrictFixer.php │ │ └── UseSortFixer.php │ ├── Loader │ │ └── ConfigLoader.php │ └── PHPFormatter.php └── bootstrap.php └── tests └── PHPFormatter ├── Finder ├── ConfigFinderTest.php └── FileFinderTest.php ├── Fixer ├── EmptyHeaderFixerTest.php ├── HeaderFixerTest.php ├── HeaderWithStrictFixerTest.php ├── StrictFixerTest.php └── UseSortFixerTest.php ├── Loader └── ConfigLoaderTest.php └── Mocks ├── .formatter.yml ├── MyClass.php ├── MyOtherClass.php ├── SimpleMock.php.mock └── directory └── MyThirdClass.php /.editorconfig: -------------------------------------------------------------------------------- 1 | ; top-most EditorConfig file 2 | root = true 3 | 4 | ; Unix-style newlines 5 | [*] 6 | end_of_line = LF 7 | 8 | [*.php] 9 | indent_style = space 10 | indent_size = 4 -------------------------------------------------------------------------------- /.formatter.yml: -------------------------------------------------------------------------------- 1 | use-sort: 2 | group: 3 | - _main 4 | - Mmoreram 5 | group-type: each 6 | sort-type: alph 7 | sort-direction: asc 8 | 9 | strict: true 10 | header: | 11 | /* 12 | * This file is part of the php-formatter package 13 | * 14 | * Copyright (c) >=2014 Marc Morera 15 | * 16 | * For the full copyright and license information, please view the LICENSE 17 | * file that was distributed with this source code. 18 | * 19 | * Feel free to edit as you please, and have fun. 20 | * 21 | * @author Marc Morera 22 | */ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /bin/* 3 | !/bin/compile 4 | !/bin/php-formatter 5 | composer.phar 6 | composer.lock 7 | phpunit.xml 8 | -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | in(__DIR__.'/src') 5 | ->in(__DIR__.'/tests') 6 | ; 7 | 8 | return PhpCsFixer\Config::create() 9 | ->setRules([ 10 | '@Symfony' => true, 11 | 'concat_space' => ['spacing' => 'one'], 12 | 'no_multiline_whitespace_before_semicolons' => false, 13 | 'array_syntax' => ['syntax' => 'short'], 14 | 'yoda_style' => false, 15 | 'return_type_declaration' => ['space_before' => 'one'], 16 | 'self_accessor' => false, 17 | 'no_extra_consecutive_blank_lines' => false, 18 | ]) 19 | ->setFinder($finder) 20 | ; 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | sudo: false 4 | 5 | matrix: 6 | include: 7 | - php: 7.0 8 | - php: 7.1 9 | - php: 7.2 10 | env: deps=low 11 | fast_finish: true 12 | 13 | install: 14 | - phpenv config-rm xdebug.ini 15 | - if [[ $deps = low ]]; then composer update --prefer-lowest --prefer-stable; else composer install; fi 16 | 17 | script: 18 | - phpdbg -qrr vendor/bin/phpunit --coverage-text 19 | 20 | notifications: 21 | email: false 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ----- 3 | 4 | This projects follows Symfony2 coding standards, so pull requests must pass phpcs 5 | checks. Read more details about 6 | [Symfony2 coding standards](http://symfony.com/doc/current/contributing/code/standards.html) 7 | and install the corresponding [CodeSniffer definition](https://github.com/opensky/Symfony2-coding-standard) 8 | to run code validation. 9 | 10 | There is a policy for contributing to this project. Pull requests must 11 | be explained step by step to make the review process easy in order to 12 | accept and merge them. New features must come paired with PHPUnit tests. 13 | 14 | If you would like to contribute, please read the [Contributing Code][1] in the project 15 | documentation. If you are submitting a pull request, please follow the guidelines 16 | in the [Submitting a Patch][2] section and use the [Pull Request Template][3]. 17 | 18 | [1]: http://symfony.com/doc/current/contributing/code/index.html 19 | [2]: http://symfony.com/doc/current/contributing/code/patches.html#check-list 20 | [3]: http://symfony.com/doc/current/contributing/code/patches.html#make-a-pull-request -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Marc Morera 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Formatter 2 | 3 | [![Build Status](https://travis-ci.org/mmoreram/php-formatter.png?branch=master)](https://travis-ci.org/mmoreram/php-formatter) 4 | 5 | This PHP formatter aims to provide you some bulk actions for you PHP projects to 6 | ensure their consistency. None of them fixes PSR rules. If you want to fix PSR 7 | rules, please check [friendsofphp/php-cs-fixer](https://github.com/friendsofphp/PHP-CS-Fixer). 8 | 9 | ## Install 10 | 11 | Install PHP Formatter in this way: 12 | 13 | ``` bash 14 | $ composer global require mmoreram/php-formatter=dev-master 15 | ``` 16 | 17 | If it is the first time you globally install a dependency then make sure 18 | you include `~/.composer/vendor/bin` in $PATH as shown [here](http://getcomposer.org/doc/03-cli.md#global). 19 | 20 | ### Always keep your PHP Formatter installation up to date: 21 | 22 | ``` bash 23 | $ composer global update mmoreram/php-formatter 24 | ``` 25 | 26 | ### .phar file 27 | 28 | You can also use already last built `.phar`. 29 | 30 | ``` bash 31 | $ git clone git@github.com:mmoreram/php-formatter.git 32 | $ cd php-formatter 33 | $ php build/php-formatter.phar 34 | ``` 35 | 36 | You can copy the `.phar` file as a global script 37 | 38 | ``` bash 39 | $ cp build/php-formatter.phar /usr/local/bin/php-formatter 40 | ``` 41 | 42 | ### Compile 43 | 44 | Finally you can also compile your own version of the package. ( You need set 45 | `phar.readonly = Off` in your php.ini ). For the compilation of this package you 46 | need the [box-project/box2](https://github.com/box-project/box2) library. 47 | 48 | ``` bash 49 | $ git clone git@github.com:mmoreram/php-formatter.git 50 | $ cd php-formatter 51 | $ composer update --no-dev 52 | $ box build -v 53 | $ build/php-formatter.phar 54 | ``` 55 | 56 | You can copy the `.phar` file as a global script 57 | 58 | ``` bash 59 | $ cp build/php-formatter.phar /usr/local/bin/php-formatter 60 | ``` 61 | 62 | ## Config 63 | 64 | You can place a file named `.formatter.yml` in the root of your project. In 65 | every command execution, this will be the priority of the definitions. 66 | 67 | If an option is set in the command, this will be used. Otherwise, if is defined 68 | in a found config file, this will be used. Otherwise, default value will be 69 | used. 70 | 71 | This is the config reference 72 | 73 | ``` yml 74 | use-sort: 75 | group: 76 | - _main 77 | - Mmoreram 78 | group-type: each 79 | sort-type: alph 80 | sort-direction: asc 81 | 82 | strict: ~ 83 | header: | 84 | /* 85 | * This file is part of the php-formatter package 86 | * 87 | * Copyright (c) 2014 Marc Morera 88 | * 89 | * For the full copyright and license information, please view the LICENSE 90 | * file that was distributed with this source code. 91 | * 92 | * Feel free to edit as you please, and have fun. 93 | * 94 | * @author Marc Morera 95 | */ 96 | ``` 97 | 98 | you can also define where to search the `.formatter.yml` file using the 99 | `--config|-c` option 100 | 101 | ``` bash 102 | $ php-formatter formatter:use:sort src/ --config="src/" 103 | ``` 104 | 105 | ## Commands 106 | 107 | PHP Formatter is a set of commands useful for your PHP projects. They do not 108 | consider any kind of Common Coding Standard, like PSR-0 or PSR-1, is more like 109 | a useful way of working for developers and reviewers. 110 | 111 | ``` bash 112 | Console Tool 113 | 114 | Usage: 115 | [options] command [arguments] 116 | 117 | Options: 118 | --help -h Display this help message. 119 | --quiet -q Do not output any message. 120 | --verbose -v|vv|vvv Increase the verbosity of messages 121 | --version -V Display this application version. 122 | --ansi Force ANSI output. 123 | --no-ansi Disable ANSI output. 124 | --no-interaction -n Do not ask any interactive question. 125 | 126 | Available commands: 127 | help Displays help for a command 128 | list Lists commands 129 | formatter 130 | formatter:header:fix Ensures that all PHP files has header defined in config file 131 | formatter:strict:fix Ensures that all PHP files have strict mode defined in config file. Only valid for >=PHP7.0 132 | formatter:use:sort Sort Use statements 133 | ``` 134 | 135 | ### Sort all Use Statements 136 | 137 | You can sort your Use Statements in different ways. For this command you 138 | must provide as an argument the path where to look for the PHP files you want to 139 | process. 140 | 141 | * command: `php-formatter formatter:use:sort` 142 | * argument: path 143 | * option: --exclude [***multiple***] 144 | * option: --group [***multiple***] 145 | * option: --group-type=***one***|***each*** 146 | * option: --sort-type=***alph***|***length*** 147 | * option: --sort-direction=***asc***|***desc*** 148 | * option: --dry-run [***no value***] 149 | 150 | #### Group 151 | 152 | You can sort your Use statements using as many groups as you want (***--group***). 153 | It means that you can group lines with same root (***Symfony\\***) in a specific 154 | order. 155 | 156 | Common group is named `_main` and if is not specified, is placed in the 157 | beginning. You can define where to place this group with the `--group` option 158 | 159 | ``` bash 160 | $ php-formatter formatter:use:sort src/ --group="Mmoreram" --group="_main" --group="Symfony" 161 | ``` 162 | 163 | This command will sort the code like this 164 | 165 | ``` php 166 | use Mmoreram\MyClass; 167 | use Mmoreram\MySecondClass; 168 | 169 | use OneBundle\OneClass; 170 | use AnotherBundle\AnotherClass; 171 | 172 | use Symfony\OneClass; 173 | use Symfony\AnotherClass; 174 | ``` 175 | 176 | As you can see, a blank line is placed between groups. If any group is defined, 177 | one big group is created with all namespaces. 178 | 179 | When using a `.formatter.yml` you can also specify subgroups by entering an array 180 | 181 | ``` yml 182 | use-sort: 183 | group: 184 | - [Symfony\Component\HttpKernel, Symfony] 185 | - _main 186 | ``` 187 | 188 | This will create a Symfony group placing all `Symfony\Component\HttpKernel` classes on top. 189 | 190 | 191 | Finally, the `--group-type` defines if you want one `use` literal in every 192 | namespace line 193 | 194 | ``` bash 195 | $ php-formatter formatter:use:sort src/ --group="Mmoreram" --group-type="each" 196 | ``` 197 | 198 | This command will sort the code like this 199 | 200 | ``` php 201 | use AnotherBundle\AnotherClass; 202 | use OneBundle\OneClass; 203 | use Symfony\AnotherClass; 204 | use Symfony\OneClass; 205 | 206 | use Mmoreram\MyClass; 207 | use Mmoreram\MySecondClass; 208 | ``` 209 | 210 | or if you want only one use for all groups. 211 | 212 | ``` bash 213 | $ php-formatter formatter:use:sort src/ --group="Mmoreram" --group-type="one" 214 | ``` 215 | 216 | This command will sort the code like this 217 | 218 | ``` php 219 | use AnotherBundle\AnotherClass, 220 | OneBundle\OneClass, 221 | Symfony\AnotherClass, 222 | Symfony\OneClass; 223 | 224 | use Mmoreram\MyClass, 225 | Mmoreram\MySecondClass; 226 | ``` 227 | 228 | #### Sort 229 | 230 | There are two options of sorting. You can sort your namespaces alphabetically 231 | ***(default value)*** 232 | 233 | ``` bash 234 | $ php-formatter formatter:use:sort src/ --sort-type="alph" 235 | ``` 236 | 237 | This command will sort the code like this 238 | 239 | ``` php 240 | use AnotherBundle\AnotherClass; 241 | use Mmoreram\MyClass; 242 | use Mmoreram\MySecondClass; 243 | use OneBundle\OneClass; 244 | use Symfony\AnotherClass; 245 | use Symfony\OneClass; 246 | ``` 247 | 248 | or by length (two namespaces with same length will be sorted alphabetically) 249 | 250 | ``` bash 251 | $ php-formatter formatter:use:sort src/ --sort-type="length" 252 | ``` 253 | 254 | This command will sort the code like this 255 | 256 | ``` php 257 | use AnotherBundle\AnotherClass; 258 | use Mmoreram\MySecondClass; 259 | use Symfony\AnotherClass; 260 | use OneBundle\OneClass; 261 | use Mmoreram\MyClass; 262 | use Symfony\OneClass; 263 | ``` 264 | 265 | You can also define the direction of the sorting. This can be ascending 266 | ***(default value)*** 267 | 268 | ``` bash 269 | $ php-formatter formatter:use:sort src/ --sort-direction="asc" 270 | ``` 271 | 272 | This command will sort the code like this 273 | 274 | ``` php 275 | use AnotherBundle\AnotherClass; 276 | use Mmoreram\MyClass; 277 | use Mmoreram\MySecondClass; 278 | use OneBundle\OneClass; 279 | use Symfony\AnotherClass; 280 | use Symfony\OneClass; 281 | ``` 282 | 283 | or descending 284 | 285 | ``` bash 286 | $ php-formatter formatter:use:sort src/ --sort-direction="desc" 287 | ``` 288 | 289 | This command will sort the code like this 290 | 291 | ``` php 292 | use Symfony\OneClass; 293 | use Symfony\AnotherClass; 294 | use OneBundle\OneClass; 295 | use Mmoreram\MySecondClass; 296 | use Mmoreram\MyClass; 297 | use AnotherBundle\AnotherClass; 298 | ``` 299 | 300 | ### Fix all PHP headers 301 | 302 | You can define your PHP header in your `.formatter.yml` file and this command 303 | will check and fix that all PHP files have it properly. 304 | 305 | * command: `php-formatter formatter:header:fix` 306 | * argument: path 307 | * option: --exclude [***multiple***] 308 | * option: --dry-run [***no value***] 309 | 310 | ### Fix all strict declarations 311 | 312 | In your >=7.0 PHP applications you can use simple type declarations in your 313 | methods. You can define your application as relaxed as you want by declaring the 314 | `strict_mode` variable in your files. Each php file must be configured itself, 315 | so in order to make sure all your files have the definition after the header if 316 | exists and before the namespace declaration, you can use this command. 317 | 318 | * command: `php-formatter formatter:strict:fix` 319 | * argument: path 320 | * option: --exclude [***multiple***] 321 | * option: --dry-run [***no value***] 322 | 323 | You can have three values here. If you define a boolean, then each file found 324 | will have the declaration with the boolean given. 325 | 326 | ``` yml 327 | strict: true 328 | ``` 329 | 330 | Otherwise, if you define a '~' value then your declaration lines will be 331 | removed. 332 | 333 | ``` yml 334 | strict: '~' 335 | ``` 336 | 337 | ## Exclude folders/files 338 | 339 | You can exclude folders and files by using the multi-option `--exclude` as many 340 | times as you need. This option works the same way the Symfony component 341 | [Finder](http://symfony.com/doc/current/components/finder.html) works, so to 342 | make sure you properly understand the way this option works, just check the 343 | documentation. 344 | 345 | ``` bash 346 | $ php-formatter formatter:header:fix src/ --exclude="vendor" 347 | ``` 348 | 349 | In that case, maybe the most used way, you will exclude all vendors from your 350 | process. 351 | 352 | ## Dry run 353 | 354 | You can use this tool just to test the files will be modified, using option 355 | --dry-run 356 | 357 | ``` bash 358 | $ php-formatter formatter:use:sort src/ --dry-run 359 | ``` 360 | 361 | Any command from this library will never have any impact in your code as long as 362 | you have defined this option. -------------------------------------------------------------------------------- /bin/php-formatter: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 15 | */ 16 | 17 | declare(strict_types=1); 18 | 19 | require __DIR__ . '/../src/bootstrap.php'; 20 | 21 | use Mmoreram\PHPFormatter\Console\Application; 22 | 23 | error_reporting(-1); 24 | ini_set('display_errors', '1'); 25 | 26 | // run the command application 27 | $application = new Application(); 28 | $application->run(); -------------------------------------------------------------------------------- /box.json: -------------------------------------------------------------------------------- 1 | { 2 | "algorithm": "SHA1", 3 | "alias": "php-formatter.phar", 4 | "banner": "This file is part of the php-formatter package\n\nCopyright (c) >=2014 Marc Morera\n\nFor the full copyright and license information, please view the LICENSE\nfile that was distributed with this source code.\n\nFeel free to edit as you please, and have fun.\n\n@author Marc Morera ", 5 | "chmod": "0755", 6 | "compactors": [ 7 | "Herrera\\Box\\Compactor\\Json", 8 | "Herrera\\Box\\Compactor\\Php" 9 | ], 10 | "directories": "src", 11 | "extract": true, 12 | "files": [ 13 | "LICENSE", 14 | "vendor/autoload.php", 15 | "vendor/composer/installed.json", 16 | "vendor/composer/LICENSE" 17 | ], 18 | "finder": [ 19 | { 20 | "exclude": [ 21 | "tests", 22 | "Tests" 23 | ], 24 | "in": [ 25 | "vendor/symfony", 26 | "vendor/composer" 27 | ], 28 | "name": "*.php" 29 | } 30 | ], 31 | "git-version": "package_version", 32 | "main": "bin/php-formatter", 33 | "output": "build/php-formatter.phar", 34 | "stub": true 35 | } 36 | -------------------------------------------------------------------------------- /build/php-formatter.phar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmoreram/php-formatter/1fa3d9a3e1c67654f3fe2511bd28fa7c33b59d7e/build/php-formatter.phar -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mmoreram/php-formatter", 3 | "type": "library", 4 | "description": "PHP custom formatter", 5 | "keywords": [ 6 | "php", "formatter" 7 | ], 8 | "license": "MIT", 9 | "authors": [ 10 | { 11 | "name": "Marc Morera", 12 | "email": "yuhu@mmoreram.com" 13 | } 14 | ], 15 | "require": { 16 | "php": ">=7.0", 17 | "symfony/console": "^3.2||^4.0", 18 | "symfony/process": "^3.2||^4.0", 19 | "symfony/finder": "^3.2||^4.0", 20 | "symfony/filesystem": "^3.2||^4.0", 21 | "symfony/yaml": "^3.2||^4.0", 22 | "symfony/event-dispatcher": "^3.2||^4.0", 23 | "symfony/property-access": "^3.2||^4.0" 24 | }, 25 | "require-dev": { 26 | "friendsofphp/php-cs-fixer": "^2.8.3", 27 | "phpunit/phpunit": "^5.6.4" 28 | }, 29 | "bin": [ 30 | "bin/php-formatter" 31 | ], 32 | "autoload": { 33 | "psr-4": {"Mmoreram\\PHPFormatter\\": "src/PHPFormatter"} 34 | }, 35 | "autoload-dev": { 36 | "psr-4": {"Mmoreram\\PHPFormatter\\Tests\\": "tests/PHPFormatter"} 37 | }, 38 | "scripts": { 39 | "fix-code": [ 40 | "vendor/bin/php-cs-fixer fix --config-file=.php_cs", 41 | "bin/php-formatter f:h:f . --exclude=vendor --verbose", 42 | "bin/php-formatter f:s:f . --exclude=vendor --verbose", 43 | "bin/php-formatter f:u:s . --exclude=vendor --verbose" 44 | ], 45 | "test": "vendor/bin/phpunit" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ./tests/ 22 | 23 | 24 | 25 | 26 | 27 | src 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/PHPFormatter/Command/HeaderCommand.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Command; 19 | 20 | use Symfony\Component\Console\Input\InputInterface; 21 | use Symfony\Component\Console\Output\OutputInterface; 22 | 23 | use Mmoreram\PHPFormatter\Fixer\HeaderFixer; 24 | use Mmoreram\PHPFormatter\Fixer\Interfaces\FixerInterface; 25 | 26 | /** 27 | * Class HeaderCommand. 28 | */ 29 | final class HeaderCommand extends PHPFormatterCommand 30 | { 31 | /** 32 | * Get command alias for configuration. 33 | * 34 | * @return string 35 | */ 36 | protected function getCommandConfigAlias() : string 37 | { 38 | return 'header'; 39 | } 40 | 41 | /** 42 | * configure. 43 | */ 44 | protected function configure() 45 | { 46 | $this 47 | ->setName('formatter:header:fix') 48 | ->setDescription('Ensures that all PHP files have the header defined in the config file'); 49 | 50 | parent::configure(); 51 | } 52 | 53 | /** 54 | * Print used config. 55 | * 56 | * @param OutputInterface $output 57 | * @param mixed $config 58 | */ 59 | protected function printUsableConfig( 60 | OutputInterface $output, 61 | $config 62 | ) { 63 | $output->writeln("# Header used:\n\n" . $config); 64 | } 65 | 66 | /** 67 | * Get a fixer instance given the configuration. 68 | * 69 | * @param mixed $config 70 | * 71 | * @return FixerInterface 72 | */ 73 | protected function getFixer($config) : FixerInterface 74 | { 75 | return new HeaderFixer($config); 76 | } 77 | 78 | /** 79 | * Get command config values. 80 | * 81 | * @param InputInterface $input 82 | * 83 | * @return mixed 84 | */ 85 | protected function getCommandConfigValue(InputInterface $input) 86 | { 87 | return null; 88 | } 89 | 90 | /** 91 | * Get default config values. 92 | * 93 | * @return mixed 94 | */ 95 | protected function getDefaultConfigValue() 96 | { 97 | return null; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/PHPFormatter/Command/PHPFormatterCommand.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Command; 19 | 20 | use Exception; 21 | use Symfony\Component\Console\Command\Command; 22 | use Symfony\Component\Console\Input\InputArgument; 23 | use Symfony\Component\Console\Input\InputInterface; 24 | use Symfony\Component\Console\Input\InputOption; 25 | use Symfony\Component\Console\Output\OutputInterface; 26 | use Symfony\Component\Filesystem\Filesystem; 27 | use Symfony\Component\Finder\Finder; 28 | use Symfony\Component\Finder\SplFileInfo; 29 | 30 | use Mmoreram\PHPFormatter\Finder\ConfigFinder; 31 | use Mmoreram\PHPFormatter\Finder\FileFinder; 32 | use Mmoreram\PHPFormatter\Fixer\Interfaces\FixerInterface; 33 | use Mmoreram\PHPFormatter\Loader\ConfigLoader; 34 | 35 | /** 36 | * Class PHPFormatterCommand. 37 | */ 38 | abstract class PHPFormatterCommand extends Command 39 | { 40 | /** 41 | * configure. 42 | */ 43 | protected function configure() 44 | { 45 | $this 46 | ->addArgument( 47 | 'path', 48 | InputArgument::REQUIRED, 49 | 'Path' 50 | ) 51 | ->addOption( 52 | '--exclude', 53 | '-e', 54 | InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 55 | 'Excluded folders', 56 | [] 57 | ) 58 | ->addOption( 59 | '--config', 60 | '-c', 61 | InputOption::VALUE_OPTIONAL, 62 | 'Config file directory', 63 | getcwd() 64 | ) 65 | ->addOption( 66 | 'dry-run', 67 | null, 68 | InputOption::VALUE_NONE, 69 | 'Just print the result, nothing is overwritten' 70 | ); 71 | } 72 | 73 | /** 74 | * Execute command. 75 | * 76 | * @param InputInterface $input Input 77 | * @param OutputInterface $output Output 78 | * 79 | * @return int|null|void 80 | * 81 | * @throws Exception 82 | */ 83 | protected function execute(InputInterface $input, OutputInterface $output) 84 | { 85 | $path = $input->getArgument('path'); 86 | $excludes = $input->getOption('exclude'); 87 | $verbose = $output->getVerbosity(); 88 | $configValue = $this->getUsableConfig($input); 89 | 90 | if ($verbose >= OutputInterface::VERBOSITY_VERBOSE) { 91 | $this 92 | ->printInitMessage( 93 | $input, 94 | $output, 95 | $path, 96 | $excludes 97 | ); 98 | $output->writeln('#'); 99 | $output->writeln('# Executing process with this configuration'); 100 | $this->printUsableConfig($output, $configValue); 101 | } 102 | 103 | $files = $this 104 | ->loadFiles( 105 | $path, 106 | $excludes 107 | ); 108 | 109 | if ($verbose >= OutputInterface::VERBOSITY_VERBOSE) { 110 | $output->writeln('# Found ' . $files->count() . ' files'); 111 | $output->writeln('# Starting format...'); 112 | $output->writeln('#'); 113 | } 114 | 115 | $fixer = $this->getFixer($configValue); 116 | $this->fixFiles( 117 | $input, 118 | $output, 119 | $files, 120 | $fixer 121 | ); 122 | } 123 | 124 | /** 125 | * Get a fixer instance given the configuration. 126 | * 127 | * @param mixed $config 128 | * 129 | * @return FixerInterface 130 | */ 131 | abstract protected function getFixer($config) : FixerInterface; 132 | 133 | /** 134 | * Get command alias for configuration. 135 | * 136 | * @return string 137 | */ 138 | abstract protected function getCommandConfigAlias() : string; 139 | 140 | /** 141 | * Print the Dry-run message if needed. 142 | * 143 | * @param InputInterface $input 144 | * @param OutputInterface $output 145 | * @param string $path 146 | * @param array $excludes 147 | */ 148 | protected function printInitMessage( 149 | InputInterface $input, 150 | OutputInterface $output, 151 | string $path, 152 | array $excludes 153 | ) { 154 | $output->writeln('# PHP Formatter'); 155 | 156 | /* 157 | * Dry-run message 158 | */ 159 | if ($input->getOption('dry-run')) { 160 | $output->writeln('# This process has been executed in mode dry-run'); 161 | } 162 | 163 | $output->writeln('# Executing process in ' . $path); 164 | foreach ($excludes as $exclude) { 165 | $output->writeln('# Path excluded - ' . $exclude); 166 | } 167 | } 168 | 169 | /** 170 | * Load config values from configuration files and/or the command execution. 171 | * 172 | * @param InputInterface $input Input 173 | * 174 | * @return mixed 175 | * 176 | * @throws Exception 177 | */ 178 | protected function getUsableConfig(InputInterface $input) 179 | { 180 | $configLoader = new ConfigLoader(); 181 | $configFinder = new ConfigFinder(); 182 | 183 | /** 184 | * This section is just for finding the right values to work with in 185 | * this execution. 186 | * 187 | * $options array will have, after this block, all these values 188 | */ 189 | $configPath = rtrim($input->getOption('config'), DIRECTORY_SEPARATOR); 190 | 191 | $configValue = $configLoader->loadConfigValues( 192 | $this->getCommandConfigAlias(), 193 | $configFinder->findConfigFile($configPath), 194 | $this->getCommandConfigValue($input), 195 | $this->getDefaultConfigValue() 196 | 197 | ); 198 | 199 | if (is_null($configValue)) { 200 | throw new Exception("Config definition must be defined in .formatter.yml file under {$this->getCommandConfigAlias()}"); 201 | } 202 | 203 | return $configValue; 204 | } 205 | 206 | /** 207 | * Print used config. 208 | * 209 | * @param OutputInterface $output 210 | * @param mixed $config 211 | */ 212 | protected function printUsableConfig( 213 | OutputInterface $output, 214 | $config 215 | ) { 216 | } 217 | 218 | /** 219 | * Load files to work with. 220 | * 221 | * @param string $path 222 | * @param array $excludes 223 | * 224 | * @return Finder|SplFileInfo[] 225 | * 226 | * @throws Exception 227 | */ 228 | protected function loadFiles( 229 | string $path, 230 | array $excludes 231 | ) { 232 | $fileFinder = new FileFinder(); 233 | 234 | /** 235 | * Building the real directory or file to work in. 236 | */ 237 | $filesystem = new Filesystem(); 238 | if (!$filesystem->isAbsolutePath($path)) { 239 | $path = getcwd() . DIRECTORY_SEPARATOR . ltrim($path, '/'); 240 | } 241 | 242 | if (!is_file($path) && !is_dir($path)) { 243 | throw new Exception('Directory or file "' . $path . '" does not exist'); 244 | } 245 | 246 | return $fileFinder 247 | ->findPHPFilesByPath( 248 | $path, 249 | $excludes 250 | ); 251 | } 252 | 253 | /** 254 | * Fix files with given fixer. 255 | * 256 | * @param InputInterface $input 257 | * @param OutputInterface $output 258 | * @param Finder $files 259 | * @param FixerInterface $fixer 260 | */ 261 | protected function fixFiles( 262 | InputInterface $input, 263 | OutputInterface $output, 264 | Finder $files, 265 | FixerInterface $fixer 266 | ) { 267 | $verbose = $output->getVerbosity(); 268 | 269 | /* 270 | * Each found php file is processed 271 | */ 272 | foreach ($files as $file) { 273 | $data = $file->getContents(); 274 | $result = $fixer->fix($data); 275 | 276 | if ($result === false || $data === $result) { 277 | continue; 278 | } 279 | 280 | if ($verbose >= OutputInterface::VERBOSITY_NORMAL) { 281 | $output->writeln('# ' . $file); 282 | } 283 | 284 | if (!$input->getOption('dry-run')) { 285 | file_put_contents($file->getRealPath(), $result); 286 | } 287 | } 288 | } 289 | 290 | /** 291 | * Get command config values. 292 | * 293 | * @param InputInterface $input 294 | * 295 | * @return mixed 296 | */ 297 | abstract protected function getCommandConfigValue(InputInterface $input); 298 | 299 | /** 300 | * Get default config values. 301 | * 302 | * @return mixed 303 | */ 304 | abstract protected function getDefaultConfigValue(); 305 | } 306 | -------------------------------------------------------------------------------- /src/PHPFormatter/Command/StrictCommand.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Command; 19 | 20 | use Symfony\Component\Console\Input\InputInterface; 21 | use Symfony\Component\Console\Output\OutputInterface; 22 | 23 | use Mmoreram\PHPFormatter\Fixer\Interfaces\FixerInterface; 24 | use Mmoreram\PHPFormatter\Fixer\StrictFixer; 25 | 26 | /** 27 | * Class StrictCommand. 28 | */ 29 | final class StrictCommand extends PHPFormatterCommand 30 | { 31 | /** 32 | * Get command alias for configuration. 33 | * 34 | * @return string 35 | */ 36 | protected function getCommandConfigAlias() : string 37 | { 38 | return 'strict'; 39 | } 40 | 41 | /** 42 | * configure. 43 | */ 44 | protected function configure() 45 | { 46 | $this 47 | ->setName('formatter:strict:fix') 48 | ->setDescription('Ensures that all PHP files have strict mode defined in config file. Only valid for >=PHP7.0'); 49 | 50 | parent::configure(); 51 | } 52 | 53 | /** 54 | * Print used config. 55 | * 56 | * @param OutputInterface $output 57 | * @param mixed $config 58 | */ 59 | protected function printUsableConfig( 60 | OutputInterface $output, 61 | $config 62 | ) { 63 | if (is_bool($config)) { 64 | $output->writeln('# Adding strict_mode=' . ($config ? '1' : '0') . ' in your files'); 65 | } else { 66 | $output->writeln('# Removing strict_mode from your files'); 67 | } 68 | } 69 | 70 | /** 71 | * Get a fixer instance given the configuration. 72 | * 73 | * @param mixed $config 74 | * 75 | * @return FixerInterface 76 | */ 77 | protected function getFixer($config) : FixerInterface 78 | { 79 | return new StrictFixer($config); 80 | } 81 | 82 | /** 83 | * Get command config values. 84 | * 85 | * @param InputInterface $input 86 | * 87 | * @return mixed 88 | */ 89 | protected function getCommandConfigValue(InputInterface $input) 90 | { 91 | return null; 92 | } 93 | 94 | /** 95 | * Get default config values. 96 | * 97 | * @return mixed 98 | */ 99 | protected function getDefaultConfigValue() 100 | { 101 | return null; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/PHPFormatter/Command/UseSortCommand.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Command; 19 | 20 | use Symfony\Component\Console\Input\InputInterface; 21 | use Symfony\Component\Console\Input\InputOption; 22 | use Symfony\Component\Console\Output\OutputInterface; 23 | 24 | use Mmoreram\PHPFormatter\Fixer\Interfaces\FixerInterface; 25 | use Mmoreram\PHPFormatter\Fixer\UseSortFixer; 26 | 27 | /** 28 | * Class UseSortCommand. 29 | */ 30 | final class UseSortCommand extends PHPFormatterCommand 31 | { 32 | /** 33 | * Get command alias for configuration. 34 | * 35 | * @return string 36 | */ 37 | protected function getCommandConfigAlias() : string 38 | { 39 | return 'use-sort'; 40 | } 41 | 42 | /** 43 | * configure. 44 | */ 45 | protected function configure() 46 | { 47 | $this 48 | ->setName('formatter:use:sort') 49 | ->setDescription('Sort Use statements') 50 | ->addOption( 51 | 'group', 52 | null, 53 | InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 54 | 'Groups defined?' 55 | ) 56 | ->addOption( 57 | 'sort-type', 58 | null, 59 | InputOption::VALUE_OPTIONAL, 60 | 'Sort type' 61 | ) 62 | ->addOption( 63 | 'sort-direction', 64 | null, 65 | InputOption::VALUE_OPTIONAL, 66 | 'Sort direction' 67 | ) 68 | ->addOption( 69 | 'group-type', 70 | null, 71 | InputOption::VALUE_OPTIONAL, 72 | 'Type of grouping' 73 | ) 74 | ->addOption( 75 | 'group-skip-empty', 76 | null, 77 | InputOption::VALUE_NONE, 78 | 'Skip empty groups' 79 | ); 80 | 81 | parent::configure(); 82 | } 83 | 84 | /** 85 | * Get command config values. 86 | * 87 | * @param InputInterface $input 88 | * 89 | * @return mixed 90 | */ 91 | protected function getCommandConfigValue(InputInterface $input) 92 | { 93 | $config = []; 94 | if (!empty($input->getOption('group'))) { 95 | $config['group'] = $input->getOption('group'); 96 | } 97 | 98 | if (true === ($input->getOption('group-skip-empty'))) { 99 | $config['group-skip-empty'] = true; 100 | } 101 | 102 | return array_merge( 103 | $config, 104 | array_filter([ 105 | 'group-type' => $input->getOption('group-type'), 106 | 'sort-type' => $input->getOption('sort-type'), 107 | 'sort-direction' => $input->getOption('sort-direction'), 108 | ], function ($element) { 109 | return !is_null($element); 110 | }) 111 | ); 112 | } 113 | 114 | /** 115 | * Get default config values. 116 | * 117 | * @return mixed 118 | */ 119 | protected function getDefaultConfigValue() 120 | { 121 | return [ 122 | 'group' => ['_main'], 123 | 'group-type' => UseSortFixer::GROUP_TYPE_EACH, 124 | 'group-skip-empty' => false, 125 | 'sort-type' => UseSortFixer::SORT_TYPE_ALPHABETIC, 126 | 'sort-direction' => UseSortFixer::SORT_DIRECTION_ASC, 127 | ]; 128 | } 129 | 130 | /** 131 | * Print used config. 132 | * 133 | * @param OutputInterface $output 134 | * @param mixed $config 135 | */ 136 | protected function printUsableConfig( 137 | OutputInterface $output, 138 | $config 139 | ) { 140 | foreach ($config['group'] as $group) { 141 | $output->writeln('# --group="' . $group . '"'); 142 | } 143 | $output->writeln('# --group-type="' . $config['group-type'] . '"'); 144 | $output->writeln('# --group-skip-empty="' . ($config['group-skip-empty'] ? 'true' : 'false') . '"'); 145 | $output->writeln('# --sort-type="' . $config['sort-type'] . '"'); 146 | $output->writeln('# --sort-direction="' . $config['sort-direction'] . '"'); 147 | } 148 | 149 | /** 150 | * Get a fixer instance given the configuration. 151 | * 152 | * @param mixed $config 153 | * 154 | * @return FixerInterface 155 | */ 156 | protected function getFixer($config) : FixerInterface 157 | { 158 | $useSortFixer = new UseSortFixer(); 159 | $useSortFixer 160 | ->setGroups($config['group']) 161 | ->setGroupType($config['group-type']) 162 | ->setGroupSkipEmpty($config['group-skip-empty']) 163 | ->setSortType($config['sort-type']) 164 | ->setSortDirection($config['sort-direction']); 165 | 166 | return $useSortFixer; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/PHPFormatter/Console/Application.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Console; 19 | 20 | use Symfony\Component\Console\Application as BaseApplication; 21 | 22 | use Mmoreram\PHPFormatter\Command; 23 | 24 | /** 25 | * Class Application. 26 | */ 27 | class Application extends BaseApplication 28 | { 29 | /** 30 | * Construct method. 31 | */ 32 | public function __construct() 33 | { 34 | if (function_exists('ini_set') && extension_loaded('xdebug')) { 35 | ini_set('xdebug.show_exception_trace', '0'); 36 | ini_set('xdebug.scream', '0'); 37 | } 38 | 39 | if (function_exists('date_default_timezone_set') && function_exists('date_default_timezone_get')) { 40 | date_default_timezone_set(@date_default_timezone_get()); 41 | } 42 | 43 | parent::__construct('PHPFormatter'); 44 | } 45 | 46 | /** 47 | * Initializes all the composer commands. 48 | */ 49 | protected function getDefaultCommands() 50 | { 51 | $commands = parent::getDefaultCommands(); 52 | $commands[] = new Command\UseSortCommand(); 53 | $commands[] = new Command\HeaderCommand(); 54 | $commands[] = new Command\StrictCommand(); 55 | 56 | return $commands; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/PHPFormatter/Finder/ConfigFinder.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Finder; 19 | 20 | use Symfony\Component\Yaml\Yaml; 21 | 22 | use Mmoreram\PHPFormatter\PHPFormatter; 23 | 24 | /** 25 | * Class ConfigFinder. 26 | */ 27 | class ConfigFinder 28 | { 29 | /** 30 | * Load, if exists, specific project configuration. 31 | * 32 | * @param string $path Path 33 | * 34 | * @return array loaded config 35 | */ 36 | public function findConfigFile($path) 37 | { 38 | $configFilePath = rtrim($path, '/') . '/' . PHPFormatter::CONFIG_FILE_NAME; 39 | $config = []; 40 | if (is_file($configFilePath)) { 41 | $yamlParser = new Yaml(); 42 | $config = $yamlParser->parse(file_get_contents($configFilePath)); 43 | } 44 | 45 | return $config; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/PHPFormatter/Finder/FileFinder.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Finder; 19 | 20 | use Symfony\Component\Finder\Finder; 21 | use Symfony\Component\Finder\SplFileInfo; 22 | 23 | /** 24 | * Class FileFinder. 25 | */ 26 | class FileFinder 27 | { 28 | /** 29 | * Find all php files by path. 30 | * 31 | * @param string $path 32 | * @param string[] $excludes 33 | * 34 | * @return Finder|SplFileInfo[] 35 | */ 36 | public function findPHPFilesByPath( 37 | string $path, 38 | array $excludes 39 | ) { 40 | $finder = new Finder(); 41 | 42 | if (file_exists($path) && !is_dir($path)) { 43 | $finder->append([0 => $path]); 44 | } else { 45 | $finder 46 | ->files() 47 | ->in($path) 48 | ->exclude($excludes) 49 | ->name('*.php'); 50 | } 51 | 52 | return $finder; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/PHPFormatter/Fixer/HeaderFixer.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Fixer; 19 | 20 | use Mmoreram\PHPFormatter\Fixer\Interfaces\FixerInterface; 21 | 22 | /** 23 | * Class HeaderFixer. 24 | */ 25 | class HeaderFixer implements FixerInterface 26 | { 27 | /** 28 | * @var string 29 | * 30 | * Header 31 | */ 32 | private $header; 33 | 34 | /** 35 | * Construct method. 36 | * 37 | * @param string $header Header 38 | */ 39 | public function __construct($header) 40 | { 41 | $this->header = '^\s*<\?php(?:(?:(?:/\*.*?\*/)|(?:(?://|#).*?\n{1})|(?:\s*))*))(?P.*)~s'; 54 | preg_match($regex, $data, $results); 55 | 56 | if (!isset($results['group'])) { 57 | return false; 58 | } 59 | 60 | $other = $results['other']; 61 | $fixedData = $this->header . ltrim($other); 62 | 63 | return $fixedData !== $data 64 | ? $fixedData 65 | : false; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/PHPFormatter/Fixer/Interfaces/FixerInterface.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Fixer\Interfaces; 19 | 20 | /** 21 | * Interface FixerInterface. 22 | */ 23 | interface FixerInterface 24 | { 25 | /** 26 | * Do the fix. Return the fixed code or false if the code has not changed. 27 | * 28 | * @param string $data 29 | * 30 | * @return string|false 31 | */ 32 | public function fix($data); 33 | } 34 | -------------------------------------------------------------------------------- /src/PHPFormatter/Fixer/StrictFixer.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Fixer; 19 | 20 | use Mmoreram\PHPFormatter\Fixer\Interfaces\FixerInterface; 21 | 22 | /** 23 | * Class StrictFixer. 24 | */ 25 | final class StrictFixer implements FixerInterface 26 | { 27 | /** 28 | * @var string 29 | * 30 | * Strict 31 | */ 32 | private $strict; 33 | 34 | /** 35 | * Construct method. 36 | * 37 | * @param bool $strict 38 | */ 39 | public function __construct($strict) 40 | { 41 | if (is_bool($strict)) { 42 | $this->strict = $strict 43 | ? 'declare(strict_types=1);' 44 | : 'declare(strict_types=0);'; 45 | } 46 | } 47 | 48 | /** 49 | * Do the fix. Return the fixed code or false if the code has not changed. 50 | * 51 | * @param string $data 52 | * 53 | * @return string|false 54 | */ 55 | public function fix($data) 56 | { 57 | $regex = '~(?P
^\s*<\?php(?:(?:(?:/\*.*?\*/)|(?:(?://|#).*?\n{1})|(?:\s*))*))(?P\s*declare\(\s*strict_types\s*=\s*[01]{1}\s*\)\s*;\s*\n*)?(?P.*)~s'; 58 | preg_match($regex, $data, $results); 59 | 60 | if (!isset($results['header'])) { 61 | return false; 62 | } 63 | 64 | $header = $results['header']; 65 | $other = isset($results['other']) ? $results['other'] : ''; 66 | 67 | $fixedData = trim($header) . rtrim("\n\n" . $this->strict . "\n\n") . "\n\n" . ltrim($other); 68 | 69 | return $fixedData !== $data 70 | ? $fixedData 71 | : false; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/PHPFormatter/Fixer/UseSortFixer.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Fixer; 19 | 20 | use Mmoreram\PHPFormatter\Fixer\Interfaces\FixerInterface; 21 | 22 | /** 23 | * Class UseSortFixer. 24 | */ 25 | final class UseSortFixer implements FixerInterface 26 | { 27 | /** 28 | * @var int 29 | * 30 | * Sort type Alphabetic 31 | */ 32 | const SORT_TYPE_ALPHABETIC = 'alph'; 33 | 34 | /** 35 | * @var int 36 | * 37 | * Sort type length 38 | */ 39 | const SORT_TYPE_LENGTH = 'length'; 40 | 41 | /** 42 | * @var int 43 | * 44 | * Sort direction ascendent 45 | */ 46 | const SORT_DIRECTION_ASC = 'asc'; 47 | 48 | /** 49 | * @var int 50 | * 51 | * Sort direction descendent 52 | */ 53 | const SORT_DIRECTION_DESC = 'desc'; 54 | 55 | /** 56 | * @var int 57 | * 58 | * Group type one USE 59 | */ 60 | const GROUP_TYPE_ONE = 'one'; 61 | 62 | /** 63 | * @var int 64 | * 65 | * Group type each USE 66 | */ 67 | const GROUP_TYPE_EACH = 'each'; 68 | 69 | /** 70 | * @var 71 | */ 72 | 73 | /** 74 | * @var array 75 | * 76 | * Groups 77 | */ 78 | private $groups = []; 79 | 80 | /** 81 | * @var int 82 | * 83 | * Sort type 84 | */ 85 | private $sortType = self::SORT_TYPE_ALPHABETIC; 86 | 87 | /** 88 | * @var int 89 | * 90 | * Sort direction 91 | */ 92 | private $sortDirection = self::SORT_DIRECTION_ASC; 93 | 94 | /** 95 | * @var int 96 | * 97 | * Group type 98 | */ 99 | private $groupType = self::GROUP_TYPE_EACH; 100 | 101 | /** 102 | * @var bool 103 | * 104 | * Skip empty groups 105 | */ 106 | private $groupSkipEmpty = false; 107 | 108 | /** 109 | * Sets Groups. 110 | * 111 | * @param array $groups 112 | * 113 | * @return UseSortFixer 114 | */ 115 | public function setGroups($groups) : UseSortFixer 116 | { 117 | $this->groups = $groups; 118 | 119 | return $this; 120 | } 121 | 122 | /** 123 | * Sets SortDirection. 124 | * 125 | * @param mixed $sortDirection 126 | * 127 | * @return UseSortFixer 128 | */ 129 | public function setSortDirection($sortDirection) : UseSortFixer 130 | { 131 | $this->sortDirection = $sortDirection; 132 | 133 | return $this; 134 | } 135 | 136 | /** 137 | * Sets SortType. 138 | * 139 | * @param mixed $sortType 140 | * 141 | * @return UseSortFixer 142 | */ 143 | public function setSortType($sortType) : UseSortFixer 144 | { 145 | $this->sortType = $sortType; 146 | 147 | return $this; 148 | } 149 | 150 | /** 151 | * Sets GroupType. 152 | * 153 | * @param int $groupType 154 | * 155 | * @return UseSortFixer 156 | */ 157 | public function setGroupType($groupType) : UseSortFixer 158 | { 159 | $this->groupType = $groupType; 160 | 161 | return $this; 162 | } 163 | 164 | /** 165 | * Sets GroupSkipEmpty. 166 | * 167 | * @param bool $groupSkipEmpty 168 | * 169 | * @return UseSortFixer 170 | */ 171 | public function setGroupSkipEmpty($groupSkipEmpty) : UseSortFixer 172 | { 173 | $this->groupSkipEmpty = $groupSkipEmpty; 174 | 175 | return $this; 176 | } 177 | 178 | /** 179 | * Do the fix. Return the fixed code or false if the code has not changed. 180 | * 181 | * @param string $data 182 | * 183 | * @return string|false 184 | */ 185 | public function fix($data) 186 | { 187 | $regex = '/(\s*(?:(?:\s+use\s+?[\w\\\\\,\s]+?;)+)\s+)/s'; 188 | preg_match($regex, $data, $results); 189 | 190 | if (!isset($results[0])) { 191 | return false; 192 | } 193 | 194 | $result = $results[0]; 195 | $blocks = explode(';', $result); 196 | $namespaces = []; 197 | 198 | foreach ($blocks as $block) { 199 | 200 | /** 201 | * Removing use literal. 202 | */ 203 | $block = trim(preg_replace('/^\s+use\s+/', '', $block)); 204 | 205 | $namespaces = array_merge( 206 | $namespaces, 207 | explode(',', $block) 208 | ); 209 | } 210 | 211 | /** 212 | * Trim all results of blank spaces and line breaks. 213 | */ 214 | $namespaces = array_map(function ($namespace) { 215 | return trim($namespace); 216 | }, $namespaces); 217 | 218 | /** 219 | * If any position becomes empty, removes. 220 | */ 221 | $namespaces = array_filter($namespaces, function ($namespace) { 222 | return !empty($namespace); 223 | }); 224 | 225 | /** 226 | * Grouping use statements by blocks defined in blocks variable. 227 | */ 228 | $groups = $this->createGroups($namespaces); 229 | 230 | /* 231 | * Every block is sorted as desired 232 | */ 233 | foreach ($groups as $groupKey => $group) { 234 | if (is_int($groupKey)) { 235 | $subGroupSorted = []; 236 | foreach ($group as $subGroupKey => $subGroup) { 237 | $subGroupSorted = array_merge($subGroupSorted, $this->sortGroup($subGroup)); 238 | } 239 | 240 | $groups[$groupKey] = $subGroupSorted; 241 | } else { 242 | $groups[$groupKey] = $this->sortGroup($group); 243 | } 244 | 245 | // Remove empty groups (if configured) after the sorting has happened. 246 | // @see https://github.com/mmoreram/php-formatter/issues/24 247 | if (0 === count($groups[$groupKey])) { 248 | unset($groups[$groupKey]); 249 | } 250 | } 251 | 252 | $doubleEOL = PHP_EOL . PHP_EOL; 253 | $spaceBetweenGroups = $this->groupSkipEmpty 254 | ? PHP_EOL 255 | : $doubleEOL; 256 | 257 | $processedResult = $doubleEOL . trim(implode($spaceBetweenGroups, array_map( 258 | function ($group) { 259 | return $this->renderGroup($group); 260 | }, $groups) 261 | )) . $doubleEOL; 262 | 263 | $fixedData = str_replace($result, $processedResult, $data); 264 | 265 | return $fixedData !== $data 266 | ? $fixedData 267 | : false; 268 | } 269 | 270 | /** 271 | * Create blocks. 272 | * 273 | * @param array $namespaces Namespaces 274 | * 275 | * @return array Groups 276 | */ 277 | private function createGroups(array $namespaces) 278 | { 279 | $groups = []; 280 | 281 | foreach ($this->groups as $group) { 282 | if (is_array($group)) { 283 | $groups[] = array_fill_keys($group, []); 284 | } else { 285 | $groups[$group] = []; 286 | } 287 | } 288 | 289 | if (!array_key_exists('_main', $groups)) { 290 | $groups = array_merge( 291 | ['_main' => []], 292 | $groups 293 | ); 294 | } 295 | 296 | foreach ($namespaces as $namespace) { 297 | foreach ($groups as $groupKey => $group) { 298 | if (is_int($groupKey)) { 299 | foreach ($group as $subGroupKey => $subGroup) { 300 | if (strpos($namespace, $subGroupKey) === 0) { 301 | array_push($groups[$groupKey][$subGroupKey], $namespace); 302 | 303 | continue 3; 304 | } 305 | } 306 | } elseif (is_string($groupKey) && strpos($namespace, $groupKey) === 0) { 307 | array_push($groups[$groupKey], $namespace); 308 | 309 | continue 2; 310 | } 311 | } 312 | 313 | array_push($groups['_main'], $namespace); 314 | } 315 | 316 | return $groups; 317 | } 318 | 319 | /** 320 | * Sort a group. 321 | * 322 | * @param array $group Group to sort 323 | * 324 | * @return array $group Sorted 325 | */ 326 | private function sortGroup(array $group) 327 | { 328 | if (empty($group)) { 329 | return []; 330 | } 331 | 332 | if ($this->sortType == self::SORT_TYPE_LENGTH) { 333 | usort($group, function ($a, $b) { 334 | $cmp = strlen($b) - strlen($a); 335 | 336 | if ($cmp === 0) { 337 | $a = strtolower($a); 338 | $b = strtolower($b); 339 | 340 | $cmp = strcmp($b, $a); 341 | } 342 | 343 | return $cmp; 344 | }); 345 | } elseif ($this->sortType == self::SORT_TYPE_ALPHABETIC) { 346 | usort($group, function ($a, $b) { 347 | $a = strtolower($a); 348 | $b = strtolower($b); 349 | 350 | $cmp = strcmp($b, $a); 351 | if ($cmp === 0) { 352 | $cmp = strlen($b) - strlen($a); 353 | } 354 | 355 | return $cmp; 356 | }); 357 | } 358 | 359 | if ($this->sortDirection == self::SORT_DIRECTION_ASC) { 360 | $group = array_reverse($group); 361 | } 362 | 363 | return $group; 364 | } 365 | 366 | /** 367 | * Render a group. 368 | * 369 | * @param array $group Group 370 | * 371 | * @return string Group rendered 372 | */ 373 | private function renderGroup(array $group) 374 | { 375 | if (empty($group)) { 376 | return ''; 377 | } 378 | if ($this->groupType === self::GROUP_TYPE_EACH) { 379 | return implode(PHP_EOL, array_map(function ($namespace) { 380 | return 'use ' . $namespace . ';'; 381 | }, $group)); 382 | } elseif ($this->groupType === self::GROUP_TYPE_ONE) { 383 | $group = implode(',' . PHP_EOL . ' ', $group); 384 | 385 | return 'use ' . $group . ';'; 386 | } 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /src/PHPFormatter/Loader/ConfigLoader.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Loader; 19 | 20 | /** 21 | * Class ConfigLoader. 22 | */ 23 | class ConfigLoader 24 | { 25 | /** 26 | * This method parses the config file, if exists, and determines the real 27 | * options values. 28 | * 29 | * * If an option is defined as a command parameter, this will be used 30 | * * Otherwise, if an option is defined in the configuration file, this will 31 | * be used. 32 | * * Otherwise, default values will be used 33 | * 34 | * @param string $commandName 35 | * @param mixed $configValues 36 | * @param mixed $commandValues 37 | * @param mixed $defaultValues 38 | * 39 | * @return array $usableValues Usable values 40 | * 41 | * If none of these is defined, then return null 42 | */ 43 | public function loadConfigValues( 44 | $commandName, 45 | $configValues, 46 | $commandValues, 47 | $defaultValues 48 | ) { 49 | if (!is_array($defaultValues)) { 50 | return $this->loadConfigValue( 51 | $commandName, 52 | $configValues, 53 | $commandValues, 54 | $defaultValues 55 | ); 56 | } 57 | 58 | $configValues = isset($configValues[$commandName]) && is_array($configValues[$commandName]) 59 | ? $configValues[$commandName] 60 | : []; 61 | 62 | $result = array_merge( 63 | $defaultValues, 64 | $configValues, 65 | $commandValues 66 | ); 67 | 68 | return !empty($result) 69 | ? $result 70 | : null; 71 | } 72 | 73 | /** 74 | * This method parses the config file, if exists, and determines the real 75 | * option value. 76 | * 77 | * * If an option is defined as a command parameter, this will be used 78 | * * Otherwise, if an option is defined in the configuration file, this will 79 | * be used. 80 | * * Otherwise, default values will be used 81 | * 82 | * If none of these is defined, then return null 83 | * 84 | * @param string $commandName 85 | * @param mixed $configValue 86 | * @param mixed $commandValue 87 | * @param mixed $defaultValue 88 | * 89 | * @return mixed|null 90 | */ 91 | public function loadConfigValue( 92 | $commandName, 93 | $configValue, 94 | $commandValue, 95 | $defaultValue 96 | ) { 97 | return array_key_exists($commandName, $configValue) 98 | ? ($configValue[$commandName] ?? '') 99 | : (!is_null($commandValue) 100 | ? $commandValue 101 | : $defaultValue); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/PHPFormatter/PHPFormatter.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter; 19 | 20 | /** 21 | * Class PHPFormatter. 22 | */ 23 | class PHPFormatter 24 | { 25 | /** 26 | * @var string 27 | * 28 | * Configuration file 29 | */ 30 | const CONFIG_FILE_NAME = '.formatter.yml'; 31 | } 32 | -------------------------------------------------------------------------------- /src/bootstrap.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | function includeIfExists($file) 19 | { 20 | return file_exists($file) ? include $file : false; 21 | } 22 | 23 | if ((!$loader = includeIfExists(__DIR__ . '/../vendor/autoload.php')) && (!$loader = includeIfExists(__DIR__ . '/../../../autoload.php'))) { 24 | echo 'You must set up the project dependencies, run the following commands:' . PHP_EOL . 25 | 'curl -sS https://getcomposer.org/installer | php' . PHP_EOL . 26 | 'php composer.phar install' . PHP_EOL; 27 | exit(1); 28 | } 29 | 30 | return $loader; 31 | -------------------------------------------------------------------------------- /tests/PHPFormatter/Finder/ConfigFinderTest.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Tests\Finder; 19 | 20 | use PHPUnit_Framework_TestCase; 21 | 22 | use Mmoreram\PHPFormatter\Finder\ConfigFinder; 23 | 24 | /** 25 | * Class ConfigFinderTest. 26 | */ 27 | class ConfigFinderTest extends PHPUnit_Framework_TestCase 28 | { 29 | /** 30 | * Test right load of config file. 31 | */ 32 | public function testFindConfigFileAndFound() 33 | { 34 | $path = dirname(__FILE__) . '/../Mocks/'; 35 | $fileFinder = new ConfigFinder(); 36 | $data = $fileFinder->findConfigFile($path); 37 | 38 | $this->assertEquals($data, [ 39 | 'use-sort' => [ 40 | 'group' => [ 41 | 'Symfony', 42 | '_main', 43 | 'Mmoreram', 44 | ], 45 | 'group-type' => 'each', 46 | 'sort-type' => 'alph', 47 | 'sort-direction' => 'desc', 48 | ], 49 | ]); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/PHPFormatter/Finder/FileFinderTest.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Tests\Finder; 19 | 20 | use PHPUnit_Framework_TestCase; 21 | 22 | use Mmoreram\PHPFormatter\Finder\FileFinder; 23 | 24 | /** 25 | * Class FileFinderTest. 26 | */ 27 | class FileFinderTest extends PHPUnit_Framework_TestCase 28 | { 29 | /** 30 | * Test php file finder. 31 | */ 32 | public function testFindPHPFilesByPath() 33 | { 34 | $path = dirname(__FILE__) . '/../Mocks/'; 35 | $fileFinder = new FileFinder(); 36 | $this->assertCount(3, $fileFinder->findPHPFilesByPath($path, [])); 37 | $this->assertCount(2, $fileFinder->findPHPFilesByPath($path, ['directory'])); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/PHPFormatter/Fixer/EmptyHeaderFixerTest.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Tests\Fixer; 19 | 20 | use PHPUnit_Framework_TestCase; 21 | 22 | use Mmoreram\PHPFormatter\Fixer\HeaderFixer; 23 | 24 | /** 25 | * Class EmptyHeaderFixerTest. 26 | */ 27 | class EmptyHeaderFixerTest extends PHPUnit_Framework_TestCase 28 | { 29 | /** 30 | * Test fixer. 31 | */ 32 | public function testFix() 33 | { 34 | $data = "assertFalse($headerFixer->fix($data)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/PHPFormatter/Fixer/HeaderFixerTest.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Tests\Fixer; 19 | 20 | use PHPUnit_Framework_TestCase; 21 | 22 | use Mmoreram\PHPFormatter\Fixer\HeaderFixer; 23 | 24 | /** 25 | * Class HeaderFixerTest. 26 | */ 27 | class HeaderFixerTest extends PHPUnit_Framework_TestCase 28 | { 29 | /** 30 | * Test fixer. 31 | * 32 | * @dataProvider dataFix 33 | */ 34 | public function testFix($data) 35 | { 36 | $header = 37 | '/** 38 | * This file is part of the php-formatter package 39 | * 40 | * Copyright (c) 2014 Marc Morera 41 | * 42 | * For the full copyright and license information, please view the LICENSE 43 | * file that was distributed with this source code. 44 | * 45 | * Feel free to edit as you please, and have fun. 46 | * 47 | * @author Marc Morera 48 | */ 49 | '; 50 | 51 | $fixedDataExpected = 52 | ' 65 | */ 66 | 67 | '; 68 | 69 | $headerFixer = new HeaderFixer($header); 70 | $fixedData = $headerFixer->fix($data); 71 | 72 | $this->assertEquals($fixedDataExpected, $fixedData); 73 | } 74 | 75 | /** 76 | * Data for testFix. 77 | */ 78 | public function dataFix() 79 | { 80 | return [ 81 | [' 94 | */'], 95 | ['=2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Tests\Fixer; 19 | 20 | use PHPUnit_Framework_TestCase; 21 | 22 | use Mmoreram\PHPFormatter\Fixer\HeaderFixer; 23 | 24 | /** 25 | * Class HeaderWithStrictFixerTest. 26 | */ 27 | class HeaderWithStrictFixerTest extends PHPUnit_Framework_TestCase 28 | { 29 | /** 30 | * Test fixer. 31 | */ 32 | public function testFix() 33 | { 34 | $data = ' 47 | */ 48 | 49 | declare(strict_types=1);'; 50 | 51 | $header = '/** 52 | * This file is part of the php-formatter package 53 | * 54 | * Copyright (c) 2014 Marc Morera 55 | * 56 | * For the full copyright and license information, please view the LICENSE 57 | * file that was distributed with this source code. 58 | * 59 | * Feel free to edit as you please, and have fun. 60 | * 61 | * @author Marc Morera 62 | */ 63 | '; 64 | 65 | $fixedDataExpected = 66 | ' 79 | */ 80 | 81 | declare(strict_types=1);'; 82 | 83 | $headerFixer = new HeaderFixer($header); 84 | $this->assertFalse($headerFixer->fix($data)); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tests/PHPFormatter/Fixer/StrictFixerTest.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Tests\Fixer; 19 | 20 | use PHPUnit_Framework_TestCase; 21 | 22 | use Mmoreram\PHPFormatter\Fixer\StrictFixer; 23 | 24 | /** 25 | * Class StrictFixerTest. 26 | */ 27 | class StrictFixerTest extends PHPUnit_Framework_TestCase 28 | { 29 | /** 30 | * Test fixer. 31 | * 32 | * @dataProvider dataFix 33 | */ 34 | public function testFix($data, $withHeader, $strict, $changes = true) 35 | { 36 | $fixedDataExpected = 37 | 'fix($data); 49 | 50 | if ($changes) { 51 | $this->assertEquals($fixedDataExpected, $fixedData); 52 | } else { 53 | $this->assertFalse($fixedData); 54 | } 55 | } 56 | 57 | /** 58 | * Data for testFix. 59 | */ 60 | public function dataFix() 61 | { 62 | return [ 63 | ["=2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Tests\Sorter; 19 | 20 | use PHPUnit_Framework_TestCase; 21 | 22 | use Mmoreram\PHPFormatter\Fixer\UseSortFixer; 23 | 24 | /** 25 | * Class UseSortFixerTest. 26 | */ 27 | class UseSortFixerTest extends PHPUnit_Framework_TestCase 28 | { 29 | /** 30 | * @var UseSortFixer 31 | * 32 | * Use sorter 33 | */ 34 | protected $useSortFixer; 35 | 36 | /** 37 | * @var string 38 | * 39 | * Data 40 | */ 41 | protected $data; 42 | 43 | /** 44 | * Setup. 45 | */ 46 | public function setUp() 47 | { 48 | $this->useSortFixer = new UseSortFixer(); 49 | $this->data = file_get_contents( 50 | dirname(__FILE__) . '/../Mocks/SimpleMock.php.mock' 51 | ); 52 | } 53 | 54 | /** 55 | * Test sort. 56 | * 57 | * @dataProvider dataSort 58 | */ 59 | public function testSort( 60 | $groups, 61 | $sortType, 62 | $sortDirection, 63 | $groupType, 64 | $result 65 | ) { 66 | $parsedData = $this 67 | ->useSortFixer 68 | ->setGroups($groups) 69 | ->setSortType($sortType) 70 | ->setSortDirection($sortDirection) 71 | ->setGroupType($groupType) 72 | ->fix($this->data); 73 | $realResult = 74 | "assertEquals( 89 | $realResult, 90 | $parsedData 91 | ); 92 | } 93 | 94 | /** 95 | * Data for testSort. 96 | * 97 | * @return array Data 98 | */ 99 | public function dataSort() 100 | { 101 | return [ 102 | [ 103 | [], 104 | UseSortFixer::SORT_TYPE_ALPHABETIC, 105 | UseSortFixer::SORT_DIRECTION_ASC, 106 | UseSortFixer::GROUP_TYPE_EACH, 107 | ' 108 | use Test1\\Myclass1; 109 | use Test1\\Myclass2; 110 | use Test1\\MyFolder5\\File as MyFile; 111 | use Test2\\Myclass3; 112 | use Test2\\Myclass4; 113 | use Test3\\File; 114 | use Test3\\MyFolder\\Myclass; 115 | use Test4\\Myclass3; 116 | ', 117 | ], 118 | [ 119 | [], 120 | UseSortFixer::SORT_TYPE_ALPHABETIC, 121 | UseSortFixer::SORT_DIRECTION_ASC, 122 | UseSortFixer::GROUP_TYPE_ONE, 123 | ' 124 | use Test1\\Myclass1, 125 | Test1\\Myclass2, 126 | Test1\\MyFolder5\\File as MyFile, 127 | Test2\\Myclass3, 128 | Test2\\Myclass4, 129 | Test3\\File, 130 | Test3\\MyFolder\\Myclass, 131 | Test4\\Myclass3; 132 | ', 133 | ], 134 | [ 135 | [], 136 | UseSortFixer::SORT_TYPE_ALPHABETIC, 137 | UseSortFixer::SORT_DIRECTION_DESC, 138 | UseSortFixer::GROUP_TYPE_EACH, 139 | ' 140 | use Test4\\Myclass3; 141 | use Test3\\MyFolder\\Myclass; 142 | use Test3\\File; 143 | use Test2\\Myclass4; 144 | use Test2\\Myclass3; 145 | use Test1\\MyFolder5\\File as MyFile; 146 | use Test1\\Myclass2; 147 | use Test1\\Myclass1; 148 | ', 149 | ], 150 | [ 151 | [], 152 | UseSortFixer::SORT_TYPE_ALPHABETIC, 153 | UseSortFixer::SORT_DIRECTION_DESC, 154 | UseSortFixer::GROUP_TYPE_ONE, 155 | ' 156 | use Test4\\Myclass3, 157 | Test3\\MyFolder\\Myclass, 158 | Test3\\File, 159 | Test2\\Myclass4, 160 | Test2\\Myclass3, 161 | Test1\\MyFolder5\\File as MyFile, 162 | Test1\\Myclass2, 163 | Test1\\Myclass1; 164 | ', 165 | ], 166 | [ 167 | [], 168 | UseSortFixer::SORT_TYPE_LENGTH, 169 | UseSortFixer::SORT_DIRECTION_DESC, 170 | UseSortFixer::GROUP_TYPE_EACH, 171 | ' 172 | use Test1\\MyFolder5\\File as MyFile; 173 | use Test3\\MyFolder\\Myclass; 174 | use Test4\\Myclass3; 175 | use Test2\\Myclass4; 176 | use Test2\\Myclass3; 177 | use Test1\\Myclass2; 178 | use Test1\\Myclass1; 179 | use Test3\\File; 180 | ', 181 | ], 182 | [ 183 | [], 184 | UseSortFixer::SORT_TYPE_LENGTH, 185 | UseSortFixer::SORT_DIRECTION_DESC, 186 | UseSortFixer::GROUP_TYPE_ONE, 187 | ' 188 | use Test1\\MyFolder5\\File as MyFile, 189 | Test3\\MyFolder\\Myclass, 190 | Test4\\Myclass3, 191 | Test2\\Myclass4, 192 | Test2\\Myclass3, 193 | Test1\\Myclass2, 194 | Test1\\Myclass1, 195 | Test3\\File; 196 | ', 197 | ], 198 | [ 199 | [], 200 | UseSortFixer::SORT_TYPE_LENGTH, 201 | UseSortFixer::SORT_DIRECTION_ASC, 202 | UseSortFixer::GROUP_TYPE_EACH, 203 | ' 204 | use Test3\\File; 205 | use Test1\\Myclass1; 206 | use Test1\\Myclass2; 207 | use Test2\\Myclass3; 208 | use Test2\\Myclass4; 209 | use Test4\\Myclass3; 210 | use Test3\\MyFolder\\Myclass; 211 | use Test1\\MyFolder5\\File as MyFile; 212 | ', 213 | ], 214 | [ 215 | [], 216 | UseSortFixer::SORT_TYPE_LENGTH, 217 | UseSortFixer::SORT_DIRECTION_ASC, 218 | UseSortFixer::GROUP_TYPE_ONE, 219 | ' 220 | use Test3\\File, 221 | Test1\\Myclass1, 222 | Test1\\Myclass2, 223 | Test2\\Myclass3, 224 | Test2\\Myclass4, 225 | Test4\\Myclass3, 226 | Test3\\MyFolder\\Myclass, 227 | Test1\\MyFolder5\\File as MyFile; 228 | ', 229 | ], 230 | [ 231 | ['_main'], 232 | UseSortFixer::SORT_TYPE_LENGTH, 233 | UseSortFixer::SORT_DIRECTION_ASC, 234 | UseSortFixer::GROUP_TYPE_ONE, 235 | ' 236 | use Test3\\File, 237 | Test1\\Myclass1, 238 | Test1\\Myclass2, 239 | Test2\\Myclass3, 240 | Test2\\Myclass4, 241 | Test4\\Myclass3, 242 | Test3\\MyFolder\\Myclass, 243 | Test1\\MyFolder5\\File as MyFile; 244 | ', 245 | ], 246 | [ 247 | ['_main', 'Test2'], 248 | UseSortFixer::SORT_TYPE_LENGTH, 249 | UseSortFixer::SORT_DIRECTION_ASC, 250 | UseSortFixer::GROUP_TYPE_ONE, 251 | ' 252 | use Test3\\File, 253 | Test1\\Myclass1, 254 | Test1\\Myclass2, 255 | Test4\\Myclass3, 256 | Test3\\MyFolder\\Myclass, 257 | Test1\\MyFolder5\\File as MyFile; 258 | 259 | use Test2\\Myclass3, 260 | Test2\\Myclass4; 261 | ', 262 | ], 263 | [ 264 | ['Test2', '_main'], 265 | UseSortFixer::SORT_TYPE_LENGTH, 266 | UseSortFixer::SORT_DIRECTION_ASC, 267 | UseSortFixer::GROUP_TYPE_ONE, 268 | ' 269 | use Test2\\Myclass3, 270 | Test2\\Myclass4; 271 | 272 | use Test3\\File, 273 | Test1\\Myclass1, 274 | Test1\\Myclass2, 275 | Test4\\Myclass3, 276 | Test3\\MyFolder\\Myclass, 277 | Test1\\MyFolder5\\File as MyFile; 278 | ', 279 | ], 280 | [ 281 | ['Test2', '_main', 'Test3'], 282 | UseSortFixer::SORT_TYPE_ALPHABETIC, 283 | UseSortFixer::SORT_DIRECTION_ASC, 284 | UseSortFixer::GROUP_TYPE_EACH, 285 | ' 286 | use Test2\\Myclass3; 287 | use Test2\\Myclass4; 288 | 289 | use Test1\\Myclass1; 290 | use Test1\\Myclass2; 291 | use Test1\\MyFolder5\\File as MyFile; 292 | use Test4\\Myclass3; 293 | 294 | use Test3\\File; 295 | use Test3\\MyFolder\\Myclass; 296 | ', 297 | ], 298 | [ 299 | ['Test2', 'TestEmpty', '_main', 'Test3'], 300 | UseSortFixer::SORT_TYPE_ALPHABETIC, 301 | UseSortFixer::SORT_DIRECTION_ASC, 302 | UseSortFixer::GROUP_TYPE_EACH, 303 | ' 304 | use Test2\\Myclass3; 305 | use Test2\\Myclass4; 306 | 307 | use Test1\\Myclass1; 308 | use Test1\\Myclass2; 309 | use Test1\\MyFolder5\\File as MyFile; 310 | use Test4\\Myclass3; 311 | 312 | use Test3\\File; 313 | use Test3\\MyFolder\\Myclass; 314 | ', 315 | ], 316 | [ 317 | ['Test2', ['Test1\MyFolder5', 'Test1'], '_main'], 318 | UseSortFixer::SORT_TYPE_ALPHABETIC, 319 | UseSortFixer::SORT_DIRECTION_ASC, 320 | UseSortFixer::GROUP_TYPE_EACH, 321 | ' 322 | use Test2\\Myclass3; 323 | use Test2\\Myclass4; 324 | 325 | use Test1\\MyFolder5\\File as MyFile; 326 | use Test1\\Myclass1; 327 | use Test1\\Myclass2; 328 | 329 | use Test3\\File; 330 | use Test3\\MyFolder\\Myclass; 331 | use Test4\\Myclass3; 332 | ', 333 | ], 334 | [ 335 | ['Test1', ['TestEmpty1', 'TestEmpty2'], '_main'], 336 | UseSortFixer::SORT_TYPE_ALPHABETIC, 337 | UseSortFixer::SORT_DIRECTION_ASC, 338 | UseSortFixer::GROUP_TYPE_EACH, 339 | ' 340 | use Test1\\Myclass1; 341 | use Test1\\Myclass2; 342 | use Test1\\MyFolder5\\File as MyFile; 343 | 344 | use Test2\\Myclass3; 345 | use Test2\\Myclass4; 346 | use Test3\\File; 347 | use Test3\\MyFolder\\Myclass; 348 | use Test4\\Myclass3; 349 | ', 350 | ], 351 | ]; 352 | } 353 | 354 | /** 355 | * Test skip empty. 356 | */ 357 | public function testGroupSkip() 358 | { 359 | $parsedData = $this 360 | ->useSortFixer 361 | ->setGroups(['Test2', 'TestEmpty', '_main', 'Test3']) 362 | ->setSortType(UseSortFixer::SORT_TYPE_ALPHABETIC) 363 | ->setSortDirection(UseSortFixer::SORT_DIRECTION_ASC) 364 | ->setGroupType(UseSortFixer::GROUP_TYPE_EACH) 365 | ->setGroupSkipEmpty(true) 366 | ->fix($this->data); 367 | 368 | $result = 369 | 'assertEquals( 393 | $result, 394 | $parsedData 395 | ); 396 | } 397 | 398 | /** 399 | * Test skip empty where consecutive empty groups are between used groups. 400 | * 401 | * @see https://github.com/mmoreram/php-formatter/issues/24 402 | */ 403 | public function testGroupSkipWithMissingGroups() 404 | { 405 | $parsedData = $this 406 | ->useSortFixer 407 | ->setGroups(['Test1', 'TestEmpty1', ['TestEmpty2', 'TestEmpty3'], '_main']) 408 | ->setSortType(UseSortFixer::SORT_TYPE_ALPHABETIC) 409 | ->setSortDirection(UseSortFixer::SORT_DIRECTION_ASC) 410 | ->setGroupType(UseSortFixer::GROUP_TYPE_EACH) 411 | ->setGroupSkipEmpty(true) 412 | ->fix($this->data); 413 | 414 | $result = 415 | 'assertEquals( 439 | $result, 440 | $parsedData 441 | ); 442 | } 443 | 444 | /** 445 | * Test skip empty where consecutive non empty groups are between used groups. 446 | */ 447 | public function testGroupNoSkipWithNotMissingGroups() 448 | { 449 | $parsedData = $this 450 | ->useSortFixer 451 | ->setGroups(['Test1', ['Test2', 'Test3'], 'Test4']) 452 | ->setSortType(UseSortFixer::SORT_TYPE_ALPHABETIC) 453 | ->setSortDirection(UseSortFixer::SORT_DIRECTION_ASC) 454 | ->setGroupType(UseSortFixer::GROUP_TYPE_EACH) 455 | ->setGroupSkipEmpty(false) 456 | ->fix($this->data); 457 | 458 | $result = 459 | 'assertEquals( 485 | $result, 486 | $parsedData 487 | ); 488 | } 489 | } 490 | -------------------------------------------------------------------------------- /tests/PHPFormatter/Loader/ConfigLoaderTest.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Tests\Loader; 19 | 20 | use PHPUnit_Framework_TestCase; 21 | 22 | use Mmoreram\PHPFormatter\Loader\ConfigLoader; 23 | 24 | /** 25 | * Class ConfigLoaderTest. 26 | */ 27 | class ConfigLoaderTest extends PHPUnit_Framework_TestCase 28 | { 29 | /** 30 | * @var array 31 | * 32 | * Default values 33 | */ 34 | protected $defaultValues = [ 35 | 'group' => [ 36 | 'Symfony', 37 | '_main', 38 | 'Mmoreram', 39 | ], 40 | 'group-type' => 'each', 41 | 'sort-type' => 'alph', 42 | 'sort-direction' => 'desc', 43 | ]; 44 | 45 | /** 46 | * Test load config values. 47 | * 48 | * @dataProvider dataLoadConfigValues 49 | */ 50 | public function testLoadConfigValues( 51 | $configValues, 52 | $commandValues, 53 | $defaultValues, 54 | $usableValues 55 | ) { 56 | $configLoader = new ConfigLoader(); 57 | $this->assertEquals( 58 | $usableValues, 59 | $configLoader->loadConfigValues( 60 | 'use-sort', 61 | $configValues, 62 | $commandValues, 63 | $defaultValues 64 | ) 65 | ); 66 | } 67 | 68 | /** 69 | * data for testLoadConfigValues. 70 | * 71 | * @return array Values 72 | */ 73 | public function dataLoadConfigValues() 74 | { 75 | return [ 76 | [ 77 | [], 78 | [], 79 | $this->defaultValues, 80 | $this->defaultValues, 81 | ], 82 | 83 | [ 84 | [ 85 | 'use-sort' => [ 86 | 'group' => [ 87 | 'Doctrine', 88 | 'Elcodi', 89 | ], 90 | 'group-type' => 'one', 91 | ], 92 | ], 93 | [], 94 | $this->defaultValues, 95 | [ 96 | 'group' => [ 97 | 'Doctrine', 98 | 'Elcodi', 99 | ], 100 | 'group-type' => 'one', 101 | 'sort-type' => 'alph', 102 | 'sort-direction' => 'desc', 103 | ], 104 | ], 105 | 106 | [ 107 | [ 108 | 'use-sort' => [ 109 | 'group' => [ 110 | 'Doctrine', 111 | 'Elcodi', 112 | ], 113 | 'group-type' => 'one', 114 | ], 115 | ], 116 | [ 117 | 'sort-type' => 'length', 118 | 'sort-direction' => 'asc', 119 | ], 120 | $this->defaultValues, 121 | [ 122 | 'group' => [ 123 | 'Doctrine', 124 | 'Elcodi', 125 | ], 126 | 'group-type' => 'one', 127 | 'sort-type' => 'length', 128 | 'sort-direction' => 'asc', 129 | ], 130 | ], 131 | 132 | [ 133 | [], 134 | [ 135 | 'sort-type' => 'length', 136 | 'sort-direction' => 'asc', 137 | ], 138 | $this->defaultValues, 139 | [ 140 | 'group' => [ 141 | 'Symfony', 142 | '_main', 143 | 'Mmoreram', 144 | ], 145 | 'group-type' => 'each', 146 | 'sort-type' => 'length', 147 | 'sort-direction' => 'asc', 148 | ], 149 | ], 150 | ]; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /tests/PHPFormatter/Mocks/.formatter.yml: -------------------------------------------------------------------------------- 1 | use-sort: 2 | group: 3 | - Symfony 4 | - _main 5 | - Mmoreram 6 | group-type: each 7 | sort-type: alph 8 | sort-direction: desc -------------------------------------------------------------------------------- /tests/PHPFormatter/Mocks/MyClass.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Tests\Mocks; 19 | 20 | /** 21 | * Class MyClass. 22 | */ 23 | class MyClass 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /tests/PHPFormatter/Mocks/MyOtherClass.php: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Tests\Mocks; 19 | 20 | /** 21 | * Class MyOtherClass. 22 | */ 23 | class MyOtherClass 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /tests/PHPFormatter/Mocks/SimpleMock.php.mock: -------------------------------------------------------------------------------- 1 | =2014 Marc Morera 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * Feel free to edit as you please, and have fun. 12 | * 13 | * @author Marc Morera 14 | */ 15 | 16 | declare(strict_types=1); 17 | 18 | namespace Mmoreram\PHPFormatter\Tests\Mocks\Directory; 19 | 20 | /** 21 | * Class MyThirdClass. 22 | */ 23 | class MyThirdClass 24 | { 25 | } 26 | --------------------------------------------------------------------------------