• Позднее статическое связывание php: как это работает?

    MegaMufa
    @MegaMufa
    Смотрите. Есть такая простая иерархия классов:
    class A
    {
        public static $text = 'class A';
    
        public function selfTest()
        {
            echo self::$text;
        }
    
        public function staticTest()
        {
            echo static::$text;
        }
    }
    
    class B extends A
    {
        public static $text = 'class B';
    }


    Мы создаем экземпляк субкласа и вызываем методы, определенные в предке.
    $obj = new B();
    $obj->selfTest(); // выведет "class A"
    $obj->staticTest(); // выведет "class B"

    self всегда указывает на тот класс, в котором он написал. Здесь метод описан в классе A, и self указывает на класс A, хоть и вызывается из класса B.
    Значение static вычисляется при вызове. И static указывает на класс объекта в котором произошел вызов. В нашем случае он указывает на B, хотя сам код описан в классе A.

    Зачем это надо? Для того, что бы можно было переопределять статичные члены вашего класса в наследниках и вы могли обращаться к новым значениям из методов, описанных в предке.

    С обычными не статичными членами это и так работает, потому что они собираются, когда вы создаете объект класса. Что бы это работало для статичных методов, надо использовать static
    Ответ написан
    1 комментарий
  • Как вы тестируете статические методы?

    He11ion
    @He11ion
    PHP-monkey
    Зависит от Вашей архитектуры, я применяю класс-"обертку" с перегрузкой нужного метода (этим же трюком пользуюсь для изменения вызова функций класса-родителя)

    Как-то так:
    class A { // "родной" класс
    	static function static_call ()
    	{
    		echo 'static_call a';
    	}
    }
    
    class B extends A{ // "обертка" для тестирования
    	static function static_call ()
    	{
    		echo 'static_call b';
    	}
    }
    Ответ написан
    5 комментариев
  • Что представляет собой тестирование ?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    Вообще вики можно для начала, а потом уже углубляться в литературу. Вот вам кратенькое описание, цель которого больше предоставить ключевые слова для поиска.

    Модульные, они же юнит тесты, предназначены для тестирования отдельных модулей/классов. Суть их в том, что мы тестируем поведение только одного класса за раз. Если класс ссылается на инстансы других классов - мы их мокаем. То есть подсовываем им фэйковый класс, который имеет тот же интерфейс, но внутри не реализациа методов, а проверка, вызывали ли метод, с каким аргументами, сколько раз вызывали и т.д. Так же методы мока могут возвращать стабы (заглушки), какие-то захардкоженные под какой-то кейс данные. То есть если мы пишем класс по работе с базой данных, сокет-сервер и т.д., нам стоит соединение с базой, сокеты и т.д. оборачивать в классы, что бы можно было потом подменить на моки это добро. Если у вас в юнит тестах идет реальная работа с файловой системой или что-либо в этом духе, то это уже попахивает интеграционными тестами. Подробнее можно почитать в документации к phpunit. Так же есть такая методология разработки как TDD, советую почитать "Экстримальное программирование" Кента Бэка в этом ключе.

    Сразу хочу отметить что юнит тесты это хорошо, но вот только рядовой разработчик на PHP редко пишет что-то, что стоит покрывать юнит тестами. Времени на их поддержку нужно не мало, а требования у заказчиков частенько меняются. В итоге тесты начинают комментить и толку от них становится ноль. А вот если вы пишите компонент/библиотеку, то тут юнит тесты обязательны (ну... не то что бы, но желательны). Так что я бы на вашем месте сконцентрировал внимание на первом этапе на интеграционных и приемочных тестах.

    Интеграционное тестирование - тестирование нескольких модулей в связке. То есть мы тестируем наш компонент или его самодостаточный кусок в реальных условиях. Если этот компонент для работы с файлами - разрешаем ему доступ к файлам. Если база данных - то даем реальное соединение с базой. А можем что-то и замокать. Это как говорится, зависит от задачи. Скажем обращение к сторонним апишкам стоит мокать и стабить. Главная цель этих тестов, удостовериться что модули вместе работают хорошо. Особенно важно это когда модули пишут разные люди.

    Функциональное тестирование - это тестирования всего приложения в сборе. Если это REST API, то у нас через curl дергаются реальные методы, отправляются более менее реальные запросы и валидируются ответы. Если web-страничка, то это UI тесты с силениумом/phantom.js/zombi.js или, если нам не нужно еще и js тестить, просто curl + какой виртуальный браузер на том же php. Вообще по хорошему функциональные тесты не допускают никаких моков и т.д. но опять же если очень хочется то можно (опять же обращение к сторонним сервисам, контроля за которыми у нас нету).

    Но реалии таковы, что UI тестами покрывать проект не слишком удобно. Вопервых UI может меняться, а поддерживать их так же нужно. Во вторых это скучно. В третьих тесты могут просто падать... вот взяли и упали. И потом начинается, так, тесты опять упали... что там упало? А, не страшно, можно релизить. Так же на больших проектах UI тесты отрабатывают долго, очень долго, некоторых их просто ночью гоняют. Толку от тестов при таком подходе не слишком много, ибо разработчику стоит знать о том что что-то сломалось как можно быстрее. А так он приходит, видит что тесты опять красные, чинит эти красные тесты, и запускает... ждет... проходит пол часа к примеру, и где-то в другом месте отвалилось... Короче сами понимаете.

    Приемочное тестирование - по сути те же функциональные тесты, но подаются в контексте фича-спеков. Если вы работали когда-нибудь с QA отделом, то возможно слышали про такие штуки как acceptance criteria. То есть это тот чек лист, который должен проверить тестировщик что бы удостовериться что все хорошо. На основе этого чек листа можно написать функциональные тесты. Так же есть инструменты вроде Cucumber/Behat, которые позволяют писать спецификации в виде стэпов. В этом случае спецификации для этих инструментов могут писать QA а вы просто имплементите для них степы. То есть уменьшается прослойка между "acceptance criteria" и готовыми к выполнению тестов. Более того, стэпы можно реюзать, комбинировать, масса стэпов есть готовых, вам же необходимо только предоставить стэпы подготвалливающие систему (загрузка/генерация фикстур и т.д.). Короче лепота и удобно. Но медленнее интеграционных, зато не такие жесткие как функциональные, за счет этого их проще поддерживать. QA пишут спеку, реализуем тесты под эту спеку, пишем код под тесты, тесты зеленые - функционал готов.

    Ну и есть еще всякие термины типа пирамида тестов и т.д. Мол лучше много юнит тестов, чуть поменьше интеграционных и мало функциональных. Тогда тесты выполняются быстрее, а покрывать все и вся функциональными тестами обычно перебор.

    Ну и опять же, есть такая вещь как здравый смысл. Некоторые вещи скажем можно вообще забить и не покрывать тестами, некоторые стоит покрыть. Некоторые не полностью, некоторые с как можно большим покрытием.... Скажем тестить UI (именно как выглядит, где какой элемент) вообще бессмысленно. На это нужно куча ресурсов. Хотя может и есть проекты где это оправдано.

    Короче почитайте про TDD и ATDD (можно и BDD затронуть, но тут не только от программиста зависит, менеджеры, заказчик или продукт-оунер тоже должны быть вовлечены, по сути этот подход хорошо работает в рамках продукта какого-то, на фрилансе и в аутсорсе редко можно встретить) , Continious Integration и Continious Delivery.
    Ответ написан
    Комментировать
  • Как сделать цикл for на angular?

    AMar4enko
    @AMar4enko
    Прочитайте о значениях понятий "императивный" и "декларативный", попейте Новопассит.
    Ответ написан
    1 комментарий
  • Что означают двойные фигурные скобки в миграциях yii2?

    Urichalex
    @Urichalex
    Кратко о себе)
    Фигурные скобки заменяются на символы выделения имени таблицы. Например в mysql это символ `
    то есть {{user}} в запрос уйдет как `user`

    Знак процента заменяется на префикс таблицы, если он был установлен в конфиге подключения к базе
    Например, в конфиге указано
    'tablePrefix' => 'qwe_'
    то {{%user}} в запрос попадет как `qwe_user`
    Ответ написан
    Комментировать
  • Как передавать пароль от браузера серверу, как его хранить на сервере и проверять корректность?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    В зависимости от степени безопасности.

    Если разбить все это на "по-проще", общий принцип такой:

    Клиент:
    - в первый раз ввести строку текста
    - нажать отправить
    - больше не вводить, ибо лентяй, а мы ж ему продавать собрались, не дай бог он сука дискомфорт почувствует

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

    Локальных хранилищ тоже бывает куча целая:
    - кукисы (хранится прямо в браузере указанное время)
    - сессии (хранится на сервере, но в кукисах есть ключ к ячейке в "банке", который опять же могут угнать)
    - ... еще десятки способов, о которых знают только крутые кодеры, которые считают себя крутыми просто потому, что их знают, на деле с точки зрения создания ценности людям - бесполезные знания

    Как ты понимаешь, это иллюзия безопасности, а не гарантия.

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

    Вот еще тема, которую василий сказал - шифровать пароль уже на стороне клиента в момент отправки формы яваскриптом. Представьте себя хакером. Вы получили пакеты какого-то чувака, поставили компьютер на расшифровку, подключив десяток видеокарт, расшифровываете пароль, а вместо него видите 64 символьный хеш, ну то есть он прямо от клиента таким вышел. После этого будут маты от того, что теперь массив видюх нужно заставить еще и ключевую фразу из хеша выдрать. Ну то есть еще время декодирования увеличивается. Если загнаться то хеш может быть не просто хешем md5() а например md5 от md5 от развернутой строки прибавленной к md5() прямой строки, прокинутой через побитовый сдвиг на один разряд - ну то есть даже в этом закодировать последовательность действий. При отсутствии https - наиболее надежный способ.

    И потом - программисты Yii не рекомендуют использовать фунцию md5(). Почему? Потому что она быстрая очень. И при переборе пароля массивом видеокарт перебирается он шибко быстро. Поэтому рекомендуют использовать hash функцию, которая для создания хеша использует побольше времени, несколько секунд скажем. При написании синхронной программы (сайта на php, например) - это заставит юзера подождать после регистрации секунды 3-4 лишних, но если загнаться то процесс генерации пароля можно запихнуть в асинхронщину, а пользователю сразу токен выдать, то есть пароль ему сгенерируется чуть позже. Таким образом - за счет более длинной хеш-функции по времени хакеру потребуется куда больше времени - то есть юзеру на прямое создание пароля 5 секунд, а хакеру на перебор нескольких миллионов паролей понадобится несколько лет. Никто ему не заплатит 10 косарей баксов, чтобы сломать ваш долбанный пароль ВКонтакте. Если банковская сфера - опять же как писал выше - хеш от хеша, да развернуть пару раз, да трижды порубить, потом постучать в бубен и все это в хеш еще раз и вуаля - декодирование пароля займет 100 лет.

    Еще значит, При малейшем намеки на несоответствие токена или внезапное изменение - сервак говорит "тю, дядя, давайка пройди второй этап авторизации, а то я не верю, что это реально ты" и просит приложить палец. Или ввести номер, получив СМС. Или вставить ключ-карту синюю (duke nukem hello). Но и тут есть взлом - ключ карту украсть, смс-ку - попросить телефон, приложить палец = отрубить руку, вырвать глаз и тд

    Тут к тебе на помощь приходит https. Https это такая штука, которая в двух словах может быть названа игрой в партизан. На войне играли так - когда радиопереговоры прослушивались, а шифраторов не было - единственным средством защиты - было использовать ошибки в тексте и матерные слова.

    Так и тут - данные отправляемые в виде нулей и единиц. Компьютеры их так же легко перекодируют в пароль, как и закодировали. Чтобы этого не было, придумали ШИФРОВАТЬ строку в одну сторону. У сервака есть трафарет, у пользователя другой трафарет. Сервак шифрует своим, но назад можно расшифровать только пользовательским. Вернее можно и сторонним конечно, но современные компьютеры сделают это за много лет, и потому никто не заморачивается особо, производительность не та.

    Таким образом сайты, где есть https на текущий момент наиболее надежный способ скрыть нули и единицы от чувака, сидящего на прослушке твоего интернет трафика. А попасть он туда может от самых простых способов типа "друг, дай мне пароль от своего вайфай", до всяческих угадываний, взломов роутеров и на худой конец - под видом электромонтера, установив маааленькую плату тебе в распределительный щит.

    Так что авторизация - она простая. Вся надежда на шифрование. А сертификат, на котором пол-мира делает бабло - это средство подтверждения того, что компьютер умеет шифровать. Интересно, что он кончается каждый год, как будто в прошлом году компьютер умел шифровать, а в этом вдруг разучился. На деле конечно - просто гоните бабло, иначе покажем красный крестик вместо зеленого замочка, и в гугле понизим, так что гоните бабки. Вот.
    Ответ написан
    1 комментарий
  • Где можно по шагам разобрать пример разработки сайта на yii 2.0?

    berezuev
    @berezuev
    #define TRUE FALSE
    Лично учился по официальному гайду.
    Есть неполный русский перевод.
    Читается очень легко.

    Плюс всегда под рукой эта книга stuff.cebe.cc/yii2-guide.pdf (там есть вообще всё)
    Ответ написан
    Комментировать
  • Где найти нормальный курс по Yii2?

    webinar
    @webinar Куратор тега Yii
    Учим yii: https://youtu.be/-WRMlGHLgRg
    Добавлю к куче видео курс "Yii2 для Блондинок и Чайников: уроки, заметки, гайды"
    https://www.youtube.com/channel/UC3jTSXXgSvQI2WJ5f...
    Ответ написан
    Комментировать
  • Где найти нормальный курс по Yii2?

    Chvalov
    @Chvalov
    — Книга "Разработка веб-приложений в Yii 2" Марка Сафронова - более глубокое описание возможностей и устройства фреймворка Yii2. Для тех, кто уже в теме. Язык - русский.

    Официальное руководство по Yii2 от разработчиков. Формат PDF, размер порядка 2 МБ

    Забугорный но полезный блог

    Кто что еще может добавить полезного ???
    Ответ написан
    Комментировать
  • Где найти и как установить PHP 5.4 на Ubuntu 16.04?

    romeOz
    @romeOz
    Конечно, можно заморочиться и собрать PHP из исходников. Кроме того, вам придётся собрать и Apache из исходников с добавлением модуля PHP (libapache2-mod-php).
    К сожалению или к счастью, у большинства разработчиков навык сборки чего-либо из исходников безвозмездно утрачен. Разве что расширения для того же PHP (pecl) иногда приходится собирать.
    Как вариант, предлагаю воспользоваться такой замечательной штукой, как контейнеризация, а именно Docker-ом. На текущий момент времени, это наиболее изящное и правильное решение. В приведённый ниже пример добавил линкование с СУБД MySQL

    docker network create mysql_net
    
    docker run --name app --net mysql_net -d -p 8080:80 \
      -v /host/to/path/app:/var/www/app/ \
      -v /host/to/path/config_apache:/etc/apache2/sites-enabled/ \
      romeoz/docker-apache-php:5.4

    , где
    • /host/to/path/app - путь к директории с вашим приложением (от корня), в данном случае, CMS Битрикс
    • /host/to/path/config_apache - путь к директории с вашим apache-конфигом. Можете посмотреть, как выглядит базовый файл конфигурации

    Далее, поднимем контейнер с СУБД:

    docker run --name db --net mysql_net -d \
      -v /host/to/path/data:/var/lib/mysql \
       romeoz/docker-mysql:5.5

    , где /host/to/path/data - директория с вашими данным (БД).

    В CMS Битрикс в качестве хоста в настройках БД, необходимо указать db, т.к. контейнер СУБД имеет такое имя - --name db.
    Ваше приложение будет доступно по адресу http://localhost:8080. Для красоты можете задать в /etc/hosts свой домен, к примеру, 127.0.0.1:8080 myapp.
    Если вы привыкли править БД с помощью web-клиента phpmyadmin, то вот здесь есть инструкция, как поднять официальный контейнер и для него. На вашем месте я бы воспользовался иным другим клиентом - свободным или не свободным. К примеру, во всех продуктах от JetBrains, в том числе, и в IDE PHPStorm, встроен отличный инструмент DataGrip, ранее известный, как 0xDBE.

    Естественно, есть необходимость в этом немного разобраться, но, поверьте, оно того стоит. За этим подходом будущее. Плюс не придётся засирать свою хост-систему всяким мусором, ведь контейнер всегда можно убить, а образ породивший его удалить.
    Ответ написан
    5 комментариев
  • Как экранировать кавычки в Postgresql?

    Апостроф (одинарная кавычка) экранируется апострофом в SQL, т.е.
    WHERE c = ''''''
    * всего 6 апострофов.
    Ответ написан
    Комментировать
  • Почему зависает Ubuntu?

    @mirspo
    0) У убуны была диагностическая утилита
    1) Изучить журналы системы : /var/log/*.
    2) Проанализировать dmesg -l err,warn
    3) Наблюдать вывод vmstat 1, tail -f /var/log/syslog, /var/log/Xorg.0.log до зависания
    Ответ написан
    Комментировать
  • Правильная проверка на пустоту переменной?

    KorsaR-ZN
    @KorsaR-ZN
    Начну с различия между isset и empty.

    isset - проверяет на существование переменной или индекса в массиве, т.е она будет ВСЕГДА true, если значение отлично от NULL, именно от константы null, т.е '', 0, "\0" и т.д, будет считаться существующей переменой, false будет только при присвоение ей null или unset($var), во всех остальных true

    empty - проверят именно на пустоту переменной, т.е возвращает true если переменная является пустой и false в противоположном случаи.

    В PHP пустотой считаются следующие значения:
    • ""
    • 0
    • 0.0
    • "0"
    • null
    • false
    • array() (пустой массив)
    • $var; (переменная объявлена, но не имеет значения)


    Что касается проверки на пустоту и как ее правильно делать, то это исходит от Вашей задачи, и что должно для Вас является пустотой, но в большинстве случаем подойдет такая проверка:
    if(isset($var) && $var !== '')  {
    // $var не null и не  ''.
    }

    Еще больше поможет разобраться в сравнение типов, это таблица сравнение типов PHP
    Ответ написан
  • В чем принципиальное отличие unique (constraints) от unique index?

    alekciy
    @alekciy
    Вёбных дел мастер
    Разница в том, что ограничения (сonstraints) призваны обеспечивать целостность данных, а индексы (index) — скорость доступа к данным. Это две абсолютно не связанные сущности. Причем если первое — часть SQL стандарта, то второе нет (ибо ни как не связанно с функциональностью языка, введение индексов — вынужденная мера). Разработчик сам решает, в каких случая применить эти механизмы и использование одного вовсе не требует использование другого.

    Теперь касательно уникальности (unique). В данном случае при добавлении ограничения уникальности (unique constraint) Postgresql сам навешивает на указанное поле индекс. Это просто особенность реализации в данной СУБД. Разработчики решили, что вот так оно будет работать и все тут (причем небезосновательно). В другой же схожей ситуации они решили, что разработчик сам думает, нужно ли ему использовать этих два механизма вместе, или нет. Я говорю об ограничении целостности по внешнему ключу (foreign key). В Postgresql индексы по полям с данным видом ограничения не создаются (Индексы по внешним ключам в Postgresql). А, к примеру, в MySQL создаются. Это особенность реализации в MySQL.

    Поэтому важно просто понимать, что это не связанные вещи, просто в некоторых реализациях они «сцеплены» между собой и создание некоторых видов ограничений приводит к автоматическому созданию индекса.
    Ответ написан
    2 комментария
  • Вопрос по View (PHP MVC)?

    Вообще говоря, да, для каждого действия нужен свой вид/шаблон, причём вид и шаблон считать синонимами можно только с натяжкой. Или даже несколько видов, среди которых контроллер выбирает нужный в данный момент.

    Использования наследования и включений шаблонов это следование другим практикам и паттернам непосредственно к MVC отношения не имеющим. Как, кстати, не имеет отношения к MVC и «запускается приложение из index.php, метод route разбирает запрос пользователя и отдает управление одному из методов соответствующего контроллера».

    При выборе между наследованием/декорирования и включение посоветовал бы делать упор на первое. Даже без использования шаблонизаторов типа Smarty или Twig оно осуществляется довольно легко с помощью функций ob_*, особенно если достаточно двухуровневого. Метод render может выглядеть примерно так:
    ob_start();
    require $template;
    $content = ob_get_clean();
    require 'layout.php';
    

    layout.php так
    <html>
    <body>
    <div id="header">Шапка</div>
    <div id="content">
      <?= $content ?>
    </div>
    </body>
    </html>
    

    А blog_index так
    <?php foreach($posts as $post): ?>
      <div class="post">
        <h1><?= $post->title ?></h1>
        <?= $post->content ?>
      </div>
    <?php endforeach ?>
    
    Ответ написан
    1 комментарий
  • В каких случая и зачем нужно использовать трейты ?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    Трейты нужны для уменьшения дублирования кода. Причем выносить в них стоит только тупой код, который не имеет смысла покрывать тестами. Все что делает хоть что нибудь умное в трейте хранить не стоит.

    Пример когда трейты хороши - паттерн композиция.

    interface A {
        function foo();
    }
    
    interface B {
        function bar();
    }
    
    trait AImpl {
        function foo() {
            return $this->a->foo();
        }
    }
    
    trait BImpl {
        function bar() {
            return $this->b->bar();
        }
    }
    
    class AB implements A, B {
        private $a;
        private $b;
    
        use AImpl;
        use BImpl;
    
        public function __construct(A $a, B $b) {
            $this->a = $a;
            $this->b = $b;
        }
    }
    Ответ написан
    4 комментария
  • Как учить веб дизайн?

    Portnik
    @Portnik
    Люблю дизайн, графику, шрифты
    Здесь советуют гуглить. Ничерта подобного. Начинающему во всем этом мусоре очень сложно найти толковое. Вход в профессию дизайнера достаточно затуманен. Вот я когда-то специально для начинающих написал пару статей.
    Вводная https://medium.com/design-news-russia-ru/a0326a103de8
    Что и где читать https://medium.com/design-news-russia-ru/72d44885123c
    Ответ написан
    Комментировать
  • Как организовать структуру и деплой проекта с docker?

    KolyaniuS
    @KolyaniuS
    безнадежный оптимист
    Как мне запулить мой проект на этот серв?

    Есть два варианта:
    1. через hub.docker.com
    а) делаете docker login для регистрации на docker-хабе (можно зайти и сделать свой проект приватным чтобы остальным не повадно было)
    б) собираете ваш dockerfile с помощью docker build
    в) затем docker push для отправки слоев на ваш хаб
    г) затем логинитесь на боевом сервере и делаете docker pull для скачивания слоев
    д) docker run
    2. Просто кидаете с помощью scp ваш Dockerfile и файлы проекта на боевой сервер и делаете
    docker build
    Подробнее о командах можно почитать в документации - я лишь описал концепцию
    Как затем производить правки в коде?

    Все просто - залейте ваш проект в любой репозиторий (github, bitbacker, gitlab ...), после внесения изменений просто логинитесь на сервере, заходите в ваш докер-контейнер и запускаете git pull в нужную директорию, затем сборка или т.п. (для автоматизации процесса можно использовать любой CI).
    Проекту нужна БД(куда без нее).

    Очень просто - добавляете новый контейнер (например docker pull mysql), на хабе можно посмотреть информацию о запуске такого контейнера https://hub.docker.com/_/mysql/ и коннектитесь к базе из вашего приложения по внутренней сети вашей docer-системы (docker bridge).
    Ответ написан
    1 комментарий