PHP
Руководство по использованию PHP.
Типизируйте
Обязательно типизируйте переменные, свойства классов, аргументы и возвращаемые типы методов и функций.
// Хорошо
class ImageService
{
protected Storage $storage;
protected string $folder;
public static function store(string $path): string
{
// ...
}
}
// Плохо
class ImageService
{
protected $storage;
protected $folder;
public static function store($path)
{
// ...
}
}
Не пишите бесполезные docblock комментарии
// Хорошо
class Foo
{
public string $name;
}
// Плохо
class Foo
{
/** @var string */
public $name;
}
// Хорошо
class Url
{
public static function fromString(string $url): string
}
// Плохо
// Переменные уже типизированы. Docblock только загрязняет код
class Url
{
/**
* Преобразует строку в URL.
* @param string $url
* @return string
*/
public static function fromString(string $url): string
}
Тернарные операторы
Правильно переносите тернарные операторы
// Хорошо
$result = $object instanceof Model
? $object->name
: 'A default value';
$name = $isFoo ? 'foo' : 'bar';
// Плохо
$result = $object instanceof Model ?
$object->name :
'A default value';
Всегда пишите скобки для условий
// Хорошо
if ($condition) {
...
}
// Плохо
if ($condition) ...
Старайтесь избегать else
Старайтесь не писать большую вложенность для условий. Например используя способ раннего выхода (early return)
// Хорошо
if (! $conditionBA) {
// Условие А не прошло
return;
}
if (! $conditionB) {
// Условие А прошло,
// но условие B не прошло
return;
}
// Оба условия прошли
// Плохо
if ($conditionA) {
if ($conditionB) {
// Оба условия прошли
} else {
// Условия А прошло,а В нет
}
} else {
// Условие А не прошло
}
Предпочитайте читабельность короткому коду
// Хорошо
if ($account->has_access_to_paid_version) {
return false;
}
if (!config('app.requires_subscription')) {
return false;
}
if ($account->isSubscribed()) {
return false;
}
return true;
// Плохо
if ($account->has_access_to_paid_version
|| (!config('app.requires_subscription')
|| $account->isSubscribed()
) {
return false;
}
return true;
Пробелы
Разделяйте пробелами логические участки кода
// Хорошо
public function getPage($url)
{
$page = $this->pages()->where('slug', $url)->first();
if (!$page) {
return null;
}
if ($page['private'] && ! Auth::check()) {
return null;
}
return $page;
}
// Плохо
public function getPage($url)
{
$page = $this->pages()->where('slug', $url)->first();
if (!$page) {
return null;
}
if ($page['private'] && ! Auth::check()) {
return null;
}
return $page;
}
Выносите захардкоженные значения в константы
// Плохо
class User
{
public $access = 7;
}
if ($user->access & 4) {
// ...
}
$user->access = 2;
// Хорошо
class User
{
public const ACCESS_READ = 1;
public const ACCESS_CREATE = 2;
public const ACCESS_UPDATE = 4;
public const ACCESS_DELETE = 8;
public $access = self::ACCESS_READ | self::ACCESS_CREATE | self::ACCESS_UPDATE;
}
if ($user->access & User::ACCESS_UPDATE) {
}
$user->access = User::ACCESS_CREATE;
Не добавляйте бесполезный контекст
// Плохо
class Car
{
public $carMake;
public $carModel;
public $carColor;
//...
}
// Хорошо
class Car
{
public $make;
public $model;
public $color;
//...
}
Используйте современный синтаксис
Если это позволяет версия PHP на текущем проекте
// Хорошо
// Этот код делает тоже самое, что и код ниже
$name = $_GET['name'] ?? $_POST['name'] ?? 'nobody';
// Плохо
if (isset($_GET['name'])) {
$name = $_GET['name'];
} elseif (isset($_POST['name'])) {
$name = $_POST['name'];
} else {
$name = 'nobody';
}
// PHP 8
// Хорошо
// Этот код делает тоже самое, что и код ниже
class CustomerDTO
{
public function __construct(
public string $name,
public string $email,
public DateTimeImmutable $birth_date,
) {}
}
// Плохо
class CustomerDTO
{
public string $name;
public string $email;
public DateTimeImmutable $birth_date;
public function __construct(
string $name,
string $email,
DateTimeImmutable $birth_date
) {
$this->name = $name;
$this->email = $email;
$this->birth_date = $birth_date;
}
}
// PHP 8
// Хорошо
// Этот код делает тоже самое, что и код ниже
$dateAsString = $booking->getStartDate()?->asDateTimeString();
// Плохо
$startDate = $booking->getStartDate();
$dateAsString = $startDate ? $startDate->asDateTimeString() : null;
// PHP 8
// Хорошо
// Этот код делает тоже самое, что и код ниже
$message = match ($statusCode) {
200, 300 => null,
400 => 'not found',
500 => 'server error',
default => 'unknown status code',
};
// Плохо
switch ($statusCode) {
case 200:
case 300:
$message = null;
break;
case 400:
$message = 'not found';
break;
case 500:
$message = 'server error';
break;
default:
$message = 'unknown status code';
break;
}И
Интерфейсы
Если есть интерфейс и несколько возможных реализаций. В этом случае интерфейс стоит назвать естественным именем, а в именах реализаций использовать префиксы, которые описывают эту реализацию
// Хорошо
interface Storage {}
class S3Storage implements Storage {}
class FileStorage implements Storage {}
// Плохо
interface StorageInterface {}
class Storage implements StorageInterface {}
// Все последующие реализации выглядят как вторичные после верхней
Полезные ссылки
Last updated