Ответы пользователя по тегу PHP
  • Правильно ли реализован класс для работы с базой данных по принципу SOLID?

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

    Отсюда мы делаем логичный вывод, что соединение с БД никаким местом не должно создаваться в конструкторе менеджера крудов. А должно точно так же передаваться в него в качестве зависимости. Это может быть либо ванильная ПДО, либо инстанс реального MySQLDatabase (но поскольку мы пока не знаем, как он должен выглядеть, то лучше остановиться на PDO).

    Сам по себе DatabaseManager выглядит избыточным. Непонятно, зачем он нужен, если любой потребитель DatabaseManager-а может просто написать
    public function __construct(CRUDInterface $crud) {
    }

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

    В-четвёртых, хоть это и не относится напрямую к теме SOLID, но для меня является очень важным: собственно, реализация методов CRUD-а. Что в них передаётся? Откуда берутся названия таблиц, полей? Передаются в параметрах методов? Это прямая дорога к SQL инъекции, не говоря уже о нарушении инкапсуляции. Поэтому, отвечая на вопрос "Как вы реализуете работу с базой данных", лично я всё больше в последнее время от развесистых ORM-ов склоняюсь к простым TableGateway-ам. Да, кода писать больше, но он строже и понятнее. И не встаёт колом в нестандартных ситуациях. Тем более что приведённый пример кода как раз очень и похож на этот паттерн. То есть
    abstract class MysqlTableGateway implements CrudInterface
    {
        protected $db;
        protected $table;
        protected $fields;
        protected $primary = 'id';
    
        public function __construct(\PDO $db)
        {
            $this->db = $db;
        }
        public function read($id): ?array
        {
            $stmt = $this->db->prepare("SELECT * FROM `$this->table` WHERE `$this->primary`=?");
            $stmt->execute([$id]);
            return $stmt->fetch();
        }
         // ну и так далее
    }

    И дальше уже классы по работе с отдельными табличками наследовать от него,
    final class UserGateway extends MysqlTableGateway {
        protected $table = 'users';
        protected $fields = ['email', 'password','phone'];
    }

    Соответственно, если мы захотим перейти с мускуля на какой-нибудь редис с джейсоном внутре, то надо будет создать новый абстрактный класс с тем же интерфейсом, и от него отнаследовать реализации. Соответственно, в интерфейсе надо нормально прописать входные и выходные параметры:
    interface CRUDInterface {
        public function create(array $data):int;
        public function read(int $id):?array;
        public function update(array $data);
        public function delete(int $id);
    }

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

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

    $f = fopen('/var/www/site.ru/data/logs/site.ru.error.log','r');
    while (!feof($f))
    {
      $line = fgets($f);
      $st_strpos = "Nemesida";
      $pos = strpos($line, $st_strpos);
    
            if ($pos !== false) {		
        // тут как раз строка унас уже ЕСТЬ! в переменной $line
      }
    }
    Ответ написан
    Комментировать
  • Как исправить код работы с шаблонами?

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

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    самое тупое решение
    <script src="/path/file.js?t=<?= filemtime($_SERVER['DOCUMENT_ROOT'].'/path/file.js') ?>">
    Ответ написан
    9 комментариев
  • Что лучще использовать для api call JS или PHP?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    если для вызова не нужна никакая авторизация или токены, то разумеется на JS
    во всех остальных случаях разумеется на РНР
    Ответ написан
    Комментировать
  • Как продолжить цепь запросов в php?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Если отбросить всю не относящуюся к вопросу чепуху, типа каких-то "внутренних запросов"(?!), то ответ сводится цепочке вызовов.
    Ответ написан
    Комментировать
  • Почему выдает ошибку при загрузке фотографии?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    То, что папка images находится в папке с product.php, не имеет вообще никакого значения.
    product.php не имеет отношения к работе веб-сервера. из неё просто читается код класса.

    При этом путь images/ является относительным. То есть, в зависимости от точки входа может вести в совершенно разные места.
    Именно поэтому все пути всегда должны быть абсолютными. Пусть даже и с помощью костыля __DIR__
    Ответ написан
    2 комментария
  • Почему данные заказа не отправляются в базу данных?

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

    Пожалуйста, прочитайте ответы, которые вам давали на этот вопрос в прошлый раз и начните уже им следовать.
    Иначе программирование для вас так и останется игрой в прятки в темноте - "ой, что-то написала, не работает! Ой, что-то другое написала - работает!".

    К тому что написано в предыдущем ответе надо добавить только одно: перед
    new mysqli(...);
    надо всегда писать
    mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
    чтобы база данных сама сообщала, почему не может выполнить запрос. Если дело именно в нём.
    Ответ написан
  • Как вставить данные в запрос?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    public $query - это неправильно с десятка разных точек зрения, от архитектуры до простой житейской логики.
    Но при этом, если бы данные в запрос передавались правильно, а не как как обычно, то чисто технически это бы сработало

    В любом случае, public $query убрать, вместо этого добавить private $db, и в конструктор, соответственно, такой же параметр
    public function addProduct(){
        $query = 'INSERT INTO products (title, price, description, category, image, active) VALUES (?,?,?,?,?,?)';
        $stmt = $this->db->prepare($query);
        $stmt->bind_param("ssssss", $this->title, $this->price, $this->description, $this->category, $this->image, $this->$active);
        $stmt->execute();
    }
    Ответ написан
  • Как правильно отправить данные из формы POST-запросом и записать их в бд?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Главная беда задавателей вопросов на форуме в том, что они всегда задают совершенно не тот вопрос, который их интересует.
    Судя по коду, автор прекрасно знает, как "отправить данные из формы POST-запросом и записать их в бд". Спрашивается - зачем задавать этот вопрос? Загадка.

    Ведь настоящий вопрос звучит "Я написал код, который отправить данные из формы POST-запросом и записать их в бд, но он не работает. Как понять, по какой причине он не работает, чтобы я мог это исправить?" - ну разве не так?

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

    Для mysqli это делается командой
    mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

    Которая пишется перед коннектом.
    Для РНР всё стандартно - error_reporting всегда в E_ALL плюс display_errors в 1 на домашнем компьютере.

    И после этого РНР сам всё расскажет и покажет. И где опечатка в запросе, и любые другие ошибки.
    Ответ написан
    Комментировать
  • Почему не воводятся данные из БД?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Потому что никто никогда не читает документацию.
    В которой написано, например - какой массив возвращает fetchAll(). И можно ли из него получить что-то в виде $user_data['id'].
    А ещё в мануале написано, какая на самом деле функция подойдёт в данном случае.

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

    И я уже не говорю, что в мануале написано темно-красным по светло-красному, что MD5 нельзя использовать для хэширования паролей.
    Ответ написан
    6 комментариев
  • Resource: что из себя представляю потоки в php?

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

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

    Например, когда мы вызываем функцию fopen, то в результате мы получаем ресурс, который в себе содержит дескриптор файла, и далее мы уже можем работать с этим файлом не считывая его полностью в память.
    Как я понимаю, через потоки.. но что это за потоки ? где именно про них почитать ? это какие-то механизмы операционной системы ?

    Вот даже здесь, поскольку fopen работает поверх абстракции, нельзя говорить о едином подходе.
    Если у нас самый простой случай - локальный файл - то РНР оборачивает функции для работы с файлами языка С в свою собственную абстракцию. Но ниже - на уровне этих самых функций языка С - никаких потоков нет. Есть обращение к функциям операционной системы, которые просто позволяют прочитать выбранный кусок файла.
    Тут никаких вопросов нет, всё честно - если мы прочитали из файла 5 килобайт - значит потратили ровно 5 килобайт памяти.
    Но работаем мы с файлом не считывая его весь в память не потому что поток, а потому что так устроена файловая система. А поток этой возможностью только пользуется.

    Если же мы открываем не файл, а URL, то РНР начинает извращаться, пытаясь предоставить те же самые инструменты, какие мы используем для работы с файлами, для доступа к ресурсам совершенно другого типа.
    И здесь возможны нюансы. Я не знаю, как реализован HTTP wrapper, но в теории HTTP позволяет чтение произвольного объема данных через заголовок Range:. То есть РНР вполне может читать и из НТТР кусками, а не целиком.

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

    А упомянутый в вопросе PSR-7 вообще никакого отношения к потокам в РНР не имеет. Это совершенно отдельная реализация принципа потоков, которая не имеет отношения к потокам в РНР.
    Ответ написан
    Комментировать
  • Как сделать так, что бы при пустом запросе в базу выводились ВСЕ значения?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    есть немного замороченный способ, но в целом он требует куда меньше кода
    WHERE (account_id=? OR ? is null)  AND (priority=? OR  ? is null)

    Чтобы его использовать, надо всего лишь передавать каждую переменную в запрос по два раза (и следить, чтобы пустые переменные содержали null).

    Другим вариантом является условная сборка запроса.
    Для этого мы собираем условия и переменные для них в массивы

    $conditions = [];
    $parameters = [];
    if ($account_id)
    {
        $conditions[] = 'account_id=?';
        $parameters[] = $account_id;
    }
    if ($priority )
    {
        $conditions[] = 'priority =?';
        $parameters[] = $priority;
    }

    собираем из условий запрос и выполняем его, привязывая все переменные разом
    тут из сложного будет привязка переменных из массива, но благодаря оператору распаковки аргументов это не так уж и сложно.
    if ($parameters)
    {
        $sql .= " WHERE ".implode(" AND ", $conditions);    
        $stmt = $db->prepare($sql);
        $stmt->bind_param(str_repeat("s", count($parameters)), ...$parameters);
        $stmt->execute();
        $result = $stmt->get_result();
    } else {
        $restult = $db->query($sql);
    }
    Ответ написан
    3 комментария
  • Как вытащить данные из stmt?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    вместо store_result(); надо писать get_result();
    этот метод даст вам привычный ресурс, из которого уже можно получить данные обычным способом
    $stmt = $link->prepare("SELECT * FROM users WHERE id=?");
    $stmt->bind_param('i', $value);
    $stmt->execute();
    $result = $stmt->get_result();
    $row = $result->fetch_assoc();
    Ответ написан
    5 комментариев
  • Многоязычный сайт php twig - как лучше реализовать?

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

    То есть берется готовый или делается кастомный фильтр. Который и занимается поиском соответствия для переводимой фразы. Которая выводится, как {{ message|trans }}

    Вот пример реализации такого фильтра в Симфони

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

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Ту, в которой перечислить разрешённые символы.
    Чёрные списки никогда не работают. Работают только белые.
    Ответ написан
    2 комментария
  • Как вложить шаблон в шаблон (исправить мою ошибку проектирования в коде)?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Я же уже отвечал на этот вопрос. С готовым примером кода, где как раз и показано, как вкладывать шаблоны друг в друга.
    А вас настолько подкосила детская проблема с путями к файлам на кривом сайте, что сам ответ вы, похоже, даже не прочитали. Зачем было спрашивать?
    Ответ написан
  • Как получить значение из многомерного массива php?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Все верно, циклом

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

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    И долго этот позор будет здесь висеть?
    Для кого-то из модераторов это тоже очень сложный вопрос, требующий развёрнутого ответа?

    Или, может быть, рука промахнулась, хотели удалить вопрос, а удалился ответ?
    Можно ещё раз попытаться.
    Ответ написан
    1 комментарий
  • Как написать код в php чтобы он определял цвет светофора в конкретное время используя инпуты?

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

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

    Дерзайте.
    Ответ написан
    Комментировать