Ответы пользователя по тегу PHP
  • Как правильно подходить к возвращаемым типам функциях php?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Хорошие вопросы.
    Разумеется, увеличивать количество запросов к БД ради красивой типизации - это абсолютно не вариант.

    Как можно увидеть, в приведенной цитате написано не однозначно, а "скорее всего".
    И поэтому надо конечно стараться, чтобы функция возвращала какое-то одно значение, но не делать этого любой ценой. Поэтому getUserDataFromDatabase вполне может возвращать array|false, а getUserIdFromDatabase - int|false, хотя я бы с ней не заморочивался, первой вполне достаточно. А userIsPresentInDb вообще не нужна, вместо неё можно использовать одну их предыдущих.

    Другое дело, что нужного результата можно добиться и другим способом.
    Ведь вполне можно вернуть пустой массив. И таким образом формально соблюсти типизацию.

    return $result ?: []; // дёшево и сердито

    Но это будет всё равно не очень красиво. В одной стороны - да, для проверки, вернула ли функция непустое значение, такой вариант вполне сгодится. Но с другой стороны все равно как-то неаккуратненько. Ведь мы на самом деле ждём не абы какой массив, а вполне определённый, с конкретным набором полей. То есть, если эта функция вернет массив вида [0, 42, 100500], то это будет явно не то, что нам нужно, но при этом типизация и слова поперёк не скажет.

    Если думать от типизации, то функция getUserDataFromDatabase, возвращающая какой-то абстрактный массив - это нонсенс, бессмыслица. Эта функция должна возвращать юзера. Поэтому в идеале надо придумать способ указать, что функция возвращает не абстрактный, а конкретный массив определенного формата. Тем более, что такой способ как раз есть, ведь типизованный массив - это же объект! И для данного случая даже специальный паттерн есть - ValueObject (хотя в случае с пользователем лучше будет все-таки делать полноценный класс, содержащий не только данные, но и методы, например auth(), который сравнивает хэш пароля с введенным).

    Соответственно, в идеале функция должна возвращать объект, представляющий пользователя.

    И уже в этом объекте поле id может либо либо быть нулём, либо иметь какое-то положительное значение. Или даже объект может содержать отдельное свойство, заполнен/не заполнен.

    И вот в этом случае типизация заиграет совсем новыми красками, и будет использоваться на 100%
    function getUserFromDb( int $userId ) : UserObject {
            return $result ? UserObject::fillFromArray($result) : new UserObject();
    };
    $user = getUserFromDb();
    if (!$user->id) {
        // нинашли :'(
    }


    Но опять же, как пишет ниже Сергей delphinpro, зависит от задачи.
    И решив проблему правильной типизации значения, которое функция вернет, если нашла пользователя, можно вернуться к вопросу о том, что возвращать, если функция ничего не нашла. И в этом случае вполне подходящим вариантом будет вернуть null
    function getUserFromDb( int $userId ) : ?UserObject {
        return $result ? UserObject::fillFromArray($result) : null;
    };
    
    if ($user = getUserFromDb()) {
        // нашли :)
    }
    Ответ написан
    6 комментариев
  • Как сделать сделать поиск по строке php?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    stripos и strpos работают правильно

    А если вам вдруг кажется, что они работают неправильно, то надо привести пример.
    И тогда вам сразу объяснят, что вы делаете неправильно
    Ответ написан
    4 комментария
  • Есть ли смысл в csrf токене при использовании samesite cookie?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Нет, не нужна, но надо помнить, что samesite=laх не защищает от поддельных GET запросов.
    Хотя конечно в первую очередь GET запросы не должны использоваться для изменения состояния сервера
    Хотя вот коллеги поправляют, что ещё зависит от браузера, поддерживает ли он этот стандарт.
    Ответ написан
  • Почему возникает ошибка Call to a member function fetch_all() on null?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Я с очень большим трудом могу представить себе код, который может вывести эту ошибку.
    Сам по себе метод query() не возвращает null. При ошибке, с отключенными исключениями, он вернет false, и ошибка будет другая.
    Если режим исключений включен, то я не представляю, как здесь можно поймать исключение, при вызове по цепочке.

    У меня получился только такой говнокод, но он, во-первых, заведомо бессмысленный и сам по себе, а во-вторых, совсем не похож на код, который автор нарисовал в вопросе.
    $result = null;
    try {
        $result = $mysqli->query("SELECT ...");
    } catch(Throwable $ex) {}
    $result->fetch_all(MYSQLI_ASSOC);
    Ответ написан
    Комментировать
  • По какой причине сайт не работает на хостинге?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Путь истинный:
    1. Зайти в панельку своего хостинга, найти там, где лежат лог ошибок
    2. В нем всё написано.
    3. Во всех непонятных ситуациях повторить пп. 1-2

    Добавлять строчку ini_set('display_errors', 1); не следует. На хостинге вывод ошибок должен быть выключен. И даже в качестве временной меры это добавлять бессмысленно - ошибки всегда будут, надо учиться работать с логами.
    Ответ написан
    Комментировать
  • Как преобразовывать заглавную букву в главную и обратно при поиске как в гугле?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Никак не преобразовывать. Все прекрасно ищется и так.
    Ответ написан
    Комментировать
  • Как сократить время до получения первого байта от сервера на хостинге?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    То что вам нужно, называется "профайлинг".
    Его можно делать с помощью разных инструментов, таких как Xdebug, но можно и коряво, вручную расставляя код типа такого
    $microtime = microtime(1);
    // что-то делаем
    $timer['sql'] = microtime(1) - $microtime;
    Потом куда-то выводим этот таймер, чтобы никто не видел.

    Но в принципе тут и без профайлинга можно сказать, что тормозит запрос в БД.
    На шаредах всегда так. Хотя кривые руки при работе с БД тоже скорее всего виноваты.
    Ответ написан
    6 комментариев
  • Форма обратной связи по SMTP работает, вводя только мою почту? Как исправить?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Вам же человеческим языком пишут, что нельзя подставлять липу в $mail->setFrom($email);
    Или просто уберите эту строчку, или пишите в нее СВОЮ ПОЧТУ.

    Как вариант, можете перед setFrom добавить addReplyTo()
    Ответ написан
    6 комментариев
  • Как вынести запросы rest api на постоянное соединение?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Ответ тут очень простой - никак.
    Мало того, что вешать все запросы на одно соединение просто глупо, когда БД может обрабатывать их тыщи. И та же нода, если делается по-уму, то использует пул соединений, а не одно.
    Но, главное, РНР так не работает.

    И проблема здесь не в количестве соединений, а в том, кто писал код.
    Если в коде используется только одно соединение за все время работы скрипта (а не открывает новое соединение на каждый запрос, как у всех гениальных скриптописателей), и база данных устроена по уму (не тормозит), то хватит даже самого дохлого хостинга с лимитом в 5 одновременных подключений. Просто потому что любой запрос к рест апи должен работать не дольше 0.01 секунды. И любые проблемы будут появляться только при частоте обращений больше 100 в секунду.

    Соответственно, вместо фантазий про "одно соединение" надо переделывать кривой код и базу данных.
    Ответ написан
    Комментировать
  • Как создать базу данных и связать с PHP и MySQL?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    База данных нинужна
    Достаточно будет
    <?php
    file_put_contents('databaza.txt', json_encode($_POST, JSON_UNESCAPED_UNICODE)."\n", FILE_APPEND);

    ну и чтобы вывести
    foreach(file('databaza.txt') as $loh) {
         echo json_decode($loh, true)['email'], "<br>\n";
    }


    Ну а форму на HTML я думаю Frontend developer (senior+) уж как-нибудь осилит.
    Ответ написан
    2 комментария
  • Как написать регулярное выражение для обработки разметки из ckeditor?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    1. Никогда не используйте регулярки для парсинга HTML
    2. Никогда не задавайте вопрос про свой вариант решения проблемы. Всегда задавайте вопрос про саму проблему.
    3. Htmlpurifier, HtmlSanitizer
    Ответ написан
    Комментировать
  • Как оптимизировать код php во избежании дублирования, при добавление данных в БД?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Для того, чтобы не повторять программный код, специально были придуманы подпрограммы.
    Когда один и тот же код может выполняться для разных наборов данных.
    В РНР есть несколько способов сделать подпрограмму, но самый удобный - это написать функцию.
    Важно только соблюдать чувство меры и принцип единственной ответственности - функция должна делать что-то одно. К примеру, если она должна что-то писать в базу данных, то не должна при этом с базой соединяться.
    function save_data($pdo, $data, $type) {
        $stmt = $db->prepare('INSERT INTO ads (snippet, amp, date, anchor, link,type) 
                              VALUES (:snippet,:amp,:date,:anchor, :link, :type)');
        $pdo->beginTransaction();
        foreach ($data as $values) {
            $values['type'] = $type;
            $stmt->execute($values);
        }
        $pdo->commit();
    }

    Как я уже писал ранее, для скорости и консистентности множественные вставки желательно заключить в транзакцию.

    И вызывать примерно так

    $db = new PDO('mysql:host=localhost;dbname=test', 'root', '');
    // ТОЛЬКО EXCEPTION! c warning-ами в детский сад ходите детей пугать
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
    $file = file_get_contents("ads.json");
    $data = json_decode($file, true);
    save_data($db, $data['p1']['ads'], 'ads');
    
    $file = file_get_contents("May-12_17-57-28.json");
    $data = json_decode($file, true);
    save_data($db, $data['p1']['serp'], 'serp');


    На этом этапе, посмотрев на повторяющийся код, можно сделать еще одну итерацию
    function import_data($filename, $type, $pdo) {
        $file = file_get_contents($filename);
        $data = json_decode($file, true);
        save_data($pdo, $data['p1'][$type], $type);    
    }

    Но можно уже и не частить
    Ответ написан
  • Не срабатывает sql запрос. Как быть?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Не выполняется mysql запрос

    Но в данном случае можно сократить путь "получение сообщения об ошибке - чтение сообщения об ошибке - гугление сообщения об ошибке".
    Надо эти черточки и нолики перенести из bind_param в сам запрос.
    Ответ написан
    Комментировать
  • Почему неверно считается результат арифметического выражения?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    3 комментария
  • Как лучше сделать добавление корзины/избранного?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Все очень просто.
    Надо всего лишь различать зарегистрированных и незарегистрированных пользователей.
    Для первых ничего не пишется на клиента, а только в базу.
    Для вторых ровно наоборот - в базу писать нечего, поэтому все сохраняется локально.
    Плюс нужна процедура мержа с клиента в базу, когда в момент авторизации что-то есть локальное. По уму надо у пользователя спросить, "вот такие-то товары были в корзине, добавить их к вашей?" Но многие магазины не заморочиваются и просто сливают локальную корзину с той что лежит в базе.
    Ответ написан
    9 комментариев
  • Как из JSON корректно передать данные в базу данных?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Немного подрихтуем ученический ответ Taulan Khatuaev

    // Параметры подключения к БД, желательно держать в отдельном файле
    $username = 'myusername';
    $password = 'mypassword';
    $dbname = 'mydatabase';
    $dbhost = '127.0.0.1';
    
    // Подключение к БД
    $dsn = 'mysql:host=$dbhost;dbname=$bname;charset=utf8mb4';
    $options = array(
        // Обязательно - режим информирования об ошибках
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    ); 
    $pdo = new PDO($dsn, $username, $password, $options);
    
    // Подготавливаем запрос
    $stmt = $pdo->prepare("INSERT INTO mytable (snippet, amp, date, anchor, link)
                           VALUES (:snippet, :amp, :date, :anchor, :link)"
    );
    
    // Используем транзакцию для скорости и консистентности
    $pdo->beginTransaction(); 
    // Перебираем массив
    foreach ($data as $values) {
        // Выполняем запрос с очередной порцией данных
        $stmt->execute($values);
    }
    // Завершаем транзакцию
    $pdo->commit();
    Ответ написан
  • Можно ли Views созданное в базе данных вывести на сайт?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Можно, но не нужно.
    Представления это те же таблицы, вид сбоку. Обращение к ним ничем не отличается от обращения к таблицам.
    Но вам не нужно никакое View, оно не решает ни одну из ваших проблем.
    Вам нужен банальный джойн с группировкой.
    И немного навести порядок в голове.
    Ответ написан
  • Php ошибка: Parse error: syntax error, unexpected token «}» in C:\Apache\Apache24\htdocs\postgresql.php on line 5. Что не так?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Две главные вещи, которые освоить перед тем как начинать знакомиться с программированием.
    1. Умение читать
    2. Умение логически мыслить, на уровне силлогизмов "если только программисты носят красные рубашки, а петя в красной рубашке, то он программист"
    Это очень важные умения, без которых в программировании совсем никак.

    Сначала читаем сообщение об ошибке:
    Ошибка синтаксиса. Неожиданный элемент «}» в 5 строке.
    Затем считаем строки. 5 строка это "echo "PostgreSQL connection failed";"
    Символа «}» в ней нет.

    Далее делаем логическое умозаключение:
    Если в приведенном коде на 5 строке нет символа «}», это означает, что сообщение об ошибке относится к какому-то другому коду, а совсем не к этому. То есть искать в нем ошибку бесполезно.
    Надо найти тот код, который вызывает эту ошибку, и искать проблему в нем.
    Ответ написан
    Комментировать
  • Экранирование sql запросов, достаточно ли функции?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Эта функция не самописная, а цельнотянутая. Причем из самых смрадных помоек интернета.
    К защите от инъекций не имеет вообще никакого отношения.

    - trim() ни к инъекциям, ни к защите отношения не имеет
    - stripslashes() просто бессмысленная функция, которая только портит данные
    - htmlspecialchars() не имеет отношения к SQL. Применяется при выводе данных, а не при получении
    - real_escape_string() - единственная функция, которая имеет отношение к SQL, но при этом вообще не предназначенная ни для каких защит.

    Попробуйте на основании этой информации самостоятельно оценить полезность вашей функции.

    Возьмем классический пример
    $_GET['id'] = '1;DROP TABLE Students;';
    $id = formatstr($_GET['id']);
    $sql = "SELECT * FRPM Students WHERE id=$id";

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

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

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    При соединении с БД надо указывать кодировку.
    Поскольку на сайте используется юникод, то соответственно указывать надо utf8mb4

    А сессии тут вообще не при чем.
    Ответ написан
    1 комментарий