Ответы пользователя по тегу PHP
  • Ошибка trying to access array offset on value of type null in?

    batyrmastyr
    @batyrmastyr
    Isset, причём в форме
    $options = $this->tree["main"]["home"]["options"] ?? $this->tree["main"]["options"] ?? $this->tree["all"]["options"];

    Но сперва прикинуть какой из уровней оказывается null вместо массива: $this->tree["main"]["home"], $this->tree["main"] или $this->tree и насколько такая ситуация нормальна?
    Ответ написан
    2 комментария
  • Стоит ли изучать PHP на Metanit?

    batyrmastyr
    @batyrmastyr
    Не стоит, это кривое обновление какого-то самоучителя из нулевых.
    Примеры из «руководства по php 8»
    1. Установка: «качаем пыху под винду, ставим апач». Зачем апач в 2021 году, если примитивный веб-сервер встроили несколько лет назад?
    2. Константы
    608ade28777c9036143560.jpeg
    А теперь смотрим на изменения в PHP 8:
    Возможность определять константы без учёта регистра была удалена. Третий аргумент define() больше не может быть true.

    3. Под видом «предопределённых констант» подсовывают неполный список «магических констант», потеряли ::class которому уже лет 8.

    В целом, как уже сказал FanatPHP, там слишком много примеров того «норм из нулевых», но сейчас за такое бьют линейкой по рукам.
    Ответ написан
    Комментировать
  • Как правильно работать с большим количеством данных?

    batyrmastyr
    @batyrmastyr
    - получение данных об общем количестве записей для построения пагинации, это SELECT count(id)

    1. count(*), а не count(id)
    2. если вас не сильно интересует абсолютно точное значение для миллионов результатов, то делаете оценку количества, начать проще с EXPLAIN <текст запроса> вы можете получить оценку количества результатов. Мы для себя решили, что если по оценке меньше 50 000 строк, то вслед за этим делаем обычный SELECT count(*) для получения точного количества.
    Потом дергается запрос для получения данных на экспорт

    1. Пожалуй, вам от этого нужно избавляться в первую очередь. Нажал человек на кнопку "экспортировать" - экспортируете, а до этого и дёргаться нет смысла. Фильтры можно получить либо при клике, либо из заголовка referer
    2. Если вам нужно абсолютно все данные, то ставите задание на экспорт в очередь и выполняете его в отдельном процессе, сохраняете в файл. Для пользователя рисуете прогресс выполнения и выводите его в нажатую пользователем кнопку, хотя можно тупо на отдельной странице выводить список "заказанных" выгрузок и ссылки на скачивание.
    Запросы на каждый выпадающий список в фильтрах - SELECT distinct field_name

    Можно с какой-то периодичностью выгружать выхлоп таких запросов в материализованное представление / справочную таблицу / ENUM. Для обновления таких справочников "в реальном времени" можно повесить триггер на вставку в основную таблицу который будет делать INSERT INTO dictionary (value, column_oid) ON CONFLICT / ALTER TYPE ADD VALUE IF NOT EXISTS
    После чего в основной таблице заводите рядом поле под идентификатор в справочнике и индексируете уже его.
    Запрос при фильтрации и сортировке - SELECT * FROM some_table WHERE field_name LIKE '%value%'

    1. если у вас значения длинные (от 8 - 10 символов), то стоит попробовать триграммные индексы. Но на коротких значениях они могут замедлить поиск раза в полтора-два.
    2. Полнотекстовый поиск. В частности есть поиск лексемы по префиксу ts_tsquery('сло:*') (быстро найдёт и "слово" и "словарь", но не найдёт "однословное")
    3. Для полей по которым вы сделаете словари лучше делать поиск через словарь SELECT * FROM table WHERE column_dictionary_id IN (SELECT id FROM dictionary WHERE value LIKE '%текст%'). В словаре у вас наверняка на порядок - три меньше значений, а несколько сотен или тысяч значений в IN постгрес нормально пережуёт.
    Полей много, разные даты, guid, названия проектов, данные из поля типа json, цены.

    Активнее используйте функциональные и частичные индексы.
    Например, у нас есть кадастровые номера. Триграммный индекс по ним весит 56 мбайт, а BTREE по номерам урезанным до кадастровых кварталов - 15 мбайт, в поиске к "cadastre_id LIKE '11:22:333333:1%'" добавился "AND to_quarter(cadastre_id) = '11:22:333333'", но сам поиск получается на порядок быстрее (~5 мсек вместо 50 - 70).
    Главное не забывайте о стоимости этих самых функций - индекс по to_quarter может строиться всего в 1,5 раза дольше нефункционального, если делать LEFT(cadastre, -(position(':' IN reverse(cadastre))), а может и в 100 раз, если использовать регулярку.
    На все индексы не поставишь, тем более что один индекс может добавить гигов 5-10 к весу.

    Если ещё не обновились, то обновляйтесь на 13-ю версию, там размер BTREE индексов уменьшили в 3 раза. Ну и посмотрите, возможно вам где-то нужны GIST, GIN или BRIN индексы.
    Ответ написан
    2 комментария
  • Интеграция php с Excel-макросом?

    batyrmastyr
    @batyrmastyr
    1. В сам макрос добавить отправку данных в ваше приложение.
    1б. Если не всем пользователям макроса можно доверять, то вместо отправки из макроса можно выдавать результат работы макроса в JSON, который пользователь сам вставит на сайте.
    2. О том, как мы на PHP запускали настоящий MS Excel ...
    3. Воспользоваться libXL, но она на C++ и обёрток для php вроде бы нет.
    4. Вместо рецепта из варианта 2 можно использовать OLE и из под винды запускать специальный скрипт на php и управлять Экселем.
    Ответ написан
    Комментировать
  • Как правильно реализовать ТОП дня, недели, месяца?

    batyrmastyr
    @batyrmastyr
    1. Вместо top_[daily|weekly|monthly] можно завести общую таблицу top со столбцом для признака, что это дневной/недельный/месячный рейтинг.
    2. Обновлять данные в ней раз в день/час.
    3. В транзакции удаляете старые записи и делаете Insert into (select ..., ‘daily’ as type
    INION select ..., ‘weekly’ as type
    INION select ..., ‘montly’ as type
    ).
    При желании можно суточный рейтинг обновлять раз в час, а недельный и месячный раз в сутки.

    Когда rating_log слишком распухнет, можно будет:
    1) секционировать её по времени. Либо встроенными средствами БД, либо вручную перенося старые записи в таблицу-архив.
    2) Воспользоваться более специализированными решениями: расширение timescaleDb для PostgreSQL или колоночную базу типа ClickHouse. Но оба варианта требуют тестирования перед использованием: например, оба не очень любят работу со строками, а Кликхаус предпочитает, чтобы данные вставляли пачками, а не построчно.
    Ответ написан
    Комментировать
  • Функция создания поста режит Splash?

    batyrmastyr
    @batyrmastyr
    Не экранируйте данные sql запросов самописными функциями, если не хотите получить SQL инъекцию.

    Для экранирования есть (1) mysqli::real_escape_string, (2) привязка переменных в подготовленных запросах
    $data = array_map(
        function ($value) use ($sqlConnect) { 
            return $sqlConnect->real_escape_string($value);
        },
        $registration_data
    );
    $data   = implode(',', $data);
    Ответ написан
    Комментировать
  • Так ли необходимо использовать плэйсхолдеры для избежания инъекций, или написанная собственноручно проверка входных данных будет более чем достаточна?

    batyrmastyr
    @batyrmastyr
    Для чисел хватит и ручной проверки.
    Для произвольных строк ручной проверки не хватит по нескольким причинам:
    1) Экранировать нужно не пару символов и даже не десяток.
    2) Помножим это на разнообразие кодировок и проверку существования пришедших символов в выбранной кодировке ("испорченные биты").
    3) Даже подстановки силами PDO иногда не спасают.
    Ответ написан