core-code-style.md 12.6 KB
Newer Older
1 2 3
Yii2 Core framework code style
==============================

4 5 6
The following code style is used for Yii 2.x core and official extensions development. If you want to pull-request code
into the core, consider using it. We aren't forcing you to use this code style for your application. Feel free to choose
what suits you better.
7 8 9 10 11 12

You can get a config for CodeSniffer here: https://github.com/yiisoft/yii2-coding-standards

1. Overview
-----------

13 14 15 16 17
Overall we're using [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
compatible style so everything that applies to
[PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) is applied to our code
style as well.

18 19 20
- Files MUST use either `<?php` or `<?=` tags.
- There should be a newline at the end of file.
- Files MUST use only UTF-8 without BOM for PHP code.
21
- Code MUST use 4 spaces for indenting, not tabs.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
- Class names MUST be declared in `StudlyCaps`.
- Class constants MUST be declared in all upper case with underscore separators.
- Method names MUST be declared in `camelCase`.
- Property names MUST be declared in `camelCase`.
- Property names MUST start with an initial underscore if they are private.
- Always use `elseif` instead of `else if`.

2. Files
--------

### 2.1. PHP Tags

- PHP code MUST use `<?php ?>` or `<?=` tags; it MUST NOT use the other tag variations such as `<?`.
- In case file contains PHP only it should not have trailing `?>`.
- Do not add trailing spaces to the end of the lines.
- Any file that contains PHP code should end with the extension `.php`.

### 2.2. Character Encoding

PHP code MUST use only UTF-8 without BOM.

3. Class Names
--------------

Class names MUST be declared in `StudlyCaps`. For example, `Controller`, `Model`.

4. Classes
----------

The term "class" refers to all classes and interfaces here.

- Classes should be named using `CamelCase`.
- The brace should always be written on the line underneath the class name.
- Every class must have a documentation block that conforms to the PHPDoc.
- All code in a class must be indented with a single tab.
- There should be only one class in a single PHP file.
- All classes should be namespaced.
- Class name should match file name. Class namespace should match directory structure.

```php
/**
 * Documentation
 */
class MyClass extends \yii\Object implements MyInterface
{
67
    // code
68 69 70 71 72 73 74 75 76 77 78 79
}
```

### 4.1. Constants

Class constants MUST be declared in all upper case with underscore separators.
For example:

```php
<?php
class Foo
{
80 81
    const VERSION = '1.0';
    const DATE_APPROVED = '2012-06-01';
82 83 84 85 86
}
```
### 4.2. Properties

- When declaring public class members specify `public` keyword explicitly.
87 88 89 90 91 92
- Public and protected variables should be declared at the top of the class before any method declarations.
  Private variables should also be declared at the top of the class but may be added right before the methods
  that are dealing with them in cases where they are only related to a small subset of the class methods.
- The order of property declaration in a class should be ascending from public over protected to private.
- For better readability there should be no blank lines between property declarations and two blank lines
  between property and method declaration sections.
93 94 95 96 97 98 99 100 101 102 103
- Private variables should be named like `$_varName`.
- Public class members and standalone variables should be named using `$camelCase`
  with first letter lowercase.
- Use descriptive names. Variables such as `$i` and `$j` are better not to be used.

For example:

```php
<?php
class Foo
{
104 105 106
    public $publicProp;
    protected $protectedProp;
    private $_privateProp;
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
}
```

### 4.3. Methods

- Functions and methods should be named using `camelCase` with first letter lowercase.
- Name should be descriptive by itself indicating the purpose of the function.
- Class methods should always declare visibility using `private`, `protected` and
  `public` modifiers. `var` is not allowed.
- Opening brace of a function should be on the line after the function declaration.

~~~
/**
 * Documentation
 */
class Foo
{
124 125 126 127 128 129 130 131
    /**
     * Documentation
     */
    public function bar()
    {
        // code
        return $value;
    }
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
}
~~~

### 4.4 Doc blocks

`@param`, `@var`, `@property` and `@return` must declare types as `boolean`, `integer`, `string`, `array` or `null`. You can use a class names as well such as `Model` or `ActiveRecord`. For a typed arrays use `ClassName[]`.

