PHP: <?php namespace core; trait Singleton { private static $instance; private static function getInstance() { return self::$instance = self::$instance ?? new self(); } } подключаем трейт PHP: <?php namespace core; use PDO; class Database { use Singleton; public static $count_instances = 0; private static $pdo; private function __construct() { self::$count_instances++; $database = Config::file('database'); self::$pdo = new PDO($database['dsn'], $database['username'], $database['passwd'], $database['options']); } public static function getServerVersion() { return self::getInstance()::$pdo->getAttribute(PDO::ATTR_SERVER_VERSION); } } Что имеем? Всё прекрасно работает, НО PhpStorm в методе getServerVersion() подсвечивает - свойство ::$pdo не найдено в Singleton Шторм перестаёт светить это свойство, если я методу getInstance() пропишу доки /** @return static */ либо пропишу new static() вместо new self(). Ну так а как реально должно выглядеть создание инстанса в трейте: self или static И ПОЧЕМУ? И если static, то особенно ПОЧЕМУ? Разве трейт не часть класса, в котором создаётся экземпляр, разве он не self? Повторяю: всё работает, интересует поведение шторма: почему просит вернуть ему из трейта static методом или доками - это ошибка разработчиков IDE или есть основания?
Попробуй, ругается ? PHP: <?php trait Singl { private static DB $inst; public static function getInst(): DB // : return type { return self :: $inst = self :: $inst ?? new self; } } class DB { private static int $p = 0; use Singl; private function __construct() { self :: $p++; } private static function getP() { return self :: $p; } public static function run() { return self :: getInst() :: getP(); } } var_dump ( DB :: run() );
@MouseZver Фишка трейта пропадает. У ТС видимо много синглтонов, он не хочет писать каждый раз одно и то же. @Вероломство Ну на то, что там рисует шторм, можно иногда и забить. Я всё, что он мне рисует, читаю, но часть его замечаний посылаю нафиг. И сейчас идёт тенденция к тому, чтобы вместо синглтонов использовать DI-контейнеры, и получать зависимости из них. Синглтон уже считается устаревшим паттерном. Он, в принципе, обладает всеми недостатками обычной глобальной переменной.
всё верно, трейт нужен многократно (может быть), контейнер не нужен: нет смысла таскать с собой массив объектов из объекта в объект, я могу и Реестром это сделать, проект простой, достаточно Одиночки, но я с трейтами если честно первый раз связался, нужно суть понять - это наследование или добавление кода в существующий класс? А то в инстансе и при static и при self у меня экземпляр класса, куда подключен трейт, следовательно это не наследование, но IDE подсвечивает при new self() свойства класса, как несуществующие в трейте (нельзя же создать экземпляр трейта, откуда сообщение, что в нём нет свойства?) , то есть IDE просит создавать экземпляр, как бы наследника в родителе через static, почему? Ошибка IDE? Получается так, ОК, порешали.
Автоматизированная копипаста Сделана в принципе для имитации множественного наследования, но по сути, просто код трейта вставляется в класс, как если бы ты его туда закопипастил. Есть конечно некоторые трюки по переименованию методов, чтоб было не совсем уж примитивно, но когда ты просто пишешь use без других конструкций, получается копипаста. Так не надо ничего таскать, нормальные реализации di поддерживают автовайринг. Но дело твоё. Так, для справки оставлю, может кто ещё зайдёт: https://php-di.org/ --- Добавлено --- Ну с ПХП приходится практически искусственный интеллект на стороне IDE разводить, может где-то и не верно понять Но код исполняет не она, поэтому можно иногда и плюнуть на то, что там она нарисовала
php-di видел, навскидку сделано неплохо, но так и не понял, как коллбэк-вызов спасает от того, что в контейнере массив объектов мы таскаем потом за собой, ну вызвали на момент надобности и потом не надо, может я не до конца вник? Так я могу и просто контейнер создать с методом get, который будет проверять по ключу есть ли объект, если нет, то создавать его методом set (вызванном внутри метода get) и возвращать, но он же в контейнере останется не ну я чёт в DI не понимаю, если не нужен объект, где он после того, когда его вызвали, где он нужен? В контейнере, в массиве, который пронизывает весь скрипт? А смысл? Я и так могу нужный мне экземпляр создать там где надо как минимум одним способом - new
Прочитай про инверсию зависимостей, один из принципов SOLID. Особенно в случае PHP это вообще не важно, через несколько миллисекунд мучения этого объекта закончатся. --- Добавлено --- Метод гет - это не самое крутое в контейнере. Самое крутое https://php-di.org/doc/autowiring.html
Лично мне понравилась Lazy предзагрузка с использованием контейнера. https://github.com/MouseZver/ContainerPHP7/blob/master/src/Container.php на 34 строке ошибка, если класс может использоваться как fun need рефлексию класса.
и каждый раз перезаписывать, чтобы иметь доступ по мере вызова, так это вроде не новость или в чём фишка?
ну ты там убеждаешься что является инстансом и поэтому опять переписываешь что ли... так это я уже описывал проще: метод set() в методе get() при отсутствии и возврат либо возврат существующего, зачем перезаписывать если есть, просто вернуть, не? --- Добавлено --- и там проверка странная, если не null, то вернём null PHP: if ( ! isset ( $this -> container[$name] ) ) { return null; }
PHP: <?php use Nouvu\{ Container, Config }; $container = new Container; // Загружаем контейнер классами без их иницилизации # - Database class $container -> set( 'database', function ( Container $cont ): \PDO { $database = $cont -> get( 'config' ) -> get( 'Database.connect' ); return new \PDO( $database['dsn'], $database['username'], $database['passwd'], $database['options'] ); } ); # - Config class $container -> set( 'config', fn( Container $cont ) => new Config ); // - end // что - то для примера class test { protected Container $container; public function __construct( Container $cont ) { $this -> container = $cont } public function getHrenZnaet( ) { $sql = 'SELECT ...' return $this -> container -> get( 'database' ) -> query( $sql ); } } $test = new test( $container ); print_r ( $test -> fetchAll() );
и что это за ПЕРЕХИТРИМ ВСЕХ: загружаем классы без инициализации? Загружаем в таком случае массив неймспейсов и по мере обращения делаем тупо NEW тем же самым геттером
чего ? каким боком там null если тебе ошибку выкинет при не существовании класса с определенным ключём ? PHP: $a = [ 1,2 ]; if ( ! isset ( $a['database'] ) ) { echo 666; } --- Добавлено --- один раз загружается а дальше берет экземпляр УЖЕ загруженного класса Если тип в контейнере "функция", то обращаемся к ней как к функции, а значение перезаписываем. Дальше при обращении контейнер не определит значение как функцию, так как будет там уже класс $this -> container[$name] instanceof \Closure ===== false PHP: if ( $this -> container[$name] instanceof \Closure ) { $this -> container[$name] = $this -> container[$name]( $this ); }
PHP: $a = null; if (!isset($a)) { echo 'А зачем ты проверил меня на NULL и вернул NULL?'; return null; } --- Добавлено --- @MouseZver слово перезапись я вижу или как?
откуда у тебя $a без типа массива ? Как проверяешь массивы на сущ ключи ? --- Добавлено --- PHP: $this -> container[$name] = $this -> container[$name]( $this ); Ты прикалываешься или реально не видишь ?
@MouseZver, смотри у меня в контейнере экземпляр user (User, то есть МЕНЯ), как мне посмотреть другого User по ключу контейнера user? Это одна и та же сущность только айдишник другой будет, мне нужно видеть свои некоторые данные в шаблоне и данные другого пользуна используя одну и ту же сущность... в контейнере по ключу user - Я
вот и разрушился контейнер, нельзя передать одну сущность из контейнера для нескольких и будем таскать за собой всю эту дичь передавая из класса в класс вместо того чтобы вызвать по месту отработать, записать данные в БД и никакой другой класс об этом даже не будет знать: если я смотрю профиль второго юзера, то зачем мне контейнер с экземплярАМИ третьего, четвёртого, пятого и т.д., которыЕ создавались и помещались в контейнер по мере захода на них, правильно? А вот если мы храним в контейнере неймспейс, то по месту на его основании мы создаём и присваиваем экземпляр, НО НЕ берём уже готовый. Так зачем мне массив даже неймспейсов, я сделаю NEW в любом месте.
То, что лежит в контейнере - большей частью аналоги синглтонов, т.е. они в нескольких экземплярах не нужны в системе. То есть положить туда текущего юзера нормально, а если нужен другой юзер - ок, запрашиваешь другого юзера из репозитория или ещё откуда-нибудь, как у тебя заведено. Чем лучше синглтонов - тем что не создаётся прямая зависимость, можно вешать разные реализации, можно вешать по интерфейсу, и менять реализацию, в зависимости от текущей задачи, отладки, тестирования и т.п.