Другое

Полное руководство по перечислениям PHP: решения и обходные пути

Изучите эффективные обходные пути для перечислений PHP до версии 8.1. Узнайте о решениях на основе классов, интерфейсов и констант с поддержкой автодополнения в IDE.

Перечисления в PHP: решения и обходные пути

Я знаю, что PHP пока не имеет собственных перечислений, но я привык к ним из мира Java. Я бы очень хотел использовать перечисления как способ предоставления предопределенных значений, которые могут распознавать функции автодополнения IDE.

Константы могут работать, но у них есть проблемы с коллизиями имен из-за их глобального характера. Массивы не имеют проблемы с пространствами имен, но они слишком расплывчаты, могут быть перезаписаны во время выполнения, и IDE редко знают, как автоматически заполнять их ключи без дополнительных аннотаций или атрибутов статического анализа.

Какие решения или обходные пути вы обычно используете для реализации перечислений в PHP? Были ли какие-либо обсуждения или решения от команды разработки PHP относительно внедрения собственных перечислений?

Перечисления в PHP: Текущее состояние и будущие возможности

В PHP до сих пор отсутствуют нативные перечисления, но существует несколько эффективных способов для имитации их поведения. Наиболее популярные решения включают использование классов, интерфейсов или констант с правильной обработкой пространств имен, каждое из которых предлагает различные преимущества для автодополнения IDE и безопасности типов. Нативные перечисления появятся в PHP 8.1 (выпущен в ноябре 2021 года), что наконец обеспечит встроенную поддержку этой распространенной программной конструкции.

Содержание

Понимание текущего состояния перечислений в PHP

PHP исторически не имел встроенной поддержки перечислений, что было заметным ограничением по сравнению с другими современными языками программирования, такими как Java, C# и Python. Это отсутствие заставило разработчиков создавать собственные системы перечислений с использованием существующих возможностей языка. Основная трудность заключается в создании решения, которое обеспечивает как безопасность типов, так и поддержку IDE, избегая при этом коллизий пространств имен.

Как объясняется на ExceptionsHub, разработчики, привыкшие к перечислениям из других языков, часто ищут способы реализации аналогичной функциональности в PHP. Основные требования обычно включают:

  • Фиксированный набор предопределенных значений
  • Возможности проверки типов
  • Поддержку автодополнения в IDE
  • Изоляцию пространств имен для избежания коллизий
  • Неизменяемость во время выполнения

Перечисления на основе классов

Наиболее широко используемое решение - реализация перечислений с использованием классов. Этот подход использует объектно-ориентированные возможности PHP для создания структурированной системы перечислений.

php
class Status {
    const ACTIVE = 'active';
    const INACTIVE = 'inactive';
    const PENDING = 'pending';
}

Более сложная реализация включает создание универсального базового класса перечисления и его расширение для конкретных перечислений:

php
abstract class Enum {
    private $value;
    
    protected function __construct($value) {
        $this->value = $value;
    }
    
    public function getValue() {
        return $this->value;
    }
    
    public function __toString() {
        return (string)$this->value;
    }
}

class Status extends Enum {
    const ACTIVE = 'active';
    const INACTIVE = 'inactive';
    const PENDING = 'pending';
    
    private function __construct($value) {
        parent::__construct($value);
    }
    
    public static function ACTIVE() {
        return new self(self::ACTIVE);
    }
    
    public static function INACTIVE() {
        return new self(self::INACTIVE);
    }
    
    public static function PENDING() {
        return new self(self::PENDING);
    }
}

Как отмечено на Stack Overflow, “наиболее распространенное решение, которое я видел для перечислений в PHP, - это создание универсального класса перечисления, а затем его расширение”. Этот подход обеспечивает лучшую безопасность типов и может быть дополнен дополнительными методами.

Перечисления на основе интерфейсов

Другой популярный подход - использование интерфейсов для определения констант перечислений. Этот метод является просто вопросом предпочтения по сравнению с решениями на основе классов, но предлагает схожие преимущества:

php
interface Status {
    const ACTIVE = 'active';
    const INACTIVE = 'inactive';
    const PENDING = 'pending';
}

Как отмечено в блоге PHP Classes, этот подход “использует имена классов как способ определения перечисленных значений” и может помочь “решить следующие проблемы: 1. Константы с разными именами и равными значениями могут использоваться в объявлении аргументов функций, позволяя PHP выполнять проверку типов с использованием поддержки подсказок типов”.

Решения на основе констант

Простой подход использует глобальные константы, но он имеет значительные недостатки:

php
define('STATUS_ACTIVE', 'active');
define('STATUS_INACTIVE', 'inactive');
define('STATUS_PENDING', 'pending');

Хотя его легко реализовать, этот метод страдает от проблем коллизий пространств имен, поскольку константы являются глобальными. Однако их можно организовать с использованием пространств имен для смягчения этой проблемы:

php
namespace App\Enums;
const ACTIVE = 'active';
const INACTIVE = 'inactive';
const PENDING = 'pending';

Обеспечение поддержки автодополнения в IDE

Одна из самых больших проблем с пользовательскими реализациями перечислений - поддержание надежной поддержки IDE. Существует несколько стратегий, которые могут помочь преодолеть это ограничение:

Комментарии PHPDoc и подсказки типов

Добавление полных комментариев PHPDoc значительно улучшает возможности автодополнения в IDE. Как рекомендует Uptimia:

php
/**
 * @param string $status One of Status::ACTIVE, Status::INACTIVE, or Status::PENDING */
public function setStatus(string $status) {
    // реализация
}

Подход на основе иерархии классов

Реализация перечислений как иерархии классов помогает поддерживать автодополнение кода в IDE и предупреждения. Как показано в этом ответе на Stack Overflow, “Вы можете сохранять автодополнение кода, предупреждения в IDE и иметь улучшенную безопасность, реализуя ваши перечисления как иерархию классов вместо.”

Расширения IDE и инструменты

Несколько расширений IDE могут улучшить возможности автодополнения для PHP:

  • PHP IntelliSense для Visual Studio Code обеспечивает расширенное автодополнение и поддержку рефакторинга
  • Плагин idea-php-advanced-autocomplete для PhpStorm добавляет поддержку автодополнения для различных встроенных функций PHP
  • Технология Tree-sitting позволяет IDE становиться осведомленными о контексте, добавляя классы, переменные и методы в сценарии автодополнения

Как отмечено в обсуждениях на Reddit, “Функция, которую вы ищете, - это tree-sitting. Она позволяет IDE становиться осведомленной о контексте.”

Нативные перечисления PHP: Будущее

Хорошая новость для разработчиков PHP: нативные перечисления наконец-то появляются! Выпуск PHP 8.1 включает встроенную поддержку перечислений, отвечая на долгосрочные запросы сообщества PHP.

Официальный RFC был предложен Ларри Гарфилдом (Larry Garfield) и Илией Товило (Ilija Tovilo), голосование по которому закончилось в пользу реализации (44 голоса “За”). Нативные перечисления PHP предоставят:

php
enum Status {
    case ACTIVE;
    case INACTIVE;
    case PENDING;
}

// Использование
$status = Status::ACTIVE;
$status instanceof Status; // true

Ключевые особенности нативных перечислений PHP включают:

  • Unit Enums: Простые перечисления без базовых значений
  • Backed Enums: Перечисления со строковыми или целочисленными базовыми значениями
  • Безопасность типов: Правильная проверка и валидация типов
  • Поддержка IDE: Полное автодополнение и поддержка статического анализа
  • Сериализация: Специальная обработка сериализации, отличающаяся от объектов

Как объясняется на Stitcher.io, “Поскольку значения перечислений на самом деле являются объектами, в настоящее время невозможно использовать их в качестве ключей массива. Следующий код приведет к ошибке…”