### 4.5 Constructors

- `__construct` should be used instead of PHP 4 style constructors.

## 5 PHP

### 5.1 Types

- All PHP types and values should be used lowercase. That includes `true`, `false`, `null` and `array`.

Changing type of an existing variable is considered as a bad practice. Try not to write such code unless it is really necessary.


```php
public function save(Transaction $transaction, $argument2 = 100)
{
155 156
    $transaction = new Connection; // bad
    $argument2 = 200; // good
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
}
```

### 5.2 Strings

- If string doesn't contain variables or single quotes, use single quotes.

```php
$str = 'Like this.';
```

- If string contains single quotes you can use double quotes to avoid extra escaping.

#### Variable substitution

```php
$str1 = "Hello $username!";
$str2 = "Hello {$username}!";
```

The following is not permitted:

```php
$str3 = "Hello ${username}!";
```

#### Concatenation

Add spaces around dot when concatenating strings:

```php
$name = 'Yii' . ' Framework';
```

When string is long format is the following:

```php
$sql = "SELECT *"
195 196
    . "FROM `post` "
    . "WHERE `id` = 121 ";
197 198 199 200
```

### 5.3 arrays

marsuboss committed
201
For arrays we're using PHP 5.4 short array syntax.
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216

#### Numerically indexed

- Do not use negative numbers as array indexes.

Use the following formatting when declaring array:

```php
$arr = [3, 14, 15, 'Yii', 'Framework'];
```

If there are too many elements for a single line:

```php
$arr = [
217 218 219
    3, 14, 15,
    92, 6, $test,
    'Yii', 'Framework',
220 221 222 223 224 225 226 227 228
];
```

#### Associative

Use the following format for associative arrays:

```php
$config = [
229 230
    'name'  => 'Yii',
    'options' => ['usePHP' => true],
231 232 233 234 235 236 237 238 239 240 241 242 243
];
```

### 5.4 control statements

- Control statement condition must have single space before and after parenthesis.
- Operators inside of parenthesis should be separated by spaces.
- Opening brace is on the same line.
- Closing brace is on a new line.
- Always use braces for single line statements.

```php
if ($event === null) {
244
    return new Event();
245 246
}
if ($event instanceof CoolEvent) {
247
    return $event->instance();
248
}
249 250
return null;

251 252

// the following is NOT allowed:
253
if (!$model && null === $event)
254
    throw new Exception('test');
255 256
```

