|
1 | | -Slevomat Coding Standard |
2 | | -====================== |
| 1 | +# Slevomat Coding Standard |
| 2 | + |
| 3 | +[](https://packagist.org/packages/slevomat/coding-standard) |
| 4 | +[](https://travis-ci.org/slevomat/coding-standard) |
| 5 | +[](https://coveralls.io/github/slevomat/coding-standard?branch=master) |
| 6 | + |
| 7 | +Slevomat Coding Standard for [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) extends [Consistence Coding Standard](https://github.com/consistence/coding-standard) by providing sniffs with additional checks. |
| 8 | + |
| 9 | +## Table of contents |
| 10 | + |
| 11 | +1. [Sniffs included in this standard](#sniffs-included-in-this-standard) |
| 12 | + - [Unused private class properties and methods](#slevomatcodingstandardclassesunusedprivateelements) |
| 13 | + - [Trailing array comma](#slevomatcodingstandardarraystrailingarraycomma-) |
| 14 | + - [Yoda conditions](#slevomatcodingstandardcontrolstructuresyodacomparison-) |
| 15 | + - [Alphabetically sorted uses](#slevomatcodingstandardnamespacesalphabeticallysorteduses-) |
| 16 | + - [Unused uses](#slevomatcodingstandardnamespacesunuseduses-) |
| 17 | + - [Other namespace-related sniffs](#other-namespace-related-sniffs) |
| 18 | + - [Empty lines around opening and closing type braces](#slevomatcodingstandardtypesemptylinesaroundtypebraces) |
| 19 | + - [Type name matches file name](#slevomatcodingstandardfilestypenamematchesfilename) |
| 20 | +2. [Installation](#installation) |
| 21 | +3. [Using the standard as a whole](#using-the-standard-as-a-whole) |
| 22 | +4. [Using individual sniffs](#using-individual-sniffs) |
| 23 | +5. [Fixing errors automatically](#fixing-errors-automatically) |
| 24 | +6. [Contributing](#contributing) |
| 25 | + |
| 26 | +## Sniffs included in this standard |
| 27 | + |
| 28 | +🔧 = [Automatic errors fixing](#fixing-errors-automatically) |
| 29 | + |
| 30 | +### SlevomatCodingStandard.Classes.UnusedPrivateElements |
| 31 | + |
| 32 | +Although PHP_CodeSniffer is not suitable for static analysis because it is limited to analysing one file at a time, it is possible to use it to perform certain checks. `UnusedPrivateElementsSniff` checks for unused methods and unused or write-only properties in a class. Reported unused elements are safe to remove. |
| 33 | + |
| 34 | +This is very useful during refactoring to clean up dead code and injected dependencies. |
| 35 | + |
| 36 | +### SlevomatCodingStandard.Arrays.TrailingArrayComma 🔧 |
| 37 | + |
| 38 | +Commas after last element in an array make adding a new element easier and result in a cleaner versioning diff. |
| 39 | + |
| 40 | +This sniff enforces trailing commas in multi-line arrays and requires short array syntax `[]`. |
| 41 | + |
| 42 | +### SlevomatCodingStandard.ControlStructures.YodaComparison 🔧 |
| 43 | + |
| 44 | +[Yoda conditions](https://en.wikipedia.org/wiki/Yoda_conditions) decrease code comprehensibility and readability by switching operands around comparison operators forcing the reader to read the code in an unnatural way. |
| 45 | + |
| 46 | +`YodaComparisonSniff` looks for and fixes such comparisons not only in `if` statements but in the whole code. |
| 47 | + |
| 48 | +### SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses 🔧 |
| 49 | + |
| 50 | +Checks whether uses at the top of a file are alphabetically sorted. Follows natural sorting and takes edge cases with special symbols into consideration. The following code snippet is an example of correctly sorted uses: |
| 51 | + |
| 52 | +```php |
| 53 | +use Baz; |
| 54 | +use Foo; |
| 55 | +use Foo\Bar; |
| 56 | +use Foo_Baz; |
| 57 | +use Foo_bar; |
| 58 | +use Foo1; |
| 59 | +use Foo2; |
| 60 | +use Foo11; |
| 61 | +use Foo22; |
| 62 | +use FooBaz; |
| 63 | +use Foobar; |
| 64 | +use Foobarz; |
| 65 | +``` |
| 66 | + |
| 67 | +### SlevomatCodingStandard.Namespaces.UnusedUses 🔧 |
| 68 | + |
| 69 | +Looks for unused imports from other namespaces. Provides a property setting `searchAnnotations` (default `false`) that enables searching for mentions in annotations, which is especially useful for projects using [Doctrine Annotations](https://github.com/doctrine/annotations). |
| 70 | + |
| 71 | +### Other namespace-related sniffs |
| 72 | + |
| 73 | +#### SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameAfterKeyword |
| 74 | + |
| 75 | +Enforces fully qualified type references after configurable set of language keywords. |
| 76 | + |
| 77 | +For example with the following setting, extended or implemented type must always be referenced with a fully qualified name: |
| 78 | + |
| 79 | +```xml |
| 80 | +<rule ref="SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameAfterKeyword"> |
| 81 | + <properties> |
| 82 | + <property name="keywordsToCheck" type="array" value="T_EXTENDS,T_IMPLEMENTS"/> |
| 83 | + </properties> |
| 84 | +</rule> |
| 85 | +``` |
| 86 | + |
| 87 | +#### SlevomatCodingStandard.Namespaces.FullyQualifiedExceptions |
| 88 | + |
| 89 | +This sniff reduces confusion in the following code snippet: |
| 90 | + |
| 91 | +```php |
| 92 | +try { |
| 93 | + $this->foo(); |
| 94 | +} catch (Exception $e) { |
| 95 | + // Is this the general exception all exceptions must extend from? Or Exception from the current namespace? |
| 96 | +} |
| 97 | +``` |
| 98 | + |
| 99 | +All references to types named `Exception` or ending with `Exception` must be referenced via a fully qualified name: |
| 100 | + |
| 101 | +```php |
| 102 | +try { |
| 103 | + $this->foo(); |
| 104 | +} catch (\FooCurrentNamespace\Exception $e) { |
| 105 | + |
| 106 | +} catch (\Exception $e) { |
| 107 | + |
| 108 | +} |
| 109 | +``` |
| 110 | + |
| 111 | +Exceptions with different names can be configured in `specialExceptionNames` property. |
| 112 | + |
| 113 | +#### SlevomatCodingStandard.Namespaces.MultipleUsesPerLine |
| 114 | + |
| 115 | +Prohibits multiple uses separated by commas: |
| 116 | + |
| 117 | +```php |
| 118 | +use Foo, Bar; |
| 119 | +``` |
| 120 | + |
| 121 | +#### SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly |
| 122 | + |
| 123 | +Enforces to use all referenced names with configurable omissions: |
| 124 | + |
| 125 | +`fullyQualifiedKeywords` - allows fully qualified names after certain keywords. Useful in tandem with FullyQualifiedClassNameAfterKeyword sniff. |
| 126 | + |
| 127 | +`allowFullyQualifiedExceptions` & `specialExceptionNames` - allows fully qualified exceptions. Useful in tandem with FullyQualifiedExceptions sniff. |
| 128 | + |
| 129 | +`allowPartialUses` - allows using and referencing whole namespaces: |
| 130 | + |
| 131 | +```php |
| 132 | +use Foo; |
| 133 | +//... |
| 134 | +new Foo\Bar(); |
| 135 | +``` |
| 136 | + |
| 137 | +`namespacesRequiredToUse` - if not set, all namespaces are required to be used. When set, only mentioned namespaces are required to be used. Useful in tandem with UseOnlyWhitelistedNamespaces sniff. |
| 138 | + |
| 139 | +#### SlevomatCodingStandard.Namespaces.UseFromSameNamespace |
| 140 | + |
| 141 | +Prohibits uses from the same namespace: |
| 142 | + |
| 143 | +```php |
| 144 | +namespace Foo; |
| 145 | + |
| 146 | +use Foo\Bar; |
| 147 | +``` |
| 148 | + |
| 149 | +#### SlevomatCodingStandard.Namespaces.UseOnlyWhitelistedNamespaces |
| 150 | + |
| 151 | +Disallows uses of other than configured namespaces. |
| 152 | + |
| 153 | +`namespacesRequiredToUse` - namespaces in this array are the only ones allowed to be used. E. g. root project namespace. |
| 154 | + |
| 155 | +`allowUseFromRootNamespace` - also allow using top-level namespace: |
| 156 | + |
| 157 | +```php |
| 158 | +use DateTimeImmutable; |
| 159 | +``` |
| 160 | + |
| 161 | +#### SlevomatCodingStandard.Namespaces.UseStartsWithBackslash |
| 162 | + |
| 163 | +Disallows leading backslash in use statement: |
| 164 | + |
| 165 | +```php |
| 166 | +use \Foo\Bar; |
| 167 | +``` |
| 168 | + |
| 169 | +### SlevomatCodingStandard.Types.EmptyLinesAroundTypeBraces |
| 170 | + |
| 171 | +Enforces one empty line after opening class/interface/trait brace and one empty line before the closing brace. |
| 172 | + |
| 173 | +### SlevomatCodingStandard.Files.TypeNameMatchesFileName |
| 174 | + |
| 175 | +For projects not following the [PSR-0](http://www.php-fig.org/psr/psr-0/) or [PSR-4](http://www.php-fig.org/psr/psr-4/) autoloading standards, this sniff checks whether a namespace and a name of a class/interface/trait follows agreed-on way to organize code into directories and files. |
| 176 | + |
| 177 | +Other than enforcing that the type name must match the name of the file it's contained in, this sniff is very configurable. Consider the following sample configuration: |
| 178 | + |
| 179 | +```xml |
| 180 | +<rule ref="SlevomatCodingStandard.Files.TypeNameMatchesFileName"> |
| 181 | + <properties> |
| 182 | + <property name="rootNamespaces" type="array" value="app/ui=>Slevomat\UI,app=>Slevomat,build/SlevomatSniffs/Sniffs=>SlevomatSniffs\Sniffs,tests/ui=>Slevomat\UI,tests=>Slevomat"/> |
| 183 | + <property name="skipDirs" type="array" value="components,forms,model,models,services,stubs,data,new"/> |
| 184 | + <property name="ignoredNamespaces" type="array" value="Slevomat\Services"/> |
| 185 | + </properties> |
| 186 | +</rule> |
| 187 | +``` |
| 188 | + |
| 189 | +`rootNamespaces` property expects configuration similar to PSR-4 - project directories mapped to certain namespaces. |
| 190 | + |
| 191 | +`skipDirs` are not taken into consideration when comparing a path to a namespace. For example, with the above settings, file at path `app/services/Product/Product.php` is expected to contain `Slevomat\Product\Product`, not `Slevomat\services\Product\Product`. |
| 192 | + |
| 193 | +Sniff is not performed on types in `ignoredNamespaces`. |
| 194 | + |
| 195 | +## Installation |
| 196 | + |
| 197 | +The recommended way to install Slevomat Coding Standard is [through Composer](http://getcomposer.org). |
| 198 | + |
| 199 | +```JSON |
| 200 | +{ |
| 201 | + "require": { |
| 202 | + "slevomat/coding-standard": "^1.0" |
| 203 | + } |
| 204 | +} |
| 205 | +``` |
| 206 | + |
| 207 | +This package also installs [jakub-onderka/php-parallel-lint](https://github.com/JakubOnderka/PHP-Parallel-Lint) which checks source code for syntax errors. Sniffs count on the processed code to be syntatically valid (no parse errors), otherwise they can behave unexpectedly. It is advised to run `PHP-Parallel-Lint` in your build tool before running `PHP_CodeSniffer` and exiting the build process early if `PHP-Parallel-Lint` fails. |
| 208 | + |
| 209 | +## Using the standard as a whole |
| 210 | + |
| 211 | +If you want to use the whole coding standard, besides requiring `slevomat/coding-standard` in composer.json, require also Consistence Coding Standard: |
| 212 | + |
| 213 | +```JSON |
| 214 | +{ |
| 215 | + "require": { |
| 216 | + "consistence/coding-standard": "^0.8" |
| 217 | + } |
| 218 | +} |
| 219 | +``` |
| 220 | + |
| 221 | +Then mention both standards in `ruleset.xml`: |
| 222 | + |
| 223 | +```xml |
| 224 | +<?xml version="1.0"?> |
| 225 | +<ruleset name="AcmeProject"> |
| 226 | + <rule ref="vendor/consistence/coding-standard/Consistence/ruleset.xml" /> |
| 227 | + <rule ref="vendor/slevomat/coding-standard/SlevomatCodingStandard/ruleset.xml" /> |
| 228 | + <!-- additional settings --> |
| 229 | +</ruleset> |
| 230 | +``` |
| 231 | + |
| 232 | +To check your code base for violations, run `PHP-Parallel-Lint` and `PHP_CodeSniffer` from the command line: |
| 233 | + |
| 234 | +``` |
| 235 | +vendor/bin/parallel-lint src tests |
| 236 | +vendor/bin/phpcs --standard=ruleset.xml --extensions=php --encoding=utf-8 --tab-width=4 -sp src tests |
| 237 | +``` |
| 238 | + |
| 239 | +## Using individual sniffs |
| 240 | + |
| 241 | +If you don't want to follow the whole standard, but find a handful of included sniffs useful, you can use them selectively. |
| 242 | + |
| 243 | +Besides requiring `slevomat/coding-standard` in composer.json, require also PHP_CodeSniffer in desired version: |
| 244 | + |
| 245 | +```JSON |
| 246 | +{ |
| 247 | + "require": { |
| 248 | + "squizlabs/php_codesniffer": "2.5.0" |
| 249 | + } |
| 250 | +} |
| 251 | +``` |
| 252 | + |
| 253 | +You can choose one of two ways to run only selected sniffs from the standard on your codebase: |
| 254 | + |
| 255 | +### List all sniffs to run |
| 256 | + |
| 257 | +Mention Slevomat Conding Standard in your project's `ruleset.xml`: |
| 258 | + |
| 259 | +```xml |
| 260 | +<?xml version="1.0"?> |
| 261 | +<ruleset name="AcmeProject"> |
| 262 | + <rule ref="vendor/slevomat/coding-standard/SlevomatCodingStandard/ruleset.xml" /> |
| 263 | +</ruleset> |
| 264 | +``` |
| 265 | + |
| 266 | +When running `phpcs` [on the command line](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Usage), use the `--sniffs` option to list all the sniffs you want to use separated by a comma: |
| 267 | + |
| 268 | +``` |
| 269 | +vendor/bin/phpcs --standard=ruleset.xml \ |
| 270 | +--sniffs=SlevomatCodingStandard.ControlStructures.YodaComparison,SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses \ |
| 271 | +--extensions=php --encoding=utf-8 --tab-width=4 -sp src tests |
| 272 | +``` |
| 273 | + |
| 274 | +### Use all sniffs except for the unwanted ones |
| 275 | + |
| 276 | +Mention Slevomat Conding Standard in your project's `ruleset.xml` and list all the excluded sniffs: |
| 277 | + |
| 278 | +```xml |
| 279 | +<?xml version="1.0"?> |
| 280 | +<ruleset name="AcmeProject"> |
| 281 | + <rule ref="vendor/slevomat/coding-standard/SlevomatCodingStandard/ruleset.xml"> |
| 282 | + <exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameAfterKeyword"/> |
| 283 | + <exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedExceptions"/> |
| 284 | + <exclude name="SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly"/> |
| 285 | + <exclude name="SlevomatCodingStandard.Namespaces.UseOnlyWhitelistedNamespaces"/> |
| 286 | + <exclude name="SlevomatCodingStandard.Types.EmptyLinesAroundTypeBraces"/> |
| 287 | + <exclude name="SlevomatCodingStandard.Files.TypeNameMatchesFileName"/> |
| 288 | + </rule> |
| 289 | +</ruleset> |
| 290 | +``` |
| 291 | + |
| 292 | +Then run the remaining sniffs in the usual way: |
| 293 | + |
| 294 | +``` |
| 295 | +vendor/bin/phpcs --standard=ruleset.xml --extensions=php --encoding=utf-8 --tab-width=4 -sp src tests |
| 296 | +``` |
| 297 | + |
| 298 | +## Fixing errors automatically |
| 299 | + |
| 300 | +Sniffs in this standard marked by the 🔧 symbol support [automatic fixing of coding standard violations](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Fixing-Errors-Automatically). To fix your code automatically, run phpcbf insteand of phpcs: |
| 301 | + |
| 302 | +``` |
| 303 | +vendor/bin/phpcbf --standard=ruleset.xml --extensions=php --encoding=utf-8 --tab-width=4 -sp src tests |
| 304 | +``` |
| 305 | + |
| 306 | +Always remember to back up your code before performing automatic fixes and check the results with your own eyes as the automatic fixer can sometimes produce unwanted results. |
| 307 | + |
| 308 | +## Contributing |
| 309 | + |
| 310 | +To make this repository work on your machine, clone it and run these two commands in the root directory of the repository: |
| 311 | + |
| 312 | +``` |
| 313 | +composer install |
| 314 | +vendor/bin/phing |
| 315 | +``` |
| 316 | + |
| 317 | +After writing some code and editing or adding unit tests, run phing again to check that everything is OK: |
| 318 | + |
| 319 | +``` |
| 320 | +vendor/bin/phing |
| 321 | +``` |
| 322 | + |
| 323 | +We are always looking forward for your bugreports, feature requests and pull requests. Thank you. |
| 324 | + |
0 commit comments