Хотя нативные перечисления решают многие проблемы, у них есть некоторые ограничения, о которых разработчикам следует знать. Однако эти ограничения решаются в будущих RFC, таких как RFC ADT/Tagged Union для неитерируемых перечислений.

Лучшие практики и рекомендации

При выборе подхода к перечислениям для вашего PHP-проекта учитывайте следующие рекомендации:

  1. Для проектов на PHP 8.1+: При возможности используйте нативные перечисления для лучшей безопасности типов и поддержки IDE.

  2. Для устаревших проектов: Рассмотрите подход на основе классов с правильными аннотациями PHPDoc для лучшего баланса функциональности и поддержки IDE.

  3. Организация пространств имен: Всегда заключайте ваши константы или классы в соответствующие пространства имен для избежания коллизий.

  4. Подсказки типов: Используйте правильные подсказки типов в сигнатурах функций для улучшения вывода типов в IDE и безопасности типов.

  5. Документация: Предоставляйте полные комментарии PHPDoc, объясняющие допустимые значения перечислений.

  6. Валидация: Реализуйте логику валидации для обеспечения принятия только допустимых значений перечислений.

  7. Планирование миграции: Если вы используете пользовательские реализации, планируйте миграцию на нативные перечисления при обновлении до PHP 8.1+.

RFC по перечислениям PHP представляет значительный прогресс в эволюции PHP в сторону более надежных типизированных систем. Как отмечено на Hacker News, “Когда я строил небольшой внутренний инструмент несколько лет назад, я не мог поверить, что до сих пор в нем отсутствует что-то такое базовое, как тип перечисления.”


Заключение

Разработчики PHP имеют несколько эффективных обходных решений для реализации перечислений до появления нативной поддержки в PHP 8.1. Подход на основе классов предлагает лучший баланс функциональности, безопасности типов и поддержки IDE, в то время как решения на основе констант обеспечивают простоту в ущерб изоляции пространств имен. С появлением нативных перечислений в PHP 8.1 разработчики, наконец, могут использовать эту мощную языковую возможность без пользовательских реализаций.

Для максимальной выгоды:

  • Мигрируйте существующие пользовательские реализации перечислений на нативные при обновлении до PHP 8.1+
  • Используйте полные комментарии PHPDoc для улучшения поддержки IDE в устаревшем коде
  • Рассмотрите расширения IDE, которые улучшают возможности статического анализа
  • Планируйте будущие RFC PHP, которые решат текущие ограничения перечислений

Появление нативных перечислений знаменует важный этап в эволюции PHP, bringing его больше в соответствие со стандартами современных языков программирования и повышая производительность разработчиков за счет лучшей поддержки инструментов.

Источники

  1. Enumerations on PHP - Stack Overflow
  2. PHP and Enumerations - Stack Overflow
  3. How Would You Create Enums in PHP - PHP Classes
  4. How To Use Enumerations In PHP? - Uptimia
  5. PHP and Enumerations - ExceptionsHub
  6. php - enumerations in function’s parameters - Stack Overflow
  7. r/PHP on Reddit: Free auto completion editor or IDE for PHP that works like PhpStorm
  8. r/PHPhelp on Reddit: VSCode Intellisense and PHP
  9. PHP IntelliSense - Visual Studio Marketplace
  10. GitHub - idea-php-advanced-autocomplete
  11. PHP: rfc:enumerations
  12. Native enumerations (enums) are coming in PHP 8.1 - Amit Merchant
  13. PHP 8.1: Enums - Stitcher.io
  14. PHP Enums Just Landed in PHP 8.1 - PHP Magazine Network
  15. Designing with PHP 8.1 Enumerations - DEV Community
  16. PHP 8.1 RFC List • PHP.Watch
  17. [RFC] Enumerations - Externals
  18. PHP RFC: Enumerations - Hacker News
Авторы
Проверено модерацией
Модерация