257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
Prefer avoiding `else` after `return` where it makes sense.
Use [guard conditions](http://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html).

```php
$result = $this->getResult();
if (empty($result)) {
  return true;
} else {
  // process result
}
```

is better as

```php
$result = $this->getResult();
if (empty($result)) {
  return true;
}

// process result
```

280 281 282 283 284 285
#### switch

Use the following formatting for switch:

```php
switch ($this->phpType) {
286
    case 'string':
Alexander Mohorev committed
287
        $a = (string) $value;
288 289 290
        break;
    case 'integer':
    case 'int':
Alexander Mohorev committed
291
        $a = (int) $value;
292 293
        break;
    case 'boolean':
Alexander Mohorev committed
294
        $a = (bool) $value;
295 296 297
        break;
    default:
        $a = null;
298 299 300 301 302 303 304 305 306 307 308
}
```

### 5.5 function calls

```php
doIt(2, 3);

doIt(['a' => 'b']);

doIt('a', [
309 310
    'a' => 'b',
    'c' => 'd',
311 312 313 314 315 316 317 318 319 320 321
]);
```

### 5.6 Anonymous functions (lambda) declarations

Note space between `function`/`use` tokens and open parenthesis:

```php
// good
$n = 100;
$sum = array_reduce($numbers, function ($r, $x) use ($n) {
322 323 324
    $this->doMagic();
    $r += $x * $n;
    return $r;
325 326 327 328 329
});

// bad
$n = 100;
$mul = array_reduce($numbers, function($r, $x) use($n) {
330 331 332
    $this->doMagic();
    $r *= $x * $n;
    return $r;
333 334 335 336 337 338
});
```

Documentation
-------------

MaximAL committed
339
- Refer to [phpDoc](http://phpdoc.org/) for documentation syntax.
340 341 342 343 344 345 346 347 348 349 350 351 352 353
- Code without documentation is not allowed.
- All class files must contain a "file-level" docblock at the top of each file
  and a "class-level" docblock immediately above each class.
- There is no need to use `@return` if method does return nothing.
- All virtual properties in classes that extend from `yii\base\Object`
  are documented with an `@property` tag in the class doc block.
  These annotations are automatically generated from the `@return` or `@param`
  tag in the corresponding getter or setter by running `./build php-doc` in the build directory.
  You may add an `@property` tag
  to the getter or setter to explicitly give a documentation message for the property
  introduced by these methods when description differs from what is stated
  in `@return`. Here is an example:

  ```php
354 355 356 357 358 359 360 361 362 363 364
    <?php
    /**
     * Returns the errors for all attribute or a single attribute.
     * @param string $attribute attribute name. Use null to retrieve errors for all attributes.
     * @property array An array of errors for all attributes. Empty array is returned if no error.
     * The result is a two-dimensional array. See [[getErrors()]] for detailed description.
     * @return array errors for all attributes or the specified attribute. Empty array is returned if no error.
     * Note that when returning errors for all attributes, the result is a two-dimensional array, like the following:
     * ...
     */
    public function getErrors($attribute = null)
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
  ```

#### File

```php
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */
```

#### Class

```php
/**
 * Component is the base class that provides the *property*, *event* and *behavior* features.
 *
 * @include @yii/docs/base-Component.md
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
class Component extends \yii\base\Object
```


#### Function / method

```php
/**
 * Returns the list of attached event handlers for an event.
 * You may manipulate the returned [[Vector]] object by adding or removing handlers.
 * For example,
 *
 * ~~~
 * $component->getEventHandlers($eventName)->insertAt(0, $eventHandler);
 * ~~~
 *
 * @param string $name the event name
 * @return Vector list of attached event handlers for the event
 * @throws Exception if the event is not defined
 */
public function getEventHandlers($name)
{
411 412 413 414 415
    if (!isset($this->_e[$name])) {
        $this->_e[$name] = new Vector;
    }
    $this->ensureBehaviors();
    return $this->_e[$name];
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
}
```

#### Markdown

As you can see in the examples above we use markdown to format the phpDoc comments.

There is additional syntax for cross linking between classes, methods and properties in the documentation:

- `'[[canSetProperty]] ` will create a link to the `canSetProperty` method or property of the same class.
- `'[[Component::canSetProperty]]` will create a link to `canSetProperty` method of the class `Component` in the same namespace.
- `'[[yii\base\Component::canSetProperty]]` will create a link to `canSetProperty` method of the class `Component` in namespace `yii\base`.
- `'[[Component]]` will create a link to the `Component` class in the same namespace. Adding namespace to the class name is also possible here.

To give one of the above mentioned links another label than the class or method name you can use the syntax shown in the following example:

```
... as displayed in the [[header|header cell]].
```

The part before the | is the method, property or class reference while the part after | is the link label.

438 439 440 441 442 443 444
It is also possible to link to the Guide using the following syntax:

```markdown
[link to guide](guide:file-name.md)
[link to guide](guide:file-name.md#subsection)
```

445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477

#### Comments

- One-line comments should be started with `//` and not `#`.
- One-line comment should be on its own line.

Additional rules
----------------

### `=== []` vs `empty()`

Use `empty()` where possible.

### multiple return points

Return early when conditions nesting starts to get cluttered. If the method is short it doesn't matter.

### `self` vs. `static`

Always use `static` except the following cases:

- accessing constants MUST be done via `self`: `self::MY_CONSTANT`
- accessing private static properties MUST be done via `self`: `self::$_events`
- It is allowed to use `self` for recursion to call current implementation again instead of extending classes implementation.

### value for "don't do something"

Properties allowing to configure component not to do something should accept value of `false`. `null`, `''`, or `[]` should not be assumed as such.

### Directory/namespace names

- use lower case
- use plural form for nouns which represent objects (e.g. validators)
marsuboss committed
478
- use singular form for names representing relevant functionality/features (e.g. web)