Профиль пользователя заблокирован сроком с 10 апреля 2022 г. и навсегда по причине: систематические нарушения правил сервиса
Ответы пользователя по тегу PHP
  • Как подставить числа в строку и сделать вычислительное действие?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Никак не подставить.
    Это так не работает.
    Надо распарсить строку, получить дерево операций, и выполнить их последовательно.
    Готовых библиотек в сети - как грязи, вот первая же из гугля https://github.com/mossadal/math-parser
    Ответ написан
    1 комментарий
  • Не получается внести данные с формы в бд, хелпанете?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Ответ написан
    Комментировать
  • Почему не видно тегов tr и td в массиве?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Браузер. Ничего. Не удаляет.
    Всё. Прекрасно. Передаётся. И это вам уже раз 10 показали.

    Это конечно какой-то позор, а не вопрос.
    Человек вроде бы занимается чем-то связанным с программированием, но логическое мышление у него отсутствует полностью.
    Ладно, понадобилось задать два вопроса чтобы узнать что такое браузер и HTML. И что, оказывается, браузер не показывает теги как есть!
    Но даже когда узнал, застопорился на неразрешимом противоречии. Как в поле чудес, угадал все буквы, не угапдал слово: " браузер удаляет эти теги из-за отсутствия тега table."
    Казалось бы, всё очевидно: если будет тег table, то всё и отобразится как надо. А если нет тега - то и tr c td будут не нужны.
    Но всё ещё ищет какое-то особое "экранирование".
    "Где логика, где разум?" (с)
    Ответ написан
    Комментировать
  • Как добавить id в titile при выводе DOM через php?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    третьим буду
    echo "<option title='$row[id]'>";

    но вообще, конечно, должно быть

    <option title="<?= htmlspecialchars($row['id']) ?>">


    а не этот ужас с загогулинами
    Ответ написан
    Комментировать
  • Как спроектировать страницу авторизации с точки зрения паттерна MVC на PHP?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Или же я вообще пишу глупость

    В целом да. Но

    вся эта логика прекрасно будет в index.php лежать?

    - это гораздо большая глупость.
    Ну то есть лежать-то будет, но к MVC уже никакого отношения не будет иметь.

    По пунктам

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

    Что такое AuthPage вообще непонятно. Модель, контроллер? По базе проверяет модель, куки пишет контроллер. А здесь какой-то кадавр.

    Перед тем как писать авторизацию "в стиле MVC", надо сначала разобраться, что такое модель, что такое контроллер, и что такое вью.

    Модель - это вся логика приложения.
    Контроллер - это интерфейс для общения модели с браузером. Делает всё, связанное с обработкой НТТР запросов.
    Вью - отображение.

    Как правильно.

    Соответственно в модели должен быть класс User с методом auth(), который принимает логин и пароль и возвращает инстанс класса Юзер.
    В конторе делается экшен: отдельный метод, который
    - проверяет, если был запрос методом ПОСТ, то берет из него логин и пароль,
    - валидирует их, если валидация не прошла, то создает ошибку, которую надо показать юзеру
    - если прошла, то вызывает метод auth() модели User, передавая в него логин и пароль
    - если совпали, то пишет в сессию ид юзера, и делает редирект куда-нибудь
    - если не совпали, то создает ошибку, которую надо показать юзеру
    - вызывает вью с формой для логина и пароля

    Для регистрации делается еще один экшен, который
    - проверяет, если был запрос методом ПОСТ, то берет из него данные для регистрации,
    - валидирует их, если валидация не прошла, то создает ошибку, которую надо показать юзеру
    - если прошла, то то заполняет класс User данными и выполняет метод save() и делает редирект куда-нибудь
    - вызывает вью с формой для регистрации

    Для личного кабинета делается третий экшен, который берет из сессии ид юзера, обращается к методу read() модели User и через View показывает личный кабинет

    Варианты реализации

    Самый простой вариант реализации контроллера - это папочка с отдельными файлами-экшенами. Ничего плохого в такой архитектуре нет, этот этап надо пройти, если раньше так не делали.

    То есть папка user в которой есть, скажем, файл index.php который является экшеном личного кабинета.
    Он проверяет юзера в сессии, и если нету, то перекидывает на auth.php
    в auth.php есть форма и ссылочка на register.php
    Все три файла инклюдят в себя файл user.php из папки model, в котором есть функции auth(), register() и profile()

    Но в более классическом варианта к трем буквам MVC добавляется ещё одна - R, роутер. Специальный сервис, который разбирает адресную строку, и видя, например, что к сайту обратились по адресу /user/register, создаёт экземпляр класса UserController и вызывает его метод register()
    Ответ написан
    4 комментария
  • Как вывести строку по id php ?id=1?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Вопрос хороший, но здесь мы упираемся в главную беду пхп - ВСЕ учебники в интернете, и особенно видео - показывают как писать адов говнокод из прошлого века.

    1. Получение значения из адресной строки

    Чтобы получить значение переменной, которую передали в строке запроса (это то что после знака вопроса), надо обратиться к переменной $_GET

    То есть в данном случае можно написать $id = $_GET['id']. Имя получаемой переменной ($id) может быть любым и не обязательно совпадать с переданным значением. А вот в индекс массива $_GET разумеется надо писать именно то имя, которое в адресной строке. То есть если site.com/?id=1 то в $_GET['id'] будет значение 1.

    2. Валидация данных

    Далее очень желательно проверить, что мы в переменной получили то что хотели, а так же что мы вообще хоть что-то получили.

    Сначала надо проверить наличие в массиве $_GET нужного ключа. В данном случае это можно сделать с помощью оператора isset() (хотя вообще она для проверки наличия ключей в массивах не рекомендуется)
    Если для показа информации используем отдельную, специальную страницу, которая только занимается показом данных по айди, то после проверки на существование надо выдать ошибку.

    Затем, поскольку id может быть только целым числом больше нуля, то лучше проверить и это тоже и тоже выдать ошибку.

    3. Соединение с БД.

    в "config.php" должно быть написано не то что там сейчас а вот это (со своими параметрами подключения разумеется)
    mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
    $link = mysqli_connect($host, $user, $pass, $db_name);
    $link->set_charset("utf8mb4");


    4. SQL

    Запрос, который нам нужен, выглядит так:
    SELECT * FROM `product` WHERE id=1

    5. Выполнение запроса в РНР

    Но разумеется вместо 1 надо подставить значение переменной.
    Это самое сложное. Но надо один раз выучить и потом везде применять

    Важно, чтобы данные в БД всегда попадали отдельно от самого запроса. Это непреложное правило, которое надо соблюдать всегда.
    Для этого надо
    1. Заменить все переменные в запросе на специальные маркеры, которые называются плейсхолдеры или параметры, а по сути - просто знаки вопроса
    2. Подготовить запрос к исполнению с помощью функции prepare(). Эта функция принимает строку запроса и возвращает экземпляр специального класса stmt, с которым в дальнейшем и производятся все манипуляции
    3. Привязать переменные к запросу.
    4. Выполнить подготовленный ранее запрос с помощью с помощью execute()
    5. Получить результат запроса через get_result()
    6. и дальше конкретную строку из БД с помощью уже знакомой fetch_assoc


    В коде это будет так
    $sql = "SELECT * FROM `product` WHERE id=?";
    $stmt = $link->prepare($sql);
    $stmt->bind_param("s", $id);
    $stmt->execute();
    $result = $stmt->get_result();
    $row = $result->fetch_assoc();


    bind_param() принимает в качестве параметров все переменные, которые должны попасть в запрос, в том же самом порядке, в котором стоят плейсхолдеры в запросе. Но кроме того, сначала в этой функции должны быть указаны типы для всех переменных, в виде строки, где тип переменной обозначается одной буквой. То есть букв в этой строке должно быть ровно столько, сколько дальше будет переменных. К счастью, можно особо не париться с типами и для всех переменных указывать тип "s".

    6. Вывод данных.

    Важно понимать, что в момент вывода данных никакой работы с БД уже быть не должно!
    Должны быть только переменные РНР с уже полученными данными.
    Ответ написан
    1 комментарий
  • Как взламывают бд крупных компаний?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Взламывают точно так же как и мелкие.
    В статье описана обычная SQL инъекция, когда данные подставляются прямо в SQL запрос.

    Я просто сомневаюсь, что valve может нанять таких людей в backend

    Не надо сомневаться.
    Нету никаких "особенных способов взлома".
    А есть такое явление как "эффективные менеджеры". У которых размер премии зависит от того, сколько денег они сэкономили акционерам.

    Если бы там были подготовленные выражения, то и взлома бы не было.
    Но вместо подготовленных выражений в больших компаниях есть эффективные менеджеры. Которые экономят на нормальных программистах, и заказывают разработку в Индии, в штате Бангалор. Где живет какой-нибудь Хамишь Кумар, который родился в касте вычерпывателей говна из сортиров. И вот у него только один шанс не черпать говно всю жизнь - всеми правдами и неправдами научиться программировать, путь даже за еду. Сравниваем с силиконовым программером с запросами $250k в месяц. Экономия налицо!

    На всякий случай напомню, для стопроцентной защиты от инъекций необходимо всегда следовать двум простым правилам:

    1. данные подставляем в запрос только через плейсхолдеры
    2. идентификаторы и ключевые слова подставляем только из белого списка, прописанного в нашем коде.


    Ключевое слово здесь - "всегда". Как только начинаются рассуждения вида "ну эти данные и так безопасные, их защищать не надо", то в этот момент мы добавляем инъекцию к себе на сайт. Следует понимать, что защищаем мы не данные, а запрос. Данные нас вообще не интересуют - какие они, откуда пришли, являются "безопасными" или нет. Важно не то откуда они пришли, а то, куда они идут. В SQL запрос? Используем подготовленные выражения, точка.
    Ответ написан
    1 комментарий
  • Конструкция if или switch?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Второй вариант это хорошая попытка, но недостаточно полно реализованная. Писать надо хотя бы так

    (function() { 
        foreach([true] as $condition) {
            $condition = ($condition) ? true : false; 
            while ($condition) {
                switch (true) {
                    case ($this->inComplete(($oldStatus), ($newStatus))):
                    case ($this->outComplete(($oldStatus), ($newStatus))):
                        ($this->action());
                        ($this->save());
                }
                break;
            }
        }
    })();

    В общем, перед тем как задавать вопрос на тостере, надо сесть и хорошенько сначала самому подумать, каких бессмысленных операторов можно добавить в код, кроме switch.
    Ответ написан
    3 комментария
  • Как получить подстроку из строки php?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    эта "строка" выдрана из исходного JSON файла тем же варварским способом, которым вы пытаетесь сейчас "распарсить" её.
    JSON надо парсить штатными средствами, а не ковырять палкой с гвоздем на конце.
    Ответ написан
    Комментировать
  • Когда полезно использовать генератор, если объем данных небольшой?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Эффективность здесь вообще не при чем.
    Генератор - это синтаксический сахар
    То есть вопрос вообще не про эффективность

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

    Но на самом деле бывают и другие критерии.
    У генератора есть одно однозначно полезное применение - возможность создания унифицированных интерфейсов.
    Любой потоковый источник он может превратить в перебираемый.
    То есть можно наплодить интерфейсов и скармливать их какому-нибудь форичу, и он будет с ними работать, не зная, поток внутри, или массив. Файл, результат запроса из БД, раскодированный джейсон - если в каждого из этих источников будет перебираемый интерфейс, то все их можно будет перебирать в одном и том же цикле.
    И вот ради такой унификации и используется генератор
    Ответ написан
    Комментировать
  • Как правильно написать метод модели с динамическими полями?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Вопрос очень хороший. И код в целом неплохой. А ошибки совершенно стандартные, их делают все.

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

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

    Соответственно, сначала надо сделать код универсальным, и вынести в абстрактный класс.
    Заодно исправив в нём одну критическую ошибку (с безопасностью) и одну логическую.
    Что будет, если значения двух последних элементов массива совпадут? Тогда уж надо ключи сравнивать, а не значения.
    И зачем вообще такие ухищрения с поиском последнего элемента, если у тебя есть переменная $fields? Если она пустая - значит это первый оборот цикла. Всё, этого достаточно

    По поводу безопасности я уже устал объяснять.
    Мы старательно защищаем значения, передаваемые в запрос, но при этом не моргнув глазом пропускаем инъекцию через имена полей.
    Список полей всегда надо писать явно. Тем более что это потом пригодится в миллионе случаев.

    abstract class AbstractTableGateway
    {
        protected $db;
        protected $table;
        protected $fields;
        protected $primary = 'id';
    
        public function __construct(DB $db)
        {
            $this->db = $db;
        }
        public function update(array $params): void
        {
            $this->validate($params);
    
            $set = "";
            foreach($params as $key => $value)
            {
                if ($key !== $this->primary)
                    $set .= ($set ? "," : "") . "`$key` = :$key";
                }
            }
            $sql = "UPDATE `$this->table` SET $set WHERE `$this->primary`=:$this->primary";
            $this->db->query($sql, $params);
        }
        protected function validate($data)
        {
            $diff = array_diff(array_keys($data), $this->fields);
            if ($diff) {
                throw new \InvalidArgumentException("Unknown field(s): ". implode(",",$diff));
            }
        }
    }


    Но вообще я не рекомендую использовать именованные плейсхолдеры для автоматических запросов. В имени поля могут встречаться символы (например пробелы), которые не годятся для имен плейсхолдеров

    После этого уже можно создавать класс юзер (и не повторять, как попугай, user user по 10 раз)

    class UserGateway extends AbstractTableGateway {
        protected $table = 'users';
        protected $fields = ['email', 'password', 'name', 'birthday'];
        protected $primary = 'user_id';
    }


    И спокойно к нему обращаться в коде

    $user = new UserGateway();
    $user->update($params);
    Ответ написан
  • Можно ли в PHP автоматически вызвать класс?

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

    (new class {
        public function log($msg)
        {
            echo $msg;
        }
    })->log("hello");
    Ответ написан
    7 комментариев
  • Как сформировать массива через Multiply select для отправки отдельных писем?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Учимся правильно работать с БД.

    1. выкинуть на помойку mysql_query, ни на одном нормальном хостинге этот код работать не будет
    2. переписать код так

    $in  = str_repeat('?,', count($users_group_list) - 1) . '?'; 
    $sql = "SELECT * FROM `users` WHERE email IN ($in)";
    $stmt  = $mysqli->prepare($sql);
    $stmt->bind_param(str_repeat('s', count($array)), ...$users_group_list);
    $Stmt->execute();
    $result = $stmt->get_result(); 
    $data = $result->fetch_all(MYSQLI_ASSOC);


    в массиве $data будет список юзеров
    Ответ написан
  • How best to scale a php application?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    You need to make your mind first, whether you want to "scale up" which means to split the processing between different servers, or you are not even sure yet, whether adding different instances is a good thing at all.

    Generally scaling up a web application indeed means providing dedicated hardware for the every major service involved. For your setup I'd make it four different servers
    - PHP/Nginx instance
    - database backend dedicated for Postgres
    - column database backend to host Clickhouse
    - processing backend to host rabbitmq and Golang microsevices

    The good thing, each of those can be easily scaled up in turn by simply adding more instances of the kind (I am not sure how it's done for Clickhouse tho).

    But I have a feeling that you are probably confusing the scaling up with just performance optimization. And you need to start from the latter. Simply checking the CPU load is tоo rough a measurement. You need a full qualified profiling to pinpoint certain bottlenecks.

    From such incomplete data I can only guess that Laravel configuration is not quite optimal. 75 rps shouldn't be a problem for such a hardware. Make sure you have all production environment settings configured properly - all caching is turned on, etc. Also, switching to 7.4 and preloading could also help.
    Ответ написан
    3 комментария
  • Почему перестал работать array_unique?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    А вся проблема в том, что ты не понимаешь, что делает "твой" код.
    И следовательно, не можешь его отлаживать.

    Это к вопросу о том, как легко и приятно быть программистом, когда код за тебя пишут другие.
    Ну вот оно тебе и прилетело в обраточку, это "приятно"

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

    Поэтому тебе надо сейчас либо собраться и решить задачу самостоятельно, продумав алгоритм и реализовав его простыми средствами - циклами и условными переходами. Без всяких красивых слов типа array_diff, array_filter и прочих. Потому что использовать синтаксический сахар можно только тогда, когда ты понимаешь, как он работает внутри. Если не понимаешь - то код надо писать руками, а не волшебными функциями.
    И после этого, понимая как работает твой код, ты сможешь заняться отладкой, запуская его, и выводя промежуточные результаты.
    Либо, если не выйдет, то продать компьютер и идти работать в макдональдс.

    А вот это вот как у тебя сейчас - "ой мне один добрый дядя написал кодик, но он ниработаит, давайте другой добрый дядя мне его починит" - это не будет работать. Два раза тебе помогут, но постоянно жить чужим трудом не получится. А "программист", который не умеет писать код сложнее уровня "вайл муэскуэль фетч эррей" никому не нужен.

    что такое array_filter? Это реализация примитивного цикла,
    foreach ($array as $key => $value) {
        if (!условие фильтра) {
            unset($array[$key]);
        }
    }

    что такое array_diff? Это реализация чуть более сложного алгоритма, когда мы перебираем первый массив, и смотрим, есть ли такой же элемент во втором. Если нет - то записываем это значение в третий.
    И так далее.
    Ответ написан
    1 комментарий
  • Почему получается ноль?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    А теперь правильный ответ, не в стиле детского сада, "ой у тебя тут куличик не из той формочки"

    error_reporting(E_ALL);

    Добавив эту волшебную строчку в свой РНР код, ты как по волшебству получишь от РНР ответ на свой вопрос.
    Ответ написан
    Комментировать
  • Реализую method chaining. Как сделать так, чтобы вызов метода влиял на общий результат?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Учись задавать вопросы. То что ты сейчас понаписал - это называется проблема XY, бич всех новичков. (ну и конечно бич всех помогаек на тостере, которым некогда думать, им надо на вопрос отвечать).

    Никогда не задавай вопрос, как починить тот кривой костыль, который ты себе по неграмотности придумал. Всегда задавай вопрос, как решить исходную задачу.

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

    class foo {
        protected $bar;
        public function bar(string $data ) {
            $this->bar = $data; 
        }
    
        private function baz() {
            if (isset($this->bar)) {
                // сделать что-то
            }
        }
    }
    Ответ написан
    1 комментарий
  • Как перейти по ссылке передавая запрос POST через PHP?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Никак

    РНР не ходит по ссылкам. По ним ходит браузер.
    Попробуй научиться отличать одно от другого.
    Ответ написан
    Комментировать
  • Как правильно писать catch в php?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Для того чтобы правильно писать catch, надо писать осмысленный код.

    А здесь мы наблюдаем очаровательный пример карго-культа.
    Узнав, что "толстый контроллер - это плохо, а сервисы - это хорошо", автор небрежным движением руки замел весь мусор под ковёр перенёс весь код из контроллера в "сервис". Ну а что? Контроллер худой, весь код в сервисе!
    Тот факт, что сама кривая структура проекта осталась, по сути, той же самой, нас не смущает.
    Как и то, что сервис вдруг начал выполнять функции НТТР контроллера и кидать почему-то НТТР исключения. Что с этими исключениями делать в случае, если тот же сервис будет вызван из консольной команды - загадка.

    Но самое забавное, что при всём при этом контроллер всё равно пытается выполнять работу модели. Казалось бы, какое отношение интерфейс для обслуживания НТТР запросов имеет к транзакциям в базе данных? А вот поди ж ты!

    Чтобы сделать этот код осмысленным, контроллеру всё-таки придется потрудиться, и выполнить какую-то работу самому, а не перекладывать на "сервис". А так же отдать модели то что ей принадлежит.

    В общем транзакцию перекинуть в createDefault. причём не напрямую, а ещё ниже - в слой для работы с БД. Стартовать транзакцию до валидации данных - это как бы *не совсем логично*, мягко говоря. И в итоге, как по волшебству, весь этот говнокод исчезнет как страшный сон.
    При этом в параметрах передавать не НТТР реквест чохом, а осмысленный набор параметров, вынутый предварительно из реквеста!
    В частности, если модель сама проверяет права доступа, то и передавать ид пользователя из авторизации.

    При этом модель не должна кидать НТТР исключения. Она должна кидать исключения бизнес-логики. Которые контроллер уже может ловить и транслировать в хттп. Но тут видимо уже сложнее, поскольку это ж ларавель судя по всему.

    В любом случае, уж catch (Exception $e)-у тут точно не место

    Но это если рассматривать твой конкретный случай.
    В общем же случае правильный код написал Илья.

    То есть внутри трая операции с БД и коммит.
    в кетче роллбэк и перевыброс исключения. Только ловить надо Throwable

    try {
      DB::beginTransaction();
      // запись в БД
      // запись в БД
      // запись в БД
      DB::commit();
    } catch (\Throwable $e) {
      DB::rollBack();
      throw $e;
    }
    Ответ написан
    2 комментария