• Symfony каталог товаров с динамичной фильтрацией?

    Самое простое решение в данном случае - использовать в базе данных дополнительное поле с типом данных JSON, в котором и хранить объект с такими дополнительными характеристиками. Т.е. мы тут как бы совмещаем функционал нашей SQL базы данных с функционалом документных NoSQL баз.
    И доктрина и сами современные SQL базы позволяют искать данные по JSON.

    Более традиционный вариант - это создание двух таблиц. Первая таблица options_list будет содержать список самих характеристик. Поля могут быть, например:

    id - код характеристики,
    name - наименование характеристики

    Во второй таблице options_values будут храниться уже сами значения характеристик.
    И у неё будут поля:

    id - код значения характеристики,
    product_id - код товара из таблицы products (или как там у вас), для которого создается характеристика. FOREIGN KEY,
    option_id - код самой характеристики из таблицы options_list. FOREIGN KEY,
    value - значение характеристики 
    Ответ написан
  • Как скачать весь контент с сайта?

    Сайт полностью сгенерирован Javascript уже в браузере после загрузки страницы. В самом исходном коде html никаких данных нет. Поэтому скачать обычным образом его не получится.

    Хорошо, что прекрасные люди придумали для этой задачи расширения для браузера. Например, вот такое https://chromewebstore.google.com/detail/save-page...

    Нажав на кнопку расширения, будет скачана вся страничка полностью вместе со всеми текстами, стилями и изображениями в формате base64
    Ответ написан
  • Как решить зависимость класса от другого класса?

    У вас циклическая зависимость. Это первейший признак того, что вы что-то неправильно спроектировали. Выносите функционал, используемый обоими классами, в третий класс. Тогда вы избавитесь от циклической зависимости.
    Ответ написан
    Комментировать
  • MacBook Air M1 8/256 подойдёт для разработки?

    У меня Macbook Pro M1 Pro 16/512.
    Когда запускаешь какую-то IDE от Jetbrains типа Goland, то батарея тает на глазах, да и памяти она кушает хорошо.

    Если будете работать в VScode, то такой проблемы возникнуть не должно, если не увлекаться плагинами. Сам Go потреблять много ресурсов не будет. Основной потребитель памяти - это всегда большое количество вкладок в браузере.

    Но... Как только вам понадобится Docker, могут начаться проблемы. На Маке он не нативный, и может сжирать много памяти. У меня много чего в Докере запускается, и вкладок в браузере всегда несколько требуется, поэтому мне 16 прям впритык.

    Мой совет - брать машину с 16гб минимум, но тогда уже нет смысла брать Air...

    P. S. Мак в разы лучше работает с памятью, чем винда, поэтому сравнивать не всегда корректно.
    Ответ написан
    2 комментария
  • Как реогранизовать процесс разработки в IT-продукте?

    1. Git. Без него сейчас ну просто никак.
    2. Gitlab CI или GitHub actions для деплоя.
    3. Пишете скрипт, который при запуске на локалке создаёт новую базу и заполняет её фейковыми данными для тестов. Так новые разработчики не будут иметь доступ к данным.
    3. Новые разработчики будут иметь доступ к коду, смогут создавать свои ветки в Git, пушить эти свои ветки в ваш удаленный репозиторий Git и даже создавать merge request на слияние их ветки с основной веткой разработки. А вы уже сможете сделать ревью их кода и подтвердить слияние, либо отказать. Все права отлично настраиваются как в Gitlab, так и в GitHub.
    4. Если же вы вообще не хотите показывать даже код сторонним разработчикам, то тут без модулей либо даже микросервисов не обойдешься. Я бы начал пробовать с модулей.

    P. S. Если в голову придёт мысль переписать проект полностью, то десять тысяч раз подумайте перед этим. В моей практике нет ни одного успешного переписывания сложного проекта... Всегда нужны новые срочные фичи, и придется параллельно внедрять эти фичи в старую кодовую базу, одновременно с этим пытаться их внедрить в сырую переписываемую кодовую базу. Это может закончиться ещё более запутанным проектом, чем был оригинал.
    Ответ написан
    Комментировать
  • Как заблокировать работу с окном браузера при перезагрузке страницы?

    Всем "опасным" кнопкам по умолчанию устанавливаем атрибут disabled. Тогда их нельзя будет нажать.

    <button class="dangerous"  disabled>Сохранить</button>


    И сделать обработчик события загрузки страницы, который уберет этот самый атрибут с опасных кнопок по окончании полной загрузки страницы

    document.addEventListener(‘load’, function() { 
    	const dangers = document.querySelectorAll(".dangerous");
            dangers.forEach((item) => {
                  item.removeAttribute('disabled'):
            });
    }


    Если же вас беспокоят именно элементы, остающиеся на экране при старте перезагрузки, то самым простым решением было бы вообще удалить весь html страницы до перезагрузки, а потом спокойно её запустить. Либо вы можете точно так же в момент, когда вам надо запустить перезагрузку, установить всем опасным кнопкам атрибут disabled, а потом стартовать перезагрузку страницы.

    Но!!! Данный подход только лишь сделает жизнь пользователей более приятной. Не забывайте, что фронтенд-фрогнтендом, но бэкенд должен всё же уметь работать с такими запросами, которые не будут содержать в себе все нужные данные.
    Первое правило бэкенда - "Никогда не верь фронтенду и его данным, проверяй и валидируй всё"
    Ответ написан
    Комментировать
  • Как организовать структуру проекта с несколькими модулями Golang?

    Делаем три разных файла main.go под каждый сервис. Кладём их каждый в свою подпапку в директории, например "cmd".
    А для общих библиотек используем отдельную директорию, например "pkg", и будем импортировать отсюда функционал во все три сервиса.

    Т.е. у нас получается структура файлов:
    cmd/
          repeater/
                main.go
          controller/
                main.go
          executor/
                main.go
    pkg/
          c-library/
                clibrary.go
    go.mod


    И потом запускаем компиляцию:

    go build ./cmd/repeater
    go build ./cmd/controller
    go build ./cmd/executor


    Пример main.go
    package main
    
    import c_library "test/pkg/c-library"
    
    func main() {
    	c_library.HelloWorld()
    }


    И общая библиотека:
    package c_library
    
    import "fmt"
    
    func HelloWorld() {
    	fmt.Println("HELLO WORLD")
    }


    Весь проект у меня лежит в папочке test, и в файле go.mod надо бы указать следующее

    module test
    Ответ написан
    5 комментариев
  • Какую CMS выбрать для первого учебного проекта?

    Всегда всем советую Wordpress, потому что это уже не просто CMS, это целый мир.
    Однако, если по какой-то причине столкнётесь с непреодолимыми сложностями, то могу посоветовать Drupal. Это более сложная, но и более подходящая для будущих программистов CMS, потому что она написана на фреймворке Symfony, являющихся одним из столпов программирования на PHP.
    Т.е.:
    - Если надо обучить людей быстро создавать сайты и начать зарабатывать денюжку, то Wordpress без вариантов.
    - Если надо в итоге сделать из людей программистов, то можно рассмотреть Drupal
    - Битрикс забываем, как страшный сон.
    Ответ написан
    1 комментарий
  • Как правильно разрабатывать symfony приложение локально?

    Я использую для локальной разработки docker compose.
    В его конфигурационном файле собираю все нужные контейнеры (PHP, MySQL, Rabbit, Mongo и т.д.) в сеть. Для PHP делаю два контейнера из разных образов:
    Один из образа php-fpm, это сам сервер.
    Второй из образа php-cli, он используется как раз для всех консольных команд. Именню в него устанавливаю Composer.
    Для того, чтобы не писать для запуска консольных команд постоянно это заклинание "docker compose run ...", я делаю Makefile, в котором прописываю часто-используемые команды.
    И, набрав "make cc" на самом деле запускаю "docker-compose run --rm php-cli bin/console c:c"
    Чтобы это всё работало без необходимости постоянно перезапускать docker-compose, я файлы проекта пробрасываю внутрь контейнеров не копированием, а через вольюм, и когда я буду их менять, они будут меняться и внутри контейнера. Так же подключаю прямо внутрь контейнера .env файл, чтобы все переменные окружения туда пробросились.
    Я вам прямо закину кучу файлов из одного из своих старых проектов, может накопаете там идей каких-то для себя:
    1. docker-compose.yml
    version: '3.8'
    services:
      # Поднимаю сразу реверс-прокси, чтобы пробросить всё наружу на нужный мне порт.m
      nginx:
        build:
          context: ./.docker/nginx
          dockerfile: Dockerfile.dev
        volumes:
          - ./:/app
        depends_on:
          - php-fpm
        ports:
          - "8080:80"
    
      php-fpm:
        build:
          context: ./.docker/php
          dockerfile: Dockerfile.fpm-dev
          args:
            IMAGE_ENV: dev
        volumes:
          # Вот тут вся магия) Мы просто подключаем файлы проекта в виде вольюма
          # Т.е. имеем доступ ко всем файлам и изнутри и снаружи контейнера. 
          # Внутри контейнера файлы будут видны по пути /app
          - ./:/app
        environment:
          # Даже XDEBUG можно пробросить для IDE
          XDEBUG_CONFIG: 'discover_client_host=true client_host=127.0.0.1 log_level=0 client_port=9003'
          PHP_IDE_CONFIG: "serverName=Docker"
        env_file:
          - .env.local
        depends_on:
          - mysql
    
      # Делаю отдельный контейнер для комманд-лайн утилит, 
      # чтобы не проще запускать консоль Symfony. Мне так удобнее.
      # Здесь будет всё практически как и у php-fpm, только без самого php-fpm процесса
      php-cli:
        build:
          context: ./.docker/php
          dockerfile: Dockerfile.cli-dev
          args:
            IMAGE_ENV: dev
        volumes:
          - ./:/app
          - composer:/root/.composer/cache
        environment:
          XDEBUG_CONFIG: 'discover_client_host=true client_host=127.0.0.1 log_level=0 client_port=9003'
          PHP_IDE_CONFIG: "serverName=Docker"
        env_file:
          - .env.local
        depends_on:
          - mysql
    
      mysql:
        build:
          context: ./.docker/db
          dockerfile: Dockerfile
        volumes:
          - mysql:/var/lib/mysql
        env_file:
          - .env.local
        environment:
          MYSQL_ROOT_HOST: '%'  # needs to be enclosed with quotes
        ports:
          - "6603:3306"
    
    # Тут ещё два дополнительных вольюма. Один для кэша composer, а второй для файлов базы данных, чтобы она не убивалась при перезапуске наших контейнеров. Убивать мы её будем только сознательно. если нам надо её очистить.
    volumes:
      composer:
      mysql:


    2. Все три докерфайла
    Файлы с конфигурациями, которые будут копироваться внутри докерфайлов, кидать не буду, у вас всё своё будет в любом случае.

    Dockerfile.dev - для nginx.
    # Образ Nginx
    FROM nginx:alpine
    
    # Копируем файл с настройками
    COPY ./task/default.conf /etc/nginx/conf.d/default.conf
    
    # Устанавливаем рабочую директорию
    WORKDIR /app


    Dockerfile.fpm-dev - для сервера php-fpm
    # Образ php-fpm
    FROM php:8.2-fpm
    
    ARG IMAGE_ENV
    
    # Устанавливаем нужные пакеты и расширения
    RUN apt-get update && apt-get install -y -qq libpq-dev unzip libxml2-dev libicu-dev libonig-dev zlib1g-dev libpng-dev librabbitmq-dev \
        && docker-php-ext-configure pdo_mysql --with-pdo-mysql=mysqlnd && docker-php-ext-install pdo_mysql && \
        docker-php-ext-configure intl && docker-php-ext-install intl && \
        pecl install xdebug amqp && rm -rf /tmp/pear && docker-php-ext-enable xdebug amqp && \
        rm -rf /var/lib/apt/lists/*
    
    # Use the default development configuration
    RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
    
    # Копируем файл с настройками xdebug
    COPY ./php.d/${IMAGE_ENV}/default.ini /usr/local/etc/php/conf.d/default.ini
    COPY ./php.d/${IMAGE_ENV}/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini
    
    
    # Создаем пользователя app, чтобы не работать под рутом
    RUN adduser --disabled-password --gecos '' --uid 1000 app
    USER app
    
    # Устанавливаем рабочую директорию
    WORKDIR /app


    Dockerfile.cli-dev - это наш консольный php для запуска консольных команд. Тут практически то же самое, плюс тот самый Composer, ради которого это всё и писалось)
    # Образ php-cli
    FROM php:8.2-cli
    
    ARG IMAGE_ENV
    
    # Устанавливаем нужные пакеты и расширения
    RUN apt-get update && apt-get install -y -qq git unzip libpq-dev libxml2-dev libicu-dev librabbitmq-dev && \
        docker-php-ext-configure pdo_mysql --with-pdo-mysql=mysqlnd && docker-php-ext-install pdo_mysql && \
        docker-php-ext-configure intl && docker-php-ext-install intl && \
        pecl install xdebug amqp && rm -rf /tmp/pear && docker-php-ext-enable xdebug amqp && \
        rm -rf /var/lib/apt/lists/*
    
    # Use the default development configuration
    RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
    
    # Копируем файл с настройками xdebug
    COPY ./php.d/${IMAGE_ENV}/default.ini /usr/local/etc/php/conf.d/default.ini
    COPY ./php.d/${IMAGE_ENV}/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini
    
    # Качаем и устанавливаем composer
    RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/bin --filename=composer --quiet
    
    # Если всё же будете работать в контейнере под рутом, то заставляем composer работать под рутом
    # ENV COMPOSER_ALLOW_SUPERUSER 1
    
    # Создаем пользователя app, чтобы не работать под рутом
    RUN adduser --disabled-password --gecos '' --uid 1000 app
    USER app
    
    # Устанавливаем рабочую директорию
    WORKDIR /app


    3. И далее звезда шоу - Makefile с кучей разных скриптов, облегчающих жизнь.
    make init - чистая сборка проекта, тут можно очищать базу и загружать в нее фикстуры
    make up - просто запуск проекта, без очистки, перед началом обычного рабочего дня.
    make down - остановка проекта
    make cc - очистка кэша Symfony.
    И т.д. и т.п.
    # Если надо подключить shell переменные, то создаём тут же ..env.local файл и делаем.
    include .env.local
    export
    
    # Поддержка возможности запускать шелл-скрипты
    SHELL:=/bin/bash
    
    # Кэш
    c: cache-clear cache-warmup
    cc: cache-clear
    cw: cache-warmup
    cache-clear:
    	docker-compose run --rm php-cli bin/console c:c
    cache-warmup:
    	docker-compose run --rm php-cli bin/console c:w
    
    up: docker-up
    down: docker-down
    restart: docker-down docker-up
    init: docker-down init-common
    reset: docker-down-clear init-common
    init-common: clear docker-pull docker-build docker-up app-init
    test: test
    test-coverage: test-coverage
    test-unit: test-unit
    test-unit-coverage: test-unit-coverage
    
    # Если в контейнере другой пользователь, то будут ошибки доступа при редактировании файлов.
    # Поэтому надо сделать chown и chmod для папки var (кэш и логи)
    chown:
    	sudo chown -R 1000:1000 . # Тут указываем свои данные пользователя с хоста
    	sudo chmod -R 777 ./var/
    
    docker-up:
    	docker-compose up -d
    
    docker-down:
    	docker-compose down --remove-orphans
    
    docker-down-clear:
    	docker-compose down -v --remove-orphans
    
    docker-pull:
    	docker-compose pull
    
    docker-build:
    	docker-compose build
    
    app-init: composer-install app-wait-db app-migrations ready
    
    clear:
    	docker run --rm -v ${PWD}:/app --workdir=/app alpine rm -f .ready
    
    ready:
    	docker run --rm -v ${PWD}:/app --workdir=/app alpine touch .ready
    
    test:
    	docker-compose run --rm php-cli php bin/phpunit
    
    test-coverage:
    	docker-compose run --rm php-cli php bin/phpunit --coverage-clover var/clover.xml --coverage-html var/coverage
    
    test-unit:
    	docker-compose run --rm php-cli php bin/phpunit --testsuite=unit
    
    test-unit-coverage:
    	docker-compose run --rm php-cli php bin/phpunit --testsuite=unit --coverage-clover var/clover.xml --coverage-html var/coverage
    
    composer-install:
    	docker-compose run --rm php-cli composer -V
    	docker-compose run --rm php-cli composer install
    
    app-create-db:
    	docker-compose run --rm php-cli ./bin/console doctrine:database:create
    
    app-wait-db:
    	#docker-compose exec lkui-mysql mysqladmin --user=root --password=password ping --silent --wait=60 &> /dev/null
    	#sleep 20
    	until docker-compose exec -T mysql mysqladmin ping -u root -P ${MYSQL_TCP_PORT} -p${MYSQL_ROOT_PASSWORD} | grep "mysqld is alive" ; do >&2 echo "MySQL is unavailable - waiting for it... " ; sleep 5 ; done
    
    app-migrations:
    	docker-compose run --rm php-cli ./bin/console doctrine:migrations:migrate --no-interaction
    
    app-fixtures:
    	docker-compose run --rm php-cli ./bin/console doctrine:fixtures:load --no-interaction
    
    migration:
    	docker-compose run --rm php-cli ./bin/console make:migration
    
    migrate:
    	docker-compose run --rm php-cli ./bin/console doctrine:migrations:migrate #--no-interaction
    
    fixtures:
    	docker-compose run --rm php-cli ./bin/console doctrine:fixtures:load
    Ответ написан
    2 комментария
  • Как лучше описывать документацию API в PHP 8.1 через аннотации или атрибуты?

    Используйте атрибуты.
    Да, на данный конкретный момент документации по аннотациям больше, но, во-первых, как верно заметил уважаемый BoShurik , атрибуты почти всегда полностью соответствуют аннотациям в плане параметров, а, во-вторых, сообщество PHP целенаправленно идёт в сторону использования атрибутов, и однажды, когда-нибудь, при апгрейде вам прямо придётся переписать аннотации на атрибуты, потому что какая-то новая версия откажется от аннотаций.
    Ответ написан
    1 комментарий
  • Как правильно писать тесты PHPUnit под ООП?

    Вам не надо тестировать приватные методы.
    Но как же тогда быть, вы скажете?
    Публичные методы используют эти приватные методы, так и проходит тестирование.
    Из этого вытекает, что класс должен быть небольшим и выполнять только одну задачу. Поэтому его приватные методы будут всегда задействованы при тестировании публичных.
    Но вы скажете, что у вас там в публичных методах обращения к базе данных и внешним API, как же тут быть?
    А быть тут просто: все зависимости вашего класса должны включаться в качестве аргументов конструктора, и через использование интерфейсов в аргументах. (Dependency Injection)
    Таким образом в тесте все зависимости класса заменяются теми же моками, и вы легко тестируете не просто сферического корня в вакууме (приватный метод), а именно то, что и нужно тестировать: функциональность самого класса, а не его частей, потому что в OOP именно класс является как бы атомом функционала.
    Но... если вам всё же таки очень надо, то можно это сделать при помощи рефлексии:
    Хорошей практикой является использовать всегда protected вместо private.
    Пишете себе такую вот утилиту:
    class PHPUnitUtil
    {
      public static function callMethod($obj, $name, array $args) {
            $class = new \ReflectionClass($obj);
            $method = $class->getMethod($name);
            // $method->setAccessible(true); // если PHP старше 8.1.0
            return $method->invokeArgs($obj, $args);
        }
    }


    И используете ее в тестах
    $returnVal = PHPUnitUtil::callMethod(
                    $this->object,
                    '_nameOfProtectedMethod', 
                    array($arg1, $arg2)
                 );


    Есть ещё вариант.
    // Оригинальный класс
    class Foo {
      protected function stuff() {
        // secret stuff, you want to test
      }
    }
    
    // Класс для тестов
    class SubFoo extends Foo {
      public function exposedStuff() {
        return $this->stuff();
      }
    }
    Ответ написан
    3 комментария
  • Можно ли конвертировать/сохранить динамически сгенерированную js страницу в статичный html-файл?

    Есть банальные расширения для браузера
    https://chromewebstore.google.com/detail/save-page...
    https://chromewebstore.google.com/detail/webscrapb...

    А так же целое десктопное приложение и ещё одно расширение для браузера
    https://github.com/webrecorder/webrecorder-desktop
    https://chromewebstore.google.com/detail/webrecord...
    Ответ написан
    Комментировать
  • Чем занимаются Middle Frontend разработчики?

    Праграмиравает, вот чем он занимается)
    А если серьёзно, то такая градация работников является чистой усвовностью, которая позволяет крупным конторам контролировать ЗП своих работников в зависимости от их скилла. Эта градация хорошо сортируется в их эксель-табличках с перформанс-метриками.
    В разных компаниях миддл может иметь совершенно разные навыки. Миддл в SpaceX и миддл на сайте для продажи женского белья - это два человека с космической бездной между ними в плане профессиональных навыков.

    Т.е. если вы в объявлении пишете, что ищете миддла, то вы сразу обговариваете категорию его зарптаты в вашей компании. И когда вы его наймёте, он не будет сильно сокрушаться, когда узнает, что сеньоры получают больше него.
    Ответ написан
    Комментировать
  • Как создавать, принимать и обрабатывать socket?

    1. Вебсокеты - это сложновато.
    2. Вебсокеты на PHP - вдвойне сложно. Проблема в том, что PHP задуман как скриптовый язык, т.е. скрипт выполняется заканчивает свою работу. А вебсокет - это постоянное соединение, т.е нам надо, чтобы программа постоянно крутилась в фоне. Вебсокеты можно реализовать на PHP, но, как персонально мне кажется, проще будет выучить Go )) , или же, как в ответе уважаемого Артём , сделать сервер на ноде.
    3. Если ваш чат не такой супер-функциональный, как чат в мессенджерах, то вместо вебсокетов можно обойтись SSE (Server Sent Events). SSE так же требует постоянного соединения, но всё работает через HTTP, и это ну прямо намного проще. Единственный недостаток - это то, что SSE работает только в одну сторону: от сервера в браузер. Т.е. запросы из браузера можно получать обычным POST запросом, а отдавать обратно информацию уже через SSE.

    С SSE есть два пути:
    1. Написать сервер самому, используя какую-то простую библиотеку вроде этой https://github.com/hhxsv5/php-sse
    2. Но я бы сделал ещё проще. Есть такой великолепный проект под названием Mercure https://mercure.rocks
    Это отдельный сервис на Go, задача которого как раз поддерживать SSE соединение и отправлять сообщения в браузеры. Сервис сидит в фоне, а браузеры подписываются на события через EventSource буквально в три строчки, как описано тут https://mercure.rocks/docs/getting-started
    Прелесть этого в том, что для того, чтобы отправить сообщение всем браузерам из кода на PHP, вам надо просто сделать обычный POST запрос на специальный адрес этого сервиса Mercure с телом самого сообщения и его id. Т.е. вам не надо делать никаких долгоживущих процессов на PHP, всё будет работать как раньше.

    Т.е. подытожим:
    - Браузеры пользователей подписываются на события в Mercure
    - Пользователь 1 отправляет текстовое сообщение обычным POST запросом на обычный PHP сайт.
    - PHP сайт получает этот POST запрос, определяет, что его надо отправить Пользователю 2, и отправляет соответствующее сообщение обычным POST запросом в сервис Mercure
    - Mercure отправляет сообщение Пользователю 2 через SSE, на которые он подписан.
    - Сообщение появляется у него на страничке
    Ответ написан
    5 комментариев
  • Где искать верстальщиков и бекендеров?

    На этот вопрос нет чёткого ответа.
    1. Связи - вот главный рецепт успеха в IT. Это лучший способ найти исполнителя.
    2. Если связей нет, то гарантированный, но дорогой и неприятный способ - это заключение контракта с какой-то галерой. Тут главное прописать все обязательства и ответственность сторон. Требовать исполнения, но и самому исполнять.
    3. Остальные способы как раз и сопряжены с теми рисками, что вы описали. Вы лишь должны принять эти риски, заложить их в сроки проекта, и не ждать чуда из места, где его не может быть. Что могу посоветовать - если нашли толкового исполнителя, то берегите его, заплатите вовремя и столько, сколько договаривались, не требуйте крупных бесплатных доработок. Такой исполнитель - большая редкость, и это добавка в список ваших связей, и в следующий раз вы начнёте уже с п.1...
    Ответ написан
    Комментировать
  • Как получить переменную из callback php?

    Можно использовать замыкание. Тут фишка в том, что надо использовать в замыкании не обычную строковую переменную, а объект, потому что строковая переменная будет передана в виде копии, и не получится её изменить.
    В коде я заменил класс Client на его мок.

    class Client {
        public function miniTicker(array $callbacks) {
            foreach ($callbacks as $key => $callback) {
                $conn = 'aaaaa';
                $msg = 'msg-'.$key;
                $callback($conn, $msg);
            }
        }
    }
    
    class Message {
        public array $messages = [];
    
        public function addMessage(string $msg) {
            $this->messages[] = $msg;
        }
    }
    
    $message = new Message();
    
    $callbacks = [
        'message' => function ($conn, $msg) use ($message) {
            $message->addMessage($msg);
            
        },
        'ping' => function ($conn, $msg) use ($message) {
            $message->addMessage($msg);
        }
    ];
    
    $client = new Client();
    $client->miniTicker($callbacks);
    var_dump($message->messages);


    В результате в конце у нас выводятся все сообщения
    https://onlinephp.io/?s=tVK7boQwEOyR-IctkDDSkR8gR4...
    Ответ написан
    5 комментариев
  • Как предотвратить бесконечную загрузку страницы при отправке post запроса?

    Использовать SSE (Server Sent Events). Это на порядок проще, чем вебсокеты, но мощь практически такая же.
    Вот неплохая статья на английском.
    https://blog.stackademic.com/real-time-communicati...

    Кстати, библиотека HTMX позволяет это обрабатывать и без написания Javascript
    https://htmx.org/extensions/server-sent-events/
    Ответ написан
    Комментировать
  • Есть ли сейчас необходимость в HTML5Shiv и Modernizr?

    Вы должны отталкиваться от того, кем являются ваши пользователи.

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

    Если же это обычные люди, сидящие в интернете, то, скорей всего они используют Chrome или Microsoft Edge. Эти браузеры постоянно обновляются в фоне, и перед внедрением новых фич вам просто нужно периодически заходить на https://caniuse.com/ и ориентироваться на последние 2 версии этих браузеров. Safari может стать проблемой, если ваши пользователи часто покупают Маки. Safari - это, к большому сожалению, новый IE6, который тормозит индустрию...

    Самый лучший способ - это собрать информацию. Если у вас уже есть какой-то сайт для вашей аудитории, то там и собирайте статистику. Исходя из этой статистики принимайте решение. Охватывать все 100% не стоит, лишняя трата времени, денег, нервов. Оно не окупается. Каким процентом пользователей вы можете пренебречь - это всегда индивидуальное решение, но убедить руководство, что такое решение надо принять, всегда надо, потому что вы потом закопаете проект в этой обратной совместимости и потенциальных багах и уязвимостях, идущих с ней в пакете...
    Ответ написан
    6 комментариев
  • Есть сервера, которые стоит написать именно на Java, а не на Node?

    Выскажу свою личную точку зрения:
    1. Изучаем, каким будет трафик сервиса на старте. Если это замена уже работающему высоконагруженному сервису, либо вы на 200% уверены, что нагрузка прямо сразу будет высокой и дальше на 200% точно уверены, что нагрузка будет расти, не выбирайте ни ноду ни Java. Берите, например, Go. Он гораздо быстрее ноды, там даже стандартная библиотека уже имеет готовые решения для всего нужного REST, и есть куча прекрасных сторонних библиотек и фреймворков.
    2. А вот если ваш проект новый, вы не знаете, сколько у вас будет пользователей, какая будет нагрузка, то не надо даже думать: берите то решение, которым вы владеете, и с помощью которого вы сможете быстро и дёшево собрать первую версию. Если это даже PHP, то пусть он и будет. С огромной вероятностью ваш сервис будет всё равно переделан и переписан позже, поэтому, чем меньше вы потратите времени и денег в самом начале, тем быстрее выйдете на рынок, и тем больше сэкономите первоначальных инвестиций. А когда проект начнёт приносить прибыль, вот тогда при помощи уже этой прибыли будете решать проблемы производительности
    Ответ написан
    Комментировать