Задать вопрос
Ответы пользователя по тегу PHP
  • Как создать ссылку в базе данных?

    @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% оперативной памяти. Вот тут вам ссылка на этот массив в помощь.
    Ответ написан
    Комментировать
  • Как правильно изменить sql запрос чтобы считывать данные с двух строк одной базы данных?

    @alexalexes
    Если предположить, что в таблице _post_extras есть столбец user_id, при этом данная таблица хранит расширенные сведения о записях другой таблицы - _post, каждая запись которой связана с первой таблицей отношением 1 к 1, через ключ post_id (название столбца-ключа как с первой таблицей, так и со второй автор не сообщал, но такая связь обязана существовать), получается следующее:
    SELECT SUM(rating) as rating, SUM(vote_num) as num
      FROM " . PREFIX . "_post_extras as pe
      inner join " . PREFIX . "_post as p on p.post_id = pe.post_id -- тут название ключей post_id с обеих сторон нужно уточнять!
    WHERE pe.user_id ='{$id}'
         and p.category = '{$category_id}'

    Также нужно уточнить автору, что должен вернуть запрос. Данный запрос вернет суммарный рейтинг и голосов по конкретному пользователю в конкретной категории публикаций.
    Ответ написан
    Комментировать
  • Как записать данные из запроса VK в бд?

    @alexalexes
    Отработать навык, как вставить в базу вообще что-то.
    Например, так https://html5css.ru/php/php_mysql_insert.php
    Обратите внимание, что у процесса взаимодействия с БД есть несколько фаз работы:
    подключение к базе, формирование запроса, добавление параметров к запросу, выполнение запроса и получение результата (если нужен действия статус или выборка).
    Ответ написан
    Комментировать
  • Как сделать проверку наличия слов?

    @alexalexes
    Самый тупой вариант без регулярок:
    $email_exploded = explode('@', $email);
    if(count($email_exploded) == 2 && ($email_exploded[1] == 'gmail.com' || $email_exploded[1] == 'mail.ru'))
    {
      echo 'Домен в порядке, проходите!';
    }
    Ответ написан
    Комментировать
  • Как сохранить writeImage все файлы директории?

    @alexalexes
    $imagick->writeImage('*.webp');
    У вас имя файла на протяжении всего цикла одно и то же.
    Чему вы удивляетесь, когда файл перезаписывается несколько раз, и остается последний перезаписанный вариант?
    Решение:
    1. Нужно посмотреть, что выводится в $ext:
    var_dump($ext);
    2. Если это действительно имя открытого файла, то самое грубое решение, добавить нужное расширение в конце, несмотря на наличие jpg.
    $imagick->writeImage($ext.'.webp');
    3. Хорошо бы посмотреть файл лога ошибок php - error_log. Возможно, есть еще какие-то проблемы с записью файлов.
    Ответ написан
  • Как сделать посуточный счетчик посещений (доработать старый)?

    @alexalexes
    Если вы понимаете, что делает этот запрос:
    UPDATE article SET visits=(visits+1) WHERE id='".$theme_idss."' LIMIT 1

    То по аналогии можно плюсовать интересуемый столбец:
    UPDATE article
    SET visits=(visits+1),
          today = today + 1 -- суточные посещения
     WHERE id='".$theme_idss."' -- когда разберетесь, что такое sql-инъекция, то перепишите все запросы, использующие такой стиль подстановки параметров (склеиванием строк)
    LIMIT 1 -- после того, как разберетесь, является ли id первичным ключом, можно убрать лимит

    Но при добавлении столбца нужно позаботиться, чтобы у столбца значение по умолчанию было 0.
    ALTER TABLE article
    ADD COLUMN today int default 0 AFTER visits

    В скрипте, который чистит таблицу раз в сутки предусмотрите запрос, обнуляющий суточный счетчик у всех статей:
    UPDATE article
    SET today = 0

    PS: Если хотите анализировать посещаемость, то лучше воспользоваться готовыми счетчиками от поисковых систем - Google Analytics или Яндекс-метрики.
    Если хотите все же использовать свой лог, то лучше его не удалять. Вдруг захотите выявить среднемесячное/суточное/годовое посещение или в каком то другом разрезе по разным разделам сайта. Для пересчета понадобится каждая запись посещения.
    Ответ написан
    2 комментария
  • Как получить значение элемента, зная его id в xml?

    @alexalexes
    Вам нужно оперировать двумя запросами к xml-дереву:
    1. Определить список узлов, для которых известны категории:
    //items/item[categoryId=//catalog/category/@id]
    2. Определить категорию узла при переборе результата запроса 1.
    //catalog/category[@id="тут вставляем код категории"]

    При вставке узла categoryname нужно ориентироваться не на categoryId, а на portal_category_id, поскольку вставлять новый узел до существующего легче, чем изобретать велосипед с функцией, которая делает вставку после.
    PS: Вопрос выглядит как задание, характерное для фриланса, поэтому можно только посоветовать, как его реализовать, без подробной реализации.
    Ответ написан
    Комментировать
  • Безопасно ли такая авторизация и последующее открытие страниц?

    @alexalexes
    Эта конструкция небезопасна, если у программиста кривые руки.
    if($rows) {
    $logged = true;
    } else {
    $logged = false; 
    }
    } 
    else {
    $logged = false;
    }

    Статус авторизации очень критичен, и если возникает какая-нибудь ошибка в его проверке, то лучше, чтобы по умолчанию он оставался в значении "не авторизован", то есть false.
    С точки зрения безопасности системы, если произойдет какая-нибудь ошибка, то пользователь лучше не дополучит свой статус, чем случайно получит уровень выше привилегий.
    Поэтому до всяких проверок, инициируете состояние по умолчанию, а потом, выстрадав все проверки присваиваете true.
    // Секция, где присваиваются значения по умолчанию основных переменных
    $logged = false;
    .....
    ....
    // начинаем проверку
    if(первое условие)
    {
      if(второе условие)
      {
        if(еще какое-то супер-пупер условие)
        {
           $logged = true; // вот теперь, пройдя все медные трубы, мы изменяем статус
        }
      }
    }
    Ответ написан
    6 комментариев
  • Как найти точку пересечения графиков?

    @alexalexes
    Можно. Но реализовать это можно многими путями.
    Вариант А.
    Серверная часть (то бишь PHP) подготавливает как данные, так и графику.
    Это значит, что вы на серверном скрипте как вычисляете все необходимые точки, так и рисуете график, например GD-библиотекой.
    а) Создаете графическое полотно.
    б) Рисуете в этом полотне необходимые линии.
    в) Возможно, где-то что-то подписываете.
    г) Отдаете клиентской части приложения (то бишь браузеру) бинарный поток файла изображения.
    Самый топорный вариант, исследователь графика будет иметь на руках только картинку, ни смаштабировать, и изменить какие-нибудь атрибуты отображения он не сможет. Единственное преимущество - процесс вычисления графика доступен только вашему скрипту, и никто не сможет провести реверсинжиниринг, если захочет разобраться как вы его генерируете.
    Вариант Б.
    На стороне сервера делаете аналитику, отдаете браузеру готовый html-документ, содержащий canvas с генерированными на стороне сервера векторными объектами (линии графика) в svg формате. Это тоже топорный, но уже промежуточный вариант между А и В. Единственное преимущество - все данные векторные, поэтому они будут легковесны для передачи и воспроизведения в браузере.
    Вариант В.
    На стороне сервера делаете аналитику, отдаете данные о вычисленных точках клиенту в виде json, а на клиенте рисуете график в какой-нибудь js-библиотеке. Выбор библиотек широк: chart.js, D3.js и т.д.
    Это самый прогрессивный вариант, поскольку на клиенте можно менять масштаб в окне просмотра графика, менять какие-нибудь атрибуты этого просмотра и т.д.
    Ответ написан
    1 комментарий
  • DELETE FROM list WHERE id = 'id строки в списке дел'. Как мне указать этот id?

    @alexalexes
    1. Подготовить текст запроса prepare-функцией.
    2. Присобачить нужные параметры запроса bind-функцией.
    3. Выполнить запрос execute-функцией.
    Запрос не выполняется? Посмотреть ошибки.
    PS: В любой непонятной ситуации читай документацию.
    Ответ написан
    Комментировать
  • Как лучше сравнить два файла изображения?

    @alexalexes
    Фишка хэширования в том, что вы считаете его ровно один раз, и при повторном сравнении уже оперируете им, а не содержимым файла.
    У вас есть 1 млн. файлов. При получении каждого файла вы подготовили для него хэш и сохранили этот хэш в отдельном массиве в оперативной памяти, в файле на диске или в таблице базы данных.
    Когда поступит следующий файл, то вы от него тоже получите хэш, но при сравнении вам уже не надо вычитывать 1 млн. файлов, чтобы сделать сравнение, достаточно обратиться к более быстрому источнику за хэшами этих файлов, сравнение которых будет короче и перебор быстрее (если эта база данных, то должны использоваться индексы, если оперативная память, то сбалансированные деревья и бинарный поиск и т.п.).
    Ответ написан
    1 комментарий