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