Ответы пользователя по тегу PHP
  • Как в MODX вывести данные в зависимости от полученного значения из сниппета?

    @alexalexes
    Голый чанковый синтаксис:
    [[*alias:is=`алиас_последнего_значения_uri,_но_не_факт,_что_не_будет_пересекаться_с_одноименными`:then=`вызов_чанка_блока_или_чистый_html`]]

    С использованием сниппета If (нужно установить одноименный пакет):
    [[!If?
      &subject=`[[*alias]]`
      &operator=`==`
      &operand=`алиас_последнего_значения_uri,_но_не_факт,_что_не_будет_пересекаться_с_одноименными`
      &then=`вызов_чанка_блока_или_чистый_html`
    ]]
    Ответ написан
    Комментировать
  • Как сделать подстановку в json файл?

    @alexalexes
    Вычитать файл csv в ассоциативный массив вида:
    $city_dictionary[$en_name] = $ru_name;
    И произвести замену:
    //$data['response'][0]['cities'][0] - вы уверены, что по конечному нулевому индексу находится весь список, а не один город?
    foreach($data['response'][0]['cities'][0] as $city_key => $city)
    {
      if(array_key_exists($city, $city_dictionary))
       $data['response'][0]['cities'][0][$city_key] = $city_dictionary[$city];
    }

    После этого $data можно конвертнуть обратно в json и в нем будет заменено то, что было в словаре $city_dictionary.
    PS: Метод имеет ограничение. В текущем формате ассоциативного массива $city_dictionary нельзя разместить два равноправных варианта перевода. В случае наличия альтернативного варианта русского перевода, из csv будет извлечен в массив последний вариант.
    PS2: Ассоциативный массив словаря ограничен объемом выделенной оперативной памяти скрипта.
    Для более объемного словаря целесообразно делать выгрузку в базу данных и от туда использовать словарь, либо в memcache - архитектура технологии идеально подходит под условия вашей задачи. Но использовать и ту и другую нужно имея небольшой опыт.
    Ответ написан
  • Почему при возведении -1000 в 1/3 степень получается Nan?

    @alexalexes
    Выражение:
    $result = -1000 ** (1 / 3);
    выполняется с таким приоритетом:
    $result = -(1000 ** (1 / 3));
    Поэтому тут нет NAN.
    Ответ написан
    Комментировать
  • Как правильно вывести массив из бд и перебрать его?

    @alexalexes
    На этом фрагменте строки:
    => '{type' =>
    у вас должно закрасться подозрение, что нужно собирать вложенный объект как-то по другому, а именно:
    $taskList[] = array( // старый способ объявления массива, можно [, но не суть
    'type' => 'Feature', 
    'id' => $row->id, 
    'geometry' => // в json-е встретилась фигурная скобка (свойство-объект) - повторяю ее открытие в своей конструкции 
    [ // это хоть и объявление массива, но в определенном режиме в JSON конвертнется в объект, так как имена свойств ассоциативные
      'type' => 'Point', 
      'coordinates' =>
        [ // а тут массив останется массивом, так как о ключах ни слова (они по индексу)
        $row->coordinates,
        $row->coordinates2
        ] // конец свойства coordinates
    ] // конец свойства geometry
    
    );
    Ответ написан
    1 комментарий
  • Какая разница между генераторами и массивами?

    @alexalexes
    https://www.php.net/manual/ru/language.generators....
    foreach (xrange(1, 9, 2) as $number) {
        echo "$number ";
    }

    Если рассказать простым языком магии, то при таком вызове внутри foreach, функция xrange определяет, что ее результат не собираются записывать в отдельную переменную, а собираются перебирать по элементам.
    Поэтому, когда выполнение интерпретатора php доходит до команды as, функция xrange выполняется до первого попавшегося yield внутри этой функции и возвращает это значение. При этом, контекст функции запоминается, а также внутренний цикл:
    for ($i = $start; $i <= $limit; $i += $step) {
                yield $i;
    }

    засыпает до того момента, пока во внешнем foreach выполнение не пройдет команду as еще раз, тогда произойдет размораживание yield и извлечение следующего значения, чтобы использовать его только как один элемент $number.
    За счет того, что as меняет режим работы вызова функции xrange, получается не извлекать все подряд.
    Ответ написан
    Комментировать
  • Как сделать, чтобы неподходящие под условие не забивали лимит?

    @alexalexes
    Вариант А.
    Вы не можете гарантировать, что любой файл изображений, сведения о которых храните в базе, будет доступен физически в любой момент.
    Придется неопределенное число раз постучаться в базу и протестировать каждую запись о файле, существует ли он физически. При тесте формируем список мертвых и живых файлов. После того, как протестировали нужное кол-во живых файлов, можно делать итоговый запрос с оглядкой на список мертвых файлов.
    $need_count = 10;  // сколько требуется файлов для выборки
    $alive_count  = 0;  // сколько живых файлов
    $is_need_repeat = true; // требуется повторить попытку получить живые файлы
    $death_list = []; // сюда накапливаем список id мертвых файлов
    $alive_list = []; // сюда накапливаем список id живых файлов
    while($is_need_repeat) // Если можно делать итерационную попытку и пока не набрали нужное количество живых файлов
    {
    // Этот запрос, чтобы прощупать целостность файлов, достаточно получить только те атрибуты, которые позволяют проверить его путь и запомнить id.
    $database->setQuery("
        SELECT id
        from блаблабла
        WHERE блаблабла 
                   ".(count($depth_list) > 0 ? : ' and id not in ('.join(',',$death_list).') ' : '')." -- отсеиваем мертвые файлы из запроса, они нам не нужны
                   ".(count($alive_list) > 0 ? : ' and id not in ('.join(',',$alive_list).') ' : '')." -- отсеиваем живые файлы из запроса, мы их уже проверяли
        ORDER блаблабла
    limit 0,".($need_count - $alive_count)); // делаем лимит по оптимистичному сценарию, как будто можем получить список файлов, и все они будут живые, но только то кол-во, которое недостает
    while($row = mysql_fetch_assoc($request))
    {
      if(file_exists('/www/ПУТЬ/'.$row['id'].'_100.jpg'))
      {
        $alive_list[] = $row['id']; // файл живой, заносим его id в список
      }
      else
      {
        $death_list[] = $row['id']; // файл мертвый, заносим его id в список
      }
      $curr_alive_count = count($alive_list);
      $is_need_repeat = $curr_alive_count > 0 && $curr_alive_count > $alive_count && $curr_alive_count < $need_count; // необходимо продолжить попытки, если на текущей итерации получили хоть один живой файл, живых файлов на этой итерации оказалось больше, чем на предыдущей, и их кол-во не достаточно до необходимого
      $alive_count = $curr_alive_count; // вписываем кол-во живых файлов на текущей итерации для проверки в будущем цикле (чтобы сравнить результаты двух циклов)
    }
    }
    // теперь можно сделать нормальный запрос, исключив мертвые файлы:
    $database->setQuery("
        SELECT *
        from блаблабла
        WHERE блаблабла 
                   ".(count($depth_list) > 0 ? : ' and id not in ('.join(',',$death_list).') ' : '')." -- отсеиваем мертвые файлы из запроса
        ORDER блаблабла
    limit 0,".$need_count);

    Вариант Б.
    Вы можете гарантировать, что контролируете целостность файлов.
    Тогда в таблице изображений делаете колонку is_del. Когда удаляете файл, вы должны пометить запись о файле в базе как удаленную по этому атрибуту.
    Если вы все таки частично контролируете целостность, то в определенный период времени (например, запускать скрипт по cron раз в час, сутки) вам нужно пройтись по всему списку файлов в базе и проверить целостность каждого файла, и внести актуальную метку is_del.
    Тогда получать живые файлы будет чуть-чуть проще:
    $database->setQuery("
        SELECT *
        from блаблабла
        WHERE блаблабла 
               and is_del is null -- или нулю, в зависимости, что будет по умолчанию
        ORDER блаблабла
    limit 0,".$need_count);
    Ответ написан
    4 комментария
  • Как извлечь данные записи бд по кнопке?

    @alexalexes
    В href записываете get параметр id строки.
    <a href="?id=1">см. запись</a>
    Если текущий список php используется для вывода всего списка, то скрипт нужно модифицировать так, что принимая параметр id, он начал работать по логике вывода сведений одной строки. Нет параметра - выводим все.
    if(isset($_GET['id']))
    {
      // тут код или вызов функции для работы с одной записью
    }
    else
    {
      // тут код или вызов функции для вывода всего списка
    }

    В ветке кода, где работаем с одной записью, подставляем get параметр в запрос:
    msqli_prepare("текст запроса ... where id = ?"); // ставим в нужное место запроса параметрические метки
    msqli_bind_param("i", $_GET["id"]); // передаем параметр на место меток-вопросов, в каком порядке они идут
    msqi_execute();
    // msqli_fetch функция и форматированный вывод сведений

    PS: msqli_* примерно накидал, смотрите документацию, как их вызывать. Идея и порядок кода примерно такой будет.
    Ответ написан
  • Как быть с 4 уровневой вложенностью кавычек в HTML?

    @alexalexes
    На более вложенном уровне экранирования нужно два символа экранирования \
    Первый символ экранирует символ экранирования для следующего уровня.
    Второй символ экранирует кавычку, чтобы не включало закрытие кавычки на уровне PHP.
    $value = '<form method="post">
    <button type="submit" onclick="return confirm(\'Вы уверены, что хотите сменить статус на \\\'Доставлен\\\' ?\')">Доставить</button>
    </form>';

    Вариант 2. Чтобы забыть об экранировании на уровне PHP, можно воспользоваться синтаксисом heredoc, который позволит вставлять любое константное содержимое без явного экранирования.
    $value = <<<ANY_RANDOM_SYMBOLS_FOR_BORDER
    <form method="post">
    <button type="submit" onclick="return confirm('Вы уверены, что хотите сменить статус на \'Доставлен\' ?')">Доставить</button>
    </form>
    ANY_RANDOM_SYMBOLS_FOR_BORDER;
    Ответ написан
    1 комментарий
  • Как понять хорош ли код?

    @alexalexes
    Для анализа производительности необходимо в вашем скрипте поставить контрольные точки, в которых будут логироваться текущее действие. Так можно понять, на что обратить внимание.
    Например, можно составить такой лог:
    0 мс: начало скрипта
    12 мс: начало чтения файла
    24 мс: конец чтения файла
    30 мс: запрос единицы товара
    34 мс: обновление цены единицы товара
    35 мс: запрос единицы товара
    36 мс: обновление цены единицы товара
     ....
    12345 мс: конец скрипта.

    PS: Логировать можно не только время, но и используемую память, и другие интересуемые параметры в момент фиксации точки.
    Ответ написан
    3 комментария
  • Нужно ли проверять длину значений, приходящих от пользователя?

    @alexalexes
    Всегда полезно проверять мин. макс. параметры всех значений и кидать исключение в случае чего.
    А вообще, есть кейсы, где лучше поругаться на слишком большую длину сообщения.
    Например, когда будете сохранять это сообщение в СУБД MySQL в поле типа данных varchar(10000) с превышением длины, то СУБД в случае превышения длины строки по тихому сохранит первые 10000 символов, а переполненную часть - нет. Так что лучше проверить длину сообщения, и если она не соответствует регламентированной, то не давать зеленый свет для обработки.
    Ответ написан
    Комментировать
  • Как создать ссылку в базе данных?

    @alexalexes
    Общий ответ с точки зрения любой СУБД по отношению к любому способу вывода (выкатить что-то на веб-страницу) - никак. База данных ничего не знает, что такое ссылка в веб-документе. Она оперирует такими понятиями, которые позволяет хранить данные, как таблица, столбец, строка, значение атрибута. Из таблиц и связующих атрибутов таблиц - внешних и внутренних ключей строится структура данных для вашей предметной области.
    С другой стороны, у вас имеется ссылка в веб документе, которая имеет некоторые атрибуты, пригодные для хранения в базе данных: href - адрес ссылки, title - подсказка на ссылки, и возможно, какой-то текст, который обрамляет эта ссылка.
    Попробуйте значения этих атрибутов поместить в таблицу базы данных links, со столбцами:
    link_id, -- идентификатор записи в таблице ссылок
    link_href, -- ссылка
    link_title, --подсказка ссылки
    link_text -- текст ссылки
    И с помощью запроса к СУБД, а также с помощью PHP сделайте форматированный построчный вывод данных:
    // тут опущены действия связанные с формированием запроса,
    // на этом этапе вы каким-то способом получили выборку данных из таблицы links
    foreach($rows as $row)
    {
      echo '<a href="'.$row['link_href'].'" title="'.$row['link_title'].'">'.$row['link_text'].'</a><br/>';
    }

    Это и будет самый примитивный пример, как хранить сведения о ссылки и как их выводить.
    В реальном проекте будет несколько сложнее выглядеть таблица, где хранятся подобные сведения.
    Ответ написан
    Комментировать
  • Как сделать сортировку?

    @alexalexes
    Добавьте еще два параметра в ту часть запроса, которая отвечает за сортировку.
    order_column - по какой колонке сортировать, order_direct - в каком направлении.
    Пример с limit намекает, как это сделать.
    $limit = 'limit 0, 500';
    $order_column = 'added';
    $order_direct = 'desc';
    if(isset($_GET['order_col']))
    {
      if($_GET['order_col'] == 'updated')
        $order_column = 'updated'; // не вздумайте подставлять из GET название колонки, будет sql-инъекция!
    }
    if(isset($_GET['order_dir']))
    {
      if($_GET['order_dir'] == 'asc')
        $order_direct = 'asc'; // не вздумайте подставлять из GET название клаузы, будет sql-инъекция!  
    }
            if (isset($_GET['ajax2'])) $limit = 'limit '.(int)$_GET['offset'].', 30'; // тут застраховано от инъекции при помощи преобразования в int!
    
            $rows = fs::getObjects($sql, "order by c_object.".$order_column." ".$order_direct." ".$limit);
            $rowsCount = count(fs::getObjects($sql));
    
            if (isset($_GET['ajax2']) && !count($rows))
            {
              header("HTTP/1.0 404 Not Found");
              die;
            }

    ПС: Осталось вам дополнить вопрос, "а где мне найти шаблон формы с фильтром списка, чтобы прописать get-параметры order_col и order_dir".
    Ответ написан
    Комментировать
  • Как сделать вывод последнего сообщения между двумя юзерами?

    @alexalexes
    Каждый уважающий себя разработчик должен пройти путь создания своего велосипеда - мессенджера.
    От простой почтовой модели данных, где в записи сообщений есть отправитель и получатель, до модели данных, содержащий беседки (они же чаты, они же диалоги - называйте как хотите).
    Но вопрос не в этом. Давайте попробуем что-то сделать с существующей моделью.
    Время сообщения у нас нет - не проблема. Время летит вперед, и id увеличиваются в ту же сторону - по возрастанию. Значит id достаточно, чтобы различать, какое сообщение пришло раньше, а какое позже.
    Отсутствует сущность беседки - тоже не проблема. Будем считать совокупность сообщений, где отправитель, в которых может быть как сам автор, так и собеседник, или получатель также может быть как сам автор, так и собеседник одной беседкой. Такую совокупность сообщений можно выделить условием: (m2.to_id = m.to_id and m2.from_id = m.from_id or m2.to_id = m.from_id and m2.from_id = m.to_id) - что и будет выделять сущность беседки в упрощенном виде. Для выделения всех сообщений, где участвует пользователь from_id будет выражение: :from_id in (m.from_id, m.to_id). Осталось сообразить, как составить подзапрос с count, чтобы выделить самые поздние сообщения.
    Где-то такой запрос получится:
    select * from messages m
     where  :from_id in (m.from_id, m.to_id)
        and (select count(*)
                  from messages m2
                 where  (m2.to_id = m.to_id and m2.from_id = m.from_id
                       or   m2.to_id = m.from_id and m2.from_id = m.to_id) 
                   and m2.id > m.id
    ) < 1
    order by id desc

    Если понадобится выделить сообщения одной беседки, то скорее всего будет такой запрос:
    select * from messages m
     where (m.to_id = :to_id and m.from_id = :from_id
     or        m.to_id = :from_id and m.from_id = :to_id)
    order by id asc
    Ответ написан
    Комментировать
  • Почему перестал работать file_get_contents?

    @alexalexes
    Попробуйте использовать более гибкие опции stream_context_create, в качестве одного из параметров функции file_get_contents. Попробуйте 'verify_peer' => false.
    $url = 'https://secure.example.com/test/1';
    $contextOptions = array(
        'ssl' => array(
            'verify_peer'   => true,
            'cafile'        => __DIR__ . '/cacert.pem',
            'verify_depth'  => 5,
            'CN_match'      => 'secure.example.com'
        )
    );
    $sslContext = stream_context_create($contextOptions);
    $result = file_get_contents($url, NULL, $sslContext);

    Источник.
    Ответ написан
  • Нужно ли фильтровать глобальные переменные в PHP?

    @alexalexes
    Можно ограничится проверками только тех данных, которые приняты от клиентской части системы и могут быть заведомо изменены пользователем так, чтобы вызвать неожиданное поведение в серверной части.
    Если один метод генерирует данные для другого метода на стороне сервера, и они без проверок не выходят транзитом через клиентскую часть приложения или через другую часть приложения, где есть канал связи (точка отказа), то можно не делать проверки в каждом методе. Важно соблюдать условие, что если какой-то элемент данных пришел от пользователя и на входе в серверной части его проверили один раз (может быть даже заэкранировали), то внутри системы этим элементом данных уже можно спокойно пользоваться.
    Нужно ли фильтровать данные из _COOKIE, _HEADER, _SERVER,
    к примеру
    ?query=
    выдает модальное окошко если запустить $_SERVER['[REQUEST_URI']

    Вы ничего не запускаете, вы просто отдаете ответ браузеру (делаете эхо) от того, что пришло в части строки адреса, где содержатся get-параметры.
    Браузер, не получив внятного описания, что ему ответили html содержимым, пытается обернуть ответ в нечто валидное по html нотации:
    <html>
    <body>
    ?query=<script>alert('о, привет!'); </script>
    </body>
    </html>

    Далее, браузер пытается воспроизвести это. Строит DOM-дерево, находит тег script, пытается выполнить у себя JS-код. На этом можно обжечься когда выводите сведения в textarea.
    $unsafe_string_for_html = "<script>alert('о, привет!'); </script>";
    echo '<textarea>'.$unsafe_string_for_html.'</textarea>';

    Чтобы такое не происходило, перед выводом небезопасных для html разметки элемента данных, нужно над ним провести экранирование.
    $unsafe_string_for_html = "<script>alert('о, привет!'); </script>";
    $safe_string_for_html = htmlspecialchars($unsafe_string_for_html);
    echo '<textarea>'.$safe_string_for_html.'</textarea>';

    Можно провести экранирование и в момент получения данных, тогда при отдаче браузеру можно будет быть уверенным, что это не выполняемые инструкции, а текстовые данные с точки зрения html. Однако, когда сохраняете в базу эти данные или делаете их обработку, нужно иметь ввиду что они прошли экранирование по html и содержание некоторых символов будет немного другое чем это проецируется в браузере.
    Ответ написан
    1 комментарий
  • Как реализовать такое на сайте?

    @alexalexes
    Самый простой концепт:
    1. При успешной авторизации создаем переменную cookie с временем жизни равным времени жизни сессии, с доступом http only, с идентификатором сессии.
    2. Если пользователь разлогинивается, то уничтожаем переменную cookie с идентификатором сессии.
    3. При генерации страницы в php, где нужно вывести статус авторизации, проверяем наличие живой куки.
    4. Если совершаем действие, требующее авторизации, дополнительно проверяем время жизни сессии в базе данных (если такой учет ведется).
    Ответ написан
  • Зачем нужен символ "&" перед функцией?

    @alexalexes
    Ответ написан
    Комментировать
  • Как лучше реализовать вывод в корзину?

    @alexalexes
    1. Ограничьте количество предметов в корзине каким-то макс. значением, оно должно быть явно задано, но не вызывало дискомфорта у пользователей (оно должно иметь такое значение, чтобы в нормальных человеческих условиях оно едва достижимо).
    $max_cart_item_count = 500;
    $curr_cart_item_count = min(count($_SESSION['cart']), $max_cart_item_count);
    for($i = 0; $i < $curr_cart_item_count; $i++)
    ....

    2. Есть опасность применения SQL инъекций.
    В этом месте, где подставляется переменная в текст запроса, если умело закомментировать кавычку, то можно получить полный доступ к базе.
    WHERE `type`= '$id'
    Избегайте подставлять любые переменные с пользовательскими данными в текст запроса ("с пользовательскими данными" и "в текст запроса" - нужно подчеркнуть и осмыслить как отдельные понятия с точки зрения безопасности). Для этого существует специальные методики подготовки запроса:
    $stmt = $conn->stmt_init();
    $stmt->prepare("SELECT * FROM `items` WHERE `type` = ?"); // подставляем текст запроса, причем на месте входных параметров ставим специальные маркеры - плейсхолдеры.
    $stmt->bind_param("i",  $id); // подставляем на место плейсхолдера значение параметра как целочисленный тип
    $stmt->execute(); // вот теперь можно выполнить запрос
    $result = $stmt->get_result();
    while($row = $result->fetch_assoc())
    {
     // Обработка результатов
    }
    Ответ написан
  • Отправка изображения canvas на сервер PHP?

    @alexalexes
    btn_send.addEventListener('click',async() => {
        let blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));
        let formData = new FormData(); // Создаем объект формы для наполнения данными, словно мы работаем с тегом form в html.
        formData.append('canvas_field', blob, 'canvas.png'); // это эквивалент тому, что в нашей html форме создаем поле input type="file" name="canvas_field" value="canvas.png" и прикрепляем туда содержимое blob (метод append сам расшаривает blob, дополнительных преобразований не требуется). Естественно, на DOM-дереве это никак не отразится, это модель формы в памяти JS.
        let response = await fetch('web_tp_word.php', {
        method: 'POST',
        body: formData // для fetch не нужно дополнительных параметров, чтобы объяснить как работать с formData. Идеально, чтобы завернуть файл в форму и отправить на сервер.
        });
    });

    На стороне сервера:
    var_dump($_FILES['canvas_field']); // Сервер при вызове скрипта сложит данные файла во временный файл, а как их получить - в $_FILES. Далее можно обрабатывать всеми теми способами, предназначенные для работы с $_FILES. И после уже неважно, как их сгенерировали на клиенте.
    Ответ написан
    1 комментарий
  • Почему не стоит передавать аргументы в функцию по ссылке?

    @alexalexes
    В отдельных задачах только передача по ссылке и спасет, если стоит цель не хапнуть лишней оперативной памяти при выполнении скрипта.
    Например, вам нужно обработать массив с over9000 элементами какой-то отдельной функцией. При существовании этого массива съедается 70% оперативной памяти. Вот тут вам ссылка на этот массив в помощь.
    Ответ написан
    Комментировать