• Как выполнить авторизацию в аккаунта при помощи RedBeanPHP?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Если подумать, то почти все вопросы новичков вида "Как сделать ххх" не несут никакого смысла.
    Потому что если открыть сам вопрос, то по коду будет совершенно очевидно, что человек и так прекрасно знает, "как сделать это ххх". А вопрос у него - "как разобраться, почему оно не работает?" или - в более общем виде - "научите меня программировать".

    Ну ОК, давайте учиться.

    Главное в программировании - это делать всё по порядку.

    У вас в текущей задаче участвует примерно миллион разных действий: html форма, код, который получает из неё данные, база данных, код, который ищет данные из формы в базе.
    Если вы ещё не освоились со всеми этими элементами, то не надо их писать все подряд.
    Надо делать по очереди, проверяя каждый этап.
    И вопрос задавать не про весь код целиком, про который вам никто никогда не скажет, почему он не работает, а про конкретный этап.

    В данном случае, если у вас база данных не находит пользователя по емейлу, надо сделать что?
    Правильно - исключить все посторонние этапы. То есть написать емейл руками в скрипте и проверить - находит ли его запрос.
    Если не находит - то здесь же, в скрипте, вывести все емейлы, и посмотреть глазками, а есть ли он вообще в базе данных?
    Если нет - то вот она, причина. Найдена самостоятельно за 5 секунд без привлечения мирового сообщества.
    Если есть, но всё равно не находит - то ищем непечатные символы и опечатки. например с помощью функции urlencode(). И исправляем то что она нашла. Русскую букву c или лишний пробел.

    Если написанный руками емейл находит, а пришедший из формы нет, то надо сделать что?
    Правильно - посмотреть на него глазками. А похож ли он на тот который руками написан? Если похож, то снова urlencode. Если не похож - то разбираться со своей формой.

    Если всё равно не работает, то надо исключить другие посторонние факторы, например библиотеки, особенно такие кривые, как redbean. И отлаживаться на чистом PDO.

    Всё это очень простые логические действия, доступные даже неспециалисту.
    Просто надо не смотреть на свой код с горестным выражением лица, и гадать почему он не работает. И не искать какой-то другой код, который обязательно заработает. А работать над своим кодом. Выполнять простые логичные действия.

    Отдельно хочу заметить, что все описанные действия может сделать только сам программист. Обращаться за ними к посторонним людям бессмысленно. У них нету вашей базы данных, вашей формы, вашего кода. Все эти вещи можно сделать только самостоятельно. И именно это и является программированием. А не вот это вот "всё указываю верно".
    Ответ написан
    4 комментария
  • Может ли такой php код считается сделанным по паттерну MVC?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Нет, с MVC эта поделка в стиле конца прошлого века не имеет ничего общего.

    "index.php, который возвращает в браузер разметку (картинки, формы, ссылки и другой HTML)" не имеет ничего общего с View. В клиент-серверной архитектуре в принципе невозможно реализовать десктопный извод MVC (ну кроме разве что SPA, но здесь явно не этот случай).
    В данном случае вообще непонятно, как это работает, поскольку в любом веб-приложении "инфа приходит" не только, и не столько от пользователя, сколько наоборот - с сервера.
    Вам не приходило в голову, что будет ваша система, если она должна будет по команде отобразить список юзеров? А откуда "view" возьмёт его?

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

    Классический вариант клиент-серверного MVC:

    Модель - вся бизнес-логика приложения. Это означает, что приложение должно уметь полностью выполнять свои функции с помощью одной только модели (на уровне вызова РНР кода и возврата структур данных в РНР). То есть возвращать список пользователей, удалять и добавлять пользователей и так далее. Выполнять все "действия, которые могут быть сложными".

    Контроллер - тонкий интерфейс между моделью и браузером. Понимать НТТР запросы, принимать данные, приводить их в понятный модели вид и отправлять в модель. Возвращать нужные НТТР коды.
    При необходимости что-то вывести - вызывает view и передаёт туда данные, полученные от модели.

    Вью - рендерит полученные данные в зависимости от желаемого формата, обычно - HTML

    Необязательный роутер - при наличии единой точки входа раскидывает апросы по контроллерам.

    Реализация MVC на классическом РНР спагетти-стайл:

    Модель - может быть набор функций, как здесь. Но все "сложные действия", которые складываются "из кирпичиков" тоже должны быть в модели.

    Контроллер - РНР файл, который принимает запрос, например на отображение списка пользователей, вызывает нужную функцию из модели, и отправляет результат в браузер. Это может быть либо НТТР код, или HTML или JSON. В случае с HTML вызывает примитивный шаблонизатор, передавая в него массив с данными и имя файла - шаблона

    Вью - тупо HTML файл с вставками РНР. Делится на две части, общий шаблон сайта и шаблон конкретной страницы/действия.
    Ответ написан
    8 комментариев
  • Как заменить часть текста, если таковой начинается с определённых символов?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Если текст всегда начинается либо с https://, либо с //, то это не замена текста, а добавление.
    Надо проверить первый символ, и в зависимости от результата либо добавить https:, либо не добавлять.
    $url = $url[0] === "/" ? "https:$url" : $url;
    Ответ написан
    Комментировать
  • Fatal error: Call to a member function fetch_row() on a non-object in ../arizonalive.zzz.com.ua/engine/server.php Как нужно исправить?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Это сообщение означает, что возникла ошибка при выполнении запроса.
    Чтобы увидеть, какая именно ошибка, надо перед коннектом к БД написать
    mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
    Ответ написан
  • Существует ли какой-то общепризнанный подход к обработке ошибок в контексте API, где его можно посмотреть?

    ipatiev
    @ipatiev
    Потомок старинного рода Ипатьевых-Колотитьевых
    С дополнением вопрос стал значительно понятнее.

    Разумеется, "решить" нельзя.
    Потому что никакой хендлер не заменит программиста.
    В случае с generic exceptions хендлер не в состоянии узнать, это у вас база отвалилась на запросе, или просто код кривой, который не проверил существование объекта при обращении к методу.

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

    А вот response руками делать - это лишнее. Куда проще кинуть кастомное исключение. Которое уже будет поймано хендлером и отрендерено в зависимости от типа, как это показано в статье.
    Ответ написан
    1 комментарий
  • Можно ли реализовать "умный" поиск по БД?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Это называется полнотекстовый поиск.
    В самом примитивном варианте создаётся fulltext index на поля, по которым будем искать. При этом база данных берет каждую запись в таблице, разбивает её на слова, записывает их в индекс и для каждого слова указывает - в каких строках таблицы оно встречается.
    После этого можно сделать запрос по нескольким словам.
    Например,
    SELECT * FROM goods WHERE MATCH(name) AGAINST ('+выключатель +белый' IN BOOLEAN MODE);

    найдёт таки все белые выключатели

    Но встроенный в MySQL полнотекстовый поиск работает так себе и обычно пользуются внешними системами, Spinx Search или Elasticsearch
    Ответ написан
    4 комментария
  • Как проверить, есть ли в одной из колонок двумерного массива хоть одно непустое значение?

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

    $columns = [];
    foreach($array as $row) {
        foreach ($row as $i=> $col) {
            $filled = $col !== "";
            $columns[$i] = !empty($columns[$i]) ? $columns[$i] : $filled;
        }
    }
    
    foreach($columns as $col => $result) {
        echo "Колонка $col ".($result ? "не" : ""). "пустая\n";
    }
    Ответ написан
    Комментировать
  • Нужно ли фильтровать глобальные переменные в PHP?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Как и многие начинающие, вы путаете фильтрацию (<script>) и валидацию (нужно ли тут проверять пустые ли переменные???), при том что это совершенно разные, никак не связанные между собой задачи.

    И решают их тоже по-разному.

    Фильтрация:
    1. Делается строго в момент использования данных
    2. Не имеет ни малейшего отношения к источнику данных.
    Например, исходя из правила №1, как выше правильно написал alexalexes, при выводе любых данных в контекст HTML, необходимо их обрабатывать htmlspecialchars.
    Исходя из правила №2 (фильтруются любые данные, вне зависимости от источника) становится понятным, что ваш "класс фильтр например который фильтрует входные данные" - это Очень Плохая Идея. Мало того что он никак не фильтрует те данные, которые вы не сочли "входящими" (при том что любая кавычка или знак "меньше" в самых супер-доверенных данных поломает вам верстку не хуже злобного хакера), но главное - на момент централизованной фильтрации вы просто не знаете, как данные будут использоваться. А вдруг это будет не вывод в HTML? А вдруг это XML c из Яндекс.Маркета и ваша "фильтрация" превратит его в тыкву?

    Вывод: фильтровать надо любые переменные, а не только "глобальные", причём не заранее, а строго в момент использования. И не всегда под одну гребёнку, а в зависимости от контекста.
    И в этом случае ваш пример с REQUEST_URI не сработает - он зафильтруется при выводе.

    Валидация:
    Валидация - это проверка данных на соответствие заданным параметрам. Это может быть не только пустота, но и размер, соответствие формату, налитчие уже таких данных в БД - миллион разных проверок.
    Валидация пишется для каждой переменной отдельно.

    Где её делать - спорный вопрос.

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

    При этом по-хорошему, в контроллере тоже необходимо делать валидацию, специфичную именно для входящих данных.
    Например, проверить, что в запросе пришли все требуемые поля, элементы name и password присутствуют во входящем массиве. В этом случае можно в какой-то мере говорить о валидации "глобальных переменных" (хотя на самом деле вы имеете в виду входящие данные)
    Также можно проверять некоторые данные на соответствие формату. Нет смысла дергать модель, если мы заранее знаем, что данные не те. Например, вместо id новости в запросе пришло "админ - дурак".
    Или взять ваш пример с query: если содержимое этой переменной должно соответствовать какому-то строгому формату, который не допускает наличие символов < и >, то вы можете проверить его на соответствие формату, и вернуть 400 ошибку.
    Но опять же - никакой централизованной проверки здесь придумать нельзя. У всех входящих переменных свой собственный формат: у ид из бд - числовой, у емейла - емейл, и так далее. Поэтому проверять надо каждое значение по отдельности. Но все эти проверки в общем не являются обязательными. В отличие от фильтрации.

    То есть можно выделить два типа валидации - валидацию входящих данных (в контроллере), и валидацию на соответствие данных определённому формату (в модели).

    Учитывая, что эти два вида валидации зачастую пересекаются, обычно никто не заморачивается, и всю валидацию делают в контроллере. Что не совсем правильно с точки зрения архитектуры (модель может быть вызвана не только из контроллера), но зато проще в реализации.

    Главное, что важно помнить про валидацию - она не должна быть молчаливой. Если данные не прошли валидацию, это должно вызывать внятную ошибку. В этом случае это действительно поможет при отладке. А если "валидация" молча коверкает данные, то это наоборот - фантастически затруднит отладку
    Ответ написан
    4 комментария
  • Как написать SQL запрос для среза непустых значений?

    ipatiev
    @ipatiev
    Потомок старинного рода Ипатьевых-Колотитьевых
    Select t2.* from t1 join t2 on t2.t1_id=t1.id 
    where t1.key=95


    Это единственный осмысленный ответ на такой вопрос.
    Ответ написан
    Комментировать
  • Как оптимизировать sql запрос (MySQL)?

    ipatiev
    @ipatiev
    Потомок старинного рода Ипатьевых-Колотитьевых
    проблематика в том, что первая таблица (visits), где я делаю выборку по office, даёт сразу много результирующих данных.

    Не факт. БД mysql умеет исправлять самые грубые ошибки, и вполне может переписать запрос так, чтобы не payments джойнилась к visits, а наоборот - как и должно быть.

    (1) индексы на нужных полях есть
    (2) у поля p.date индекса нет


    Я один здесь вижу взаимоисключающие параграфы?

    В общем, добавить индекс на поле date
    И поменять запрос, чтобы payments была первой, а вместо date() нужная дата выбиралась через BETWEEN
    Ответ написан
    Комментировать
  • Что пришло на смену кэшу запросов в Mysql?

    ipatiev
    @ipatiev
    Потомок старинного рода Ипатьевых-Колотитьевых
    В Oracle Mysql/Percona теперь кэша запросов нет вообще. Потому что не нужно и вредно.
    Вместо этого, при необходимости, точечно кэшировать отдельные запросы, на клиенте.
    (Очевидные вещи, типа тюнинга конфигурации сервера и оптимизации запросов я здесь не упоминаю, они сами собой разумеются, и к кэшированию прямого отношения не имеют).

    Ну или использовать MariaDB, там кэш пока ещё остался.
    Ответ написан
  • Можно ли использовать return;?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    return; - это бессмыслица, её надо убрать из кода
    И никакое false она не возвращает.
    Про "еще одно противоположное значение" вас тоже обманули

    Если функции нечего вернуть, то ничего возвращать она и не должна.

    Максимум, где можно использовать пустой return - если он используется для управления ходои исполнения. То есть чтобы досрочно завершить исполнение кода
    Ответ написан
    Комментировать
  • Как вызвать функцию класса php из onclick?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Никак.
    Функцию РНР "из onclick" вызвать нельзя
    Можно вызвать РНР скрипт только целиком, передав ему данные. обычным аяксом
    А уже в РНР можно написать условие, которое, в зависимости от переданных аяксом данных, создаст экземпляр класса и выполнит тот или иной метод.
    Ответ написан
    Комментировать
  • Is_file, file_exist говорят что файла нет, но он есть?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Если видимые пути полностью совпадают, даже в регистре символов, то дело скорее всего в невидимых символах. Например это может быть перевод строки при чтении из текстового файла.
    Для того чтобы их увидеть, можно набросать функцию типа такой
    function print_nonprintable($string) {
        $i = -1;
        $ret = '';
        while(isset($string[++$i])) {
            $ret .= ctype_print($string[$i]) ? $string[$i] : urlencode($string[$i]);
        }
        return $ret;
    }


    И использовать её в сообщении об ошибке. Тогда проблема сразу станет видна.
    Правда, эта функция изуродует русский текст, но с поддержкой многобайтных символов у меня сходу решения нет
    Ответ написан
    Комментировать
  • Как спроектировать систему подписок на сайте?

    ipatiev
    @ipatiev
    Потомок старинного рода Ипатьевых-Колотитьевых
    одна таблица subscribes с вот такой структурой:

    subscribe_id
    subscribe_author_id
    subscribe_object_id

    и, разумеется, выкинуть счетчики из таблицы users
    больше ничего не нужно
    для остального учить базовый SQL. Все подписки пользователя удаляются одним запросом. Все подписчики - тоже.

    Если оставить в стороне нелепые вопросы "а для запроса на удаление тыщи записей какой-то другой SQL нужен?", и вернуться проектированию подписок, то, как правильно подсказывает Slava Rozhnev,

    Во-первых, в таблицу подписок необходимо добавить два составных индекса,
    subscribe_author_id, subscribe_object_id
    subscribe_object_id, subscribe_author_id
    и тогда ужасный запрос count(*) перестанет быть ночным кошмаром, а будет выполняться мгновенно

    А во-вторых, в неё можно добавить внешние ключи, которые будут ссылаться на таблицу users, с опцией каскадного удаления. Тогда отдельный запрос на удаление подписок и вовсе не придётся писать руками, достаточно будет удалить только юзера.

    Запроса на удаление бояться не надо. Этот запрос всего лишь пометит нужные записи, как удалённые, никто файл на диске укорачивать не будет. Потом база данных использует эти же самые ячейки для других подписок.
    Ответ написан
    1 комментарий
  • Почему число не хочет преобразовываться в строку?

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

    Дополнительно с ключом key будут сделаны следующие преобразования:

    • Строки (string), содержащие целое число (int) (исключая случаи, когда число предваряется знаком +) будут преобразованы к типу int. Например, ключ со значением "8" будет в действительности сохранён со значением 8. С другой стороны, значение "08" не будет преобразовано, так как оно не является корректным десятичным целым.


    В свое время я очень крепко налетел на этом преобразовании. тоже думал, что строки кладу ключами...
    Ответ написан
    2 комментария
  • Как попасть в менеджмент в IT?

    ipatiev
    @ipatiev
    Потомок старинного рода Ипатьевых-Колотитьевых
    Что делать, если очень хочется вайти, но с алгоритмами и структурами данных не получается?
    Так и быть, придётся из эникейщиков сразу в начальники, буду руководить.
    Ответ написан
    6 комментариев
  • ООП в php - не понимаю объекты, неправильно передаю ссылки?

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

    Я сейчас напишу минимально приемлемый вариант, но повторять его не надо. Потому что всё равно непонятно будет.
    Надо забыть временно про классы и учить базовый синтаксис РНР. Просто потому что сначала надо выучить базовые понятия - работу с БД, обработку ошибок, разделение ответственности:
    - работа с переменными в запросе ведётся через параметризованные запросы
    - код класса не должно тошнить прямо на экран сообщениями об ошибках
    - метод getArray не должен возвращать объект класса mysqli_result. он должен возвращать массив
    - код, который будет в дальнейшем работать с результатом вызова метода getArray, не должен ничего знать про базу данных

    Про сам же код ООП надо хотя бы один раз посмотреть его в учебнике, а не писать на основе чистой фантазии.
    class CProducts
    {
        private $link;
        public function __construct(mysqli $link) {
            $thi->link = $link;
        }
        public function getArray($lim) {
            $query = "SELECT * FROM products ORDER BY DATE_CREATE DESC LIMIT ?";
            $stmt = $this->link->prepare($query);
            $stmt->bind_param("s", $lim);
            $stmt->exeсute();
            $result = $stmt->get_result();
            return $result->fetch_all(MYSQLI_ASSOC);
        }
    }
     
    mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
    $db = new mysqli($host, $user, $pass, $dbname);
    $db->set_charset('utf8mb4');
    $db->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, 1);
      
    $product = new CProducts($db);
    $result = $product->getArray(3);
    Ответ написан
    Комментировать
  • Как лучше сделать сохранение в базу данных из нескольких запросов для одинаковых полей в базе данных?

    ipatiev
    @ipatiev
    Потомок старинного рода Ипатьевых-Колотитьевых
    Это какой-то ужас.
    Я не знаю, вопрос звучит как "Мне надо надеть штаны на голову и выйти погулять. Я думаю продеть в штанины руки, зайти в туалет, облиться холодной водой, высохнуть, надеть штаны на голову и идти гулять. Вот только боюсь простудиться, потому что вода холодная".

    Весь этот кошмар происходит потому, что вы используете базу данных как микроскоп для колки орехов. Все проблемы тут оттого, что "это" сохраняется в одно поле в базе данных. А должно в разные. Так база данных устроена. Каждая частичка данных хранится в своем поле. Исключения бывают, но именно как исключения, а не как подход по умолчанию, и это явно не ваш случай.

    Надо сделать отдельную таблицу, где будут разные поля, которые привязываются к пользователю по его id. И никаких проблем при редактировании этих полей никогда не будет.

    Я имею в виду что должна это быть одна таблица, которая линкуется к текущей по связи один-ко-многим: таблица из трех колонок - id, user_id и собственно то значение, которое надо менять. Таким образом подмножество этой таблицы с одним и тем же user_id будет представлять из себя то самое "поле", которые вы пытаетесь редактировать сейчас. Но при этом все элементы будут лежать в отдельных строках, и редактироваться обычными SQL запросами.
    Ответ написан
    Комментировать
  • Не поддерживает русский язык, формат xls?

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

    Плюс формируете вы не xls, а HTML, замаскированный под xls.
    И из-за этой маскировки Excel не предлагает поменять кодировку при импорте.

    Поэтому есть два пути
    1. Сохранять данные как CSV. Тогда при импорте можно будет выбрать кодировку.
    2. Перекодировать данные перед сохранением,
    mb_convert_encoding($output, "windows-1251", "utf-8");
    Ответ написан
    Комментировать