Задать вопрос
  • Почему PHP выполняет математические операции неправильно?

    Vamp
    @Vamp
    Потому что типом данных double нет возможности представить число 0.58. Поэтому компьютер берёт наиболее близкое к 0.58 число, которое double может выразить.

    Если вам нужны точные вычисления, то следует воспользоваться специальными функциями:

    $format = bcdiv("580000000", "1000000000", 2); // "0.58"
    
    var_dump(bcmul($format, "100")); // string(2) "58"


    Существует даже специальный сайт, рассказывающий о данной особенности математики чисел с плавающей запятой: https://0.30000000000000004.com/
    Ответ написан
    Комментировать
  • Как ограничить подключение к MongoDB?

    Vamp
    @Vamp
    На уровне ОС это можно сделать через фаервол.
    На уровне базы через настройку authenticationRestrictions в аккаунте пользователя.
    Ответ написан
  • Как пробросить сервер за NAT провайдера?

    Vamp
    @Vamp
    У cloudflare есть решение для этой задачи - cloudflare tunnel.

    Cloudflare Tunnel provides you with a secure way to connect your resources to Cloudflare without a publicly routable IP address. With Tunnel, you do not send traffic to an external IP — instead, a lightweight daemon in your infrastructure (cloudflared) creates outbound-only connections to Cloudflare's global network.
    Ответ написан
    1 комментарий
  • Как исправить проблему с mail php и заголовками для gmail?

    Vamp
    @Vamp
    Заголовки To и Subject подставляются автоматически функцией mail. Вам не нужно добавлять их самостоятельно. Просто удалите строки:

    $headers .= "To: $to\r\n";
    $headers .= "Subject: $subject\r\n";
    Ответ написан
    1 комментарий
  • Как деплоить php приложение вместе с docker?

    Vamp
    @Vamp
    Это типичный вариант развёртывания приложения в докере. Единственный его минус - некоторое время даунтайма между моментом когда опустился старый контейнер и поднялся новый. Для решения этой проблемы есть разные техники. Например, blue-green deployment. Это когда есть два контейнера, условно называемые green и blue. И трафик льётся только на один. Скажем, на green. В момент деплоя обновляется blue контейнер и после того как он полностью будет готов - переключаем трафик на blue. В следующий раз деплой начнётся с обновления green.

    Переключение трафика делается разными способами. Самый безопасный - переписать конфиг nginx и релоаднуть его. Или менее безопасный, зато без необходимости править конфиг, - добавить оба конейнера в одну upstream группу и опустить green контейнер после обновления blue.

    Довольно муторное дело. Но в оркестраторах типа openshift и kubernetes такой деплоймент процесс уже встроен из коробки. Но вот сами оркестраторы довольно тяжелая штука. Для них нужен отдельный выделенный человек, который будет заниматься только ими. Так что я советую начинать присматриваться к оркестраторам только когда количество серваков перевалит за 50. С меньшим количеством это просто нерентабельно.

    Другой вариант развёртывания - контейнер с пхп поднят постоянно, но код проекта монтируется в контейнер через volume. Далее ci джоба по sftp заливает новый код в соседнюю папку и переключает симлинк. Симлинк переключается атомарно и контейнер сразу начинает работать с новым кодом. Ничего больше делать не надо. Я рекомендую такой подход когда на проекте менее 50 серверов.
    Ответ написан
    4 комментария
  • Как работать с DI-контейнером?

    Vamp
    @Vamp
    В самом контейнере задаются только примитивные типы данных?

    В DI контейнере обычно регистрируют объекты, общие для всего приложения, а не для конкретного запроса. Например, коннект к базе, роутер, кеш. Автовайринг примитивных типов в DI - прямая дорога к путанице и сложным для обнаружения ошибкам.

    А все объектные зависимости решаются с помощью автовайринга через Reflection API?

    Можно и так. А можно руками связи выставить. Или аннотациями. Разные способы бывают, смотря какие поддерживаются выбранной вами реализацией DI.

    К примеру, у меня есть контроллер.

    В случае с контроллерами обычно делают front controller, на который сваливаются абсолютно все запросы, а дальше он сам определяет какой из контроллеров нужно создать и передаёт запрос на обработку ему.

    Сам вид принимает шаблоны, это обычные строки с разванием шаблонов, которые нужно подключить.

    Если строки с названием шаблонов фиксированы, хранятся в конфиге/базе и одинаковы для всех страниц и всех пользователей, то можно шаблон и в DI зарегистрировать. В противном случае лучше создавать его во фронт контроллере. Либо зарегистрировать в DI фабрику шаблонов, которая будет создавать объекты вью в зависимости от переданных параметров. Аналогично и с моделью.

    Модель принимает класс соединения с БД, а БД принимает массив с настройками подключения.

    В контейнере можно зарегистрировать объект Settings, в котором хранятся данные для подключения к базе и прочие глобальные настройки, а затем инжектить его в класс для работы с базой. Ещё контейнер может сам выступать хранилищем настроек и инжектить их (в том числе примитивные). Так сделано в DI компоненте Symfony.

    Есть еще класс пагинации, он тоже должен быть в контроллере и его конструктор принимает три параметра типа int.

    Опять же, если эти три параметра не зависят от запроса, то можно зарегистрировать пагинатор в DI. Если зависят, то можно из конструктора перенести эти параметры в метод и вызывать его в контроллере. Например: $page = $this->paginator->calulatePage($one, $two, $three)
    Ответ написан
    2 комментария
  • Как отправить длинное SMS с помощью SMPP?

    Vamp
    @Vamp
    Отправляйте текст через message_payload, а не short_message. В этом случае делением на сегменты будет заниматься провайдер.

    Пример Данилы лучше не использовать. Нельзя втупую резать текст по 153 символа. А ещё там косяк с кодировкой.

    Я не вижу никаких ошибок, потому что либа даже не отправляет мое SMS.

    Дебажить SMPP - это боль и страдания (не зависит от используемого языка). Лучше всего снять дамп трафика и смотреть на сырые пдухи в wireshark. В идеале отправлять смски через другой протокол. Многие провайдеры предоставляют HTTP API.
    Ответ написан
    Комментировать
  • Какой софт поставить на домашний файловый сервер из древнего железа?

    Vamp
    @Vamp
    Я на свой древневековый ноутбук с 32-битным процессором (intel atom) поставил openmediavault. На слабом железе хорошо работает.

    Не верю в стабильность внешних ресурсов.

    А в стабильность хлама значит верите? По опыту эксплуатации хлама скажу, что лучше его выкинуть/продать и взять что-нибудь свежее взамен. Посмотрите мини-пк или одноплатники. Будет надёжнее, меньше места занимать, меньше энергии потреблять. Я в итоге заменил ноутбук на raspberry pi с m.2 модулем, куда вставил терабайтный SSD.
    Ответ написан
  • Какой оптимальный способ попасть в домашнюю сеть снаружи?

    Vamp
    @Vamp
    Самый удобный вариант - развернуть частный VPN типа zerotier или tailscale.
    Ответ написан
  • Как запустить парсер NBBC под PHP8?

    Vamp
    @Vamp
    Нужно ещё конструкторы в классах переименовать в __construct() и тогда останется только два теста, которые заваливаются из-за того, что htmlspecialchars по умолчанию применяет ENT_QUOTES.
    Ответ написан
    Комментировать
  • Низкая скорость отдачи с VPS на конкретный хост?

    Vamp
    @Vamp
    Разница почти наверняка из-за неудачного маршрута PC2->PC1. Маршрут можно посмотреть командой traceroute или mtr (в ubuntu пакет называется mtr-tiny). Далее можно попробовать написать письмо в техподдержку хостера PC2, приложив трейсы (с обоих сторон) и результаты iperf3 (так же с обоих сторон). Тогда хостер может подправить маршрутизацию и скорость восстановится.
    Ответ написан
    5 комментариев
  • Как правильно должно быть развернута инфрастуктура с Docker, Kubernetes?

    Vamp
    @Vamp
    1. Потому что докер объединил уже имеющиеся в линуксе способы изоляции процессов (chroot, cgroups, namespaces) в удобное для использования приложение.

    2. Гипервизором выступает ядро линукса. Поэтому накладные расходы на контейнеризацию значительно ниже, чем на виртуализациию.
    Ответ написан
  • Как избавиться от требования обработки исключения в Project Reactor?

    Vamp
    @Vamp
    неужели есть только один вариант это обрабатывать с поощью try/catch внутри каждого блока что бы избежать Unhandled exceptions?

    Вобщем-то да. Можно вынести try/catch в отдельный универсальный метод, который оборачивает исключение в RuntimeException:

    private @NotNull Mono<JsonObject> invoke(Channel channel, Class<?> clazz, Method method, Object... args) {
        return Mono.fromCallable(() -> clazz.getDeclaredConstructor(Server.class, Channel.class))
            .map(constructor -> ex(() -> constructor.newInstance(this.manager.getServer(), channel)))
            .flatMap(obj -> new GenericData<JsonObject>().invoke(method, obj, args))
            .onErrorResume(Mono::error);
    }
    
    private <T> T ex(Callable<T> code) {
        try {
            return code.call();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    Конкретно в вашем примере можно вызов конструктора переместить в callable, из которого делается Mono, так как из Callable разрешено стрелять проверяемыми исключениями, в отличии от Function, который принимает map:

    private @NotNull Mono<JsonObject> invoke(Channel channel, Class<?> clazz, Method method, Object... args) {
        return Mono.fromCallable(() -> {
                var constructor = clazz.getDeclaredConstructor(Server.class, Channel.class);
                return constructor.newInstance(this.manager.getServer(), channel);
            })
            .flatMap(obj -> new GenericData<JsonObject>().invoke(method, obj, args))
            .onErrorResume(Mono::error);
    }
    Ответ написан
    Комментировать
  • Как правильно добавить точку монтирования в docker compose для elasticsearch?

    Vamp
    @Vamp
    Нужно монтировать /usr/share/elasticsearch/data

    volumes:
          - /home/user/green:/usr/share/elasticsearch/data
    Ответ написан
    Комментировать
  • Как на самом деле работает параллелизм?

    Vamp
    @Vamp
    Вопрос 1. Два конвейера позволяют только сократить накладные расходы на context switch. Параллелизм это не увеличивает, так как вычислительное ядро по-прежнему одно.

    Вопрос 2. Верно. Разделите понятия процесс и поток. Думайте о процессе как о контейнере для потоков, а не как об активной сущности, которая исполняет код. В каждом процессе есть как минимум 1 поток. Все без исключения программы стартуют как однопоточный процесс, который имеет возможность превратиться в многопоточный, заспавнив ещё потоков. Ну и, соответственно, ОС шедулит потоки, а не процессы.

    Вопрос 3. Нет. Процессор сам подгружает инструкции из кеша/основной памяти. Потоки не совершают никаких дополнительных действий для этого. Всё происходит прозрачно для потока.

    Вопрос 4. Kernelspace и userspace потоки отличаются только тем, в каких кольцах они исполняются. Kernel потоки исполняются в привилегированных кольцах, а user потоки в непривилегированных. Во всём остальном эти потоки ничем друг от друга не отличаются. Кольца ограничивают набор процессорных инструкций и системных вызовов, которые поток может использовать. Например, чтение файла с диска - это привилегированная операция (так как требует прямого доступа к девайсу), поэтому когда обычный userspace поток хочет прочитать файл, ОС приостанавливает userspace поток и передаёт управление kernelspace потоку, у которого достаточно привилегий на выполнение данной операции.

    Кооперативную многозадачность можно сделать и самому. Концепция называется green threads. В языках программирования встречается уже готовая реализация грин тредов, только называется иначе: корутины (python), горутины (go), виртуальные потоки (java). Все они делают одно и то же - реализуют кооперативную многозадачность в userspace. При большом желании можно написать и свою реализацию грин тредов без использования уже готовых языковых инструментов. Хотя практического смысла в этом мало (кроме нарабатывания опыта).

    Вопрос 5. Думаю, ответ на этот вопрос очевиден из ответа на предыдущий.

    И всё же под "kernel" обычно подразумевают ядро ОС, а не ядро процессора (которое называют core, а не kernel). Единственный способ запустить поток в kernelspace - написать модуль/драйвер ядра. Обычные программы так не могут.

    Финальный вопрос. Правильно понимаете. Вот только "пошаговая стратегия" - это про многозадачность, а не про параллелизм. Процессор с одним ядром многозадачным быть может. Параллельным - нет.
    Ответ написан
    7 комментариев
  • Стоит ли фильтровать пароль на символы?

    Vamp
    @Vamp
    У вас классическая проблема обработки пользовательского ввода. Решается она по-разному в зависимости от контекста, в котором используются данные. Так как вы не указываете контекст, то и ответ будет соответствующе широким.

    1. Запрос первоначально поступает в node.js (конкретно в движок v8, отвечающий за сетевое взаимодействие). Здесь возможны уязвимости, при которых специальным образом сформированный запрос может привести к отказу в обслуживании (DoS), несанкционированному доступу к памяти (вспоминается эпичнейшая уязвимость heartbleed) и иным спецэффектам.

    2. Далее запрос поступает в express, где он парсится и преобразовывается в объект req. Здесь так же могут быть уязвимости, эксплуатируемые путем отправки специально сформированного запроса.

    Пункты 1 и 2 исполняются до вашего кода, поэтому всё что вы можете здесь поделать - это вовремя обновлять софт и библиотеки.

    3. Далее ваш код занимается обработкой данных. Важный момент в этом - санация и валидация данных.

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

    Валидация - это проверка данных на корректность. Например, вы ожидаете номер телефона в поле phone. После санации останется проверить, что длина номера не нулевая и не больше 15.

    Если данные предполагается передавать дальше, скажем, на удалённый JSON REST сервис, то вы должны будете корректно экранировать строку перед добавлением её в JSON, иначе случайная кавычка сломает ваш запрос.

    Либо если данные будут вставляться в базу данных, то необходимо экранировать данные (либо вставлять данные через подготовленные запросы) чтобы не получить SQL injection уязвимость.

    Вот тут как раз и появляется зависимость от контекста. В контексте базы данных - один способ экранирования. В контексте json - другой. В контексте HTML (при выводе данных пользователю) - третий. В ещё каком-нибудь другом контексте будут свои способы обезопасить данные.

    Если рассматривать конкретно пароль, то с ним всё просто. Как правило, пароли не хранятся на сервере в открытом виде и предварительно подвергаются преобразованию в криптографически стойкий хеш (например, argon2 или scrypt). Сам хеш уже имеет фиксированный размер и известный набор символов. Поэтому санировать пароль не нужно, а при валидации просто проверить минимально допустимую длину, несовпадение с логином, email и может ещё какие-нибудь проверки, навязанные бизнесом или применяемыми стандартами.
    Ответ написан
    6 комментариев
  • Как в контейнере сделаться рутом?

    Vamp
    @Vamp
    Ваш образ создан from scratch. Это значит, что в нём только приложение. И больше ничего лишнего нет. В том числе и бинарника /bin/bash, путь к которому вы указываете в /etc/passwd

    Возьмите образ на основе альпины: anycable/anycable-go:1.2-alpine
    Там есть /bin/sh, пакетный менеджер и ещё всяких разных утилит по мелочи.
    Ответ написан
    1 комментарий
  • Как работать с сырыми результатами SELECT MySQL CLI в PHP CLI?

    Vamp
    @Vamp
    Вам придется самостоятельно парсить $res в более удобный массив или объект. Можно слегка упростить вывод mysql, добавив аргумент --batch.
    Ответ написан
    1 комментарий
  • Как авторизовавшись на одном сайте в "сети сайтов", быть автоматически авторизованным и на других?

    Vamp
    @Vamp
    Видел однажды такую реализацию.

    При успешном прохождении авторизации кидает на страницу, где через тег img подключается "картинка" с каждого домена, участвующего в sso тусовке.

    <h2>Вы успешно вошли</h2>
    <img src="https://example.com/auth.php?key=1hB7fa014bbXCDg920">
    <img src="https://example.net/auth.php?key=hVVFD41looas8730MM">
    <img src="https://example.org/auth.php?key=QnaVBhj7mqwaBK9xq6">

    При авторизации создаётся и записывается в базу пачка одноразовых временных токенов, привязанных к только что авторизованному пользователю. При загрузке auth.php проверяется этот токен (передается через key в примере выше) и если токен ещё не просрочился, то выдаётся однопиксельный прозрачный gif с авторизационным кукисом. Этот кукис привязывается к домену, с которого загружается картинка.
    Ответ написан
    1 комментарий
  • Зачем освобождать память в C?

    Vamp
    @Vamp
    Потому что неосвобождённая память остаётся занятой даже после выхода указателя за пределы области видимости. В итоге если вы будете только аллоцировать память, но не освобождать её, то рано или поздно программа сожрёт всю память в системе.
    Ответ написан
    1 комментарий