В php как и в большинстве других ООП языков существуют модификаторы видимости. Это ключевые слова public, protected и private. Но они применимы исключительно к свойствам, методам или константам. Данные модификаторы тесно связаны с возможностью инкапсуляции данных. Стоит заметить, что в таких языках как java, C#, go (https://golang.org/doc/go1.4#internalpackages), ruby (https://habr.com/post/419969/), crystal (https://crystal-lang.org/reference/syntax_and_semantics/visibility.html) есть возможность ограничивать область видимость пакетов (packages) или классов\типов. В php нет возможности ограничивать область видимости для классов — любой подключенный класс доступен из любого другого класса. Однако можно эмулировать данную возможность с применением нескольких трюков.
Для чего вообще может понадобиться сокрытие на уровне классов:
- Служебные классы (хелперы) библиотеки — не замусориваем API библиотеки не имеющими смысла internal классами.
- Инкапсуляция с сокрытием внутренних объектов "бизнес логики", например запрет прямого порождения зависимых объектов в обход более общего класса.
Отдельно можно выделить разбиение "больших" классов на мелкие объекты. Хорошей практикой считается ограничивать сложность (и количество строк) как отдельных методов так и классов. Количество строк тут идёт как один из маркеров, что метод класса или сам класс берёт на себя лишнию ответственность. При рефакторинге public метода мы выносим его части в private\protected методы. Но когда по тем или иным причинам класс разрастается и мы выделяем из него отдельную сущность, эти самые private\protected классы переносятся в отдельный класс, тем самым мы косвенно открываем доступ к методам, которые ранее были ограничены областью видимости одного класса.
Теперь собственно сами способы эмуляции сокрытия.
На уровне соглашения оформления кода
Используя PHPDoc комментарии можно отметить класс, трэйт или интерфейс как internal
(http://docs.phpdoc.org/references/phpdoc/tags/internal.html). При этом некоторые IDE (например PhpStorm) могут понимать такие метки.
Использовать runtime информацию
Во время исполнения кода можно проверить откуда был вызван конструктор класса. Например через метод debug_backtrace
(http://php.net/manual/ru/function.debug-backtrace.php) или использовать аналогичный функционал Xdebug для контроля кода в dev\test окружении. Пример оформленного решения есть тут (https://coderwall.com/p/ixvnga/how-emulates-private-class-concept-in-php).
/**
* The private class
*/
final class PrivateClass
{
/**
* defines the only class able to instantiate the current one
*
* @var string
*/
private $allowedConsumer = 'AllowedPrivateClassConsumer';
/**
* constructor
*
* @throws Exception
*/
public function __construct()
{
/**
* here comes the privacy filter, it could be extracted to a private method
* or to a static method of another class with few adjustments
*/
$builder = debug_backtrace();
if (count($builder) < 2 ||
!isset($builder[1]['class']) ||
$builder[1]['class'] !== $this->allowedConsumer)
{
throw new Exception('Need to be instantiated by '.$this->allowedConsumer);
}
}
}
Использовать анонимные классы
Относительно новый функционал в php — анонимные классы (http://php.net/manual/ru/language.oop5.anonymous.php). Описав анонимный класс внутри защищенного метода мы добиваемся его сокрытия. Что бы не городить портянку определения класса внутри функции, можно описать "приватный" класс в отдельном файле как абстрактный, и уже расширять его в определении анонимного класса. Хороший пример использования данного метода есть по этой ссылке (https://markbakeruk.net/2018/06/25/using-php-anonymous-classes-as-package-private-classes/).
Исходя из найденного материала видно, что функционал сокрытия классов в какой то мере востребован (и существует во многих языках), однако практика его использования очень ограничена, возможно отсутствием описанием примеров в различных "best practices", сборников шаблонов и подобных источниках. Что на мой взгляд является довольно странным, что есть акцентирование на сокрытие внутренних методов и свойств объектов, но почти никто не обращает внимание, что более крупные логические куски кода в виде служебных классов библиотек или доменной области остаются в глобальном пространстве видимости.
SDKiller
Имхо, использовать debug_backtrace накладно
GHostly_FOX
Поддерживаю, да еще и без параметров в большом проекте это чревато…
ZurgInq
Абсолютно верное замечание. Такие ресурсоемкие не обязательные операции часто помещают под флаг, что бы они отключались на prod серверах. Как пример — php функция assert.