Задать вопрос
  • Почему не получается обратиться к наследуемому свойству (php) через асинхронный запрос?

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

    Это если исходить из предоставленных данных. Вполне вероятно, что автор запутался где-то ещё. Но тут уже ему придётся разбираться самостоятельно. Но опять же, рассуждая логически, а не "Почему не получается обратиться к свойству php через асинхронный запрос?".
    Ответ написан
    2 комментария
  • Как добавить данные в бд mssql 20 с помощью php sqlsrv?

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

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

    1. Убедиться, что в РНР включён вывод ошибок. Проверить можно тупо допустив ошибку синтаксиса. Если она выведется - очень хорошо, значит вы видите ошибки. Если нет, то включить, хотя бы через ini_set.
    1.2. Для древних, как гуано мамонта расширений, таких как sqsrv, надо проверять ошибки запросов вручную. Но делать это надо с умом, а не как в соседнем ответе. Ошибку SQL надо не тупо вываливать на экран вручную, а превратить в ошибку РНР, чтобы она выводилась так же и туда же, куда и остальные ошибки. Это можно сделать простым кодом вида
    if( $stmt === false ) {
        throw new Exception(print_r( sqlsrv_errors(), true));
    }

    2. Упростить свой код. Всегда проверять что-то одно. Вы что хотите проверить? Выполнение запроса на вставку? А зачем вам тогда форма вообще? Сделайте скрипт, который при обращении к нему просто записывает данные в БД, без всяких форм и прочего.
    Работает? Очень хорошо, переходите к формам. И если что-то не будет работать, то и вопрос будет совсем другой, "у меня не работает форма", который к "как сделать запрос" уже совсем никакого отношения не имеет.
    Не работает? Разбирайтесь с запросом, без всяких форм. Если вы проверяете что-то одно, то у вас и не работает что-то одно. А не десять разных потенциальных точек отказа.
    3. Контролировать ход выполнения своего кода.
    Например, вот вы разобрались с запросом, и перешли к обработке формы. И вот у вас есть условие, if (isset($_POST['signInButton'])). А вы проверили, оно хоть выполнилось? Если нет, то в базу никогда ничего не добавится, хоть обперегружайся. Поэтому добавляете временный вывод отладочной информации:
    echo "проверяем нажатие кнопки\n";
    if (isset($_POST['signInButton'])) {
        echo "проверка прошла успешно\n";
        createAccount($conn, $email, $password, $keepSignIn);
    }

    Если "проверка прошла успешно" не вывелось - значит никакого $_POST['signInButton']) в вашем скрипте нет, и надо разбираться - почему. Например, в форме не указан метод, или кнопка называется совсем по-другому. Чтобы разобраться с этим, надо
    3.1 Выводить содержимое используемых переменных. Делаете
    var_dump($_POST);
    после отправки формы, и смотрите. Если там совсем пусто, значит никакую форму методом POST вы не отправляли. Если там что-то есть - то смотрите, что именно, и насколько это соответствует вашим ожиданиям.

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

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

    Сначала общая информация:

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


    Самым простым вариантом будет заключить запросы между вызовами beginTransaction() и commit(), как показано например в документации к последнему.
    $db->beginTransaction();
    $db->prepare("UPDATE `tab1` SET `col` = ?")->execute($data1);
    $db->prepare("UPDATE `tab2` SET  `col` = ?")->execute($data2);
    $db->prepare("UPDATE `tab3` SET  `col` = ?")->execute($data3);
    $db->commit();

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

    Соответственно, при ошибке в любом из запросов транзакция автоматически откатится. А при успешном выполнении всех запросов транзакция, соответственно, закоммитится.

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

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


    Также желательно помнить, что в любом более-менее сложном коде очень быстро появляются вложенные транзакции, а PDO при попытке стартовать транзакцию при уже открытой, выбросит исключение, что, соответственно, приведёт к откату родительской (и это гораздо лучше поведения MySQL по умолчанию, которая автоматически коммитит старую). И имеет смысл накидать простой кодик, который считает вложенные транзакции, и не стартует, если уже был старт, а при коммите вычитает вложенность, а реально коммитит только если вложенности не осталось. Что-то вроде кода из комментариев к beginTransaction(), подравняв его напильником
    class \MyPDO extends \PDO
    {
        protected $transactionCounter = 0;
    
        public function beginTransaction()
        {
            if($this->transactionCounter++ === 0) {
                return parent::beginTransaction();
            }
        }
        public function commit()
        {
            $this->transactionCounter--;
            if($this->transactionCounter === 0) {
                return parent::commit();
            }
        }
        public function rollback()
        {
            $this->transactionCounter = 0;
            return parent::rollback();
        }
    }

    разместив его либо прямо в PDO, либо в своем враппере.
    Ответ написан
    3 комментария
  • Разбивка на страницы?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Чтобы ограничить количество выводимых страниц требуется знание арифметики в пределах начальной школы.
    Чтобы вместо 1 и $num_pages использовать заранее рассчитанные значения, укладывающиеся в определённый интервал. Например 10 страниц.
    То есть надо всего лишь определить начальную и конечную цифры, которые подставлять в for вместо 1 и $num_pages.
    Например, если мы хотим выводить только 10 страниц, а текущую выводить посередине то для получения первой цифры надо отнять от текущей 4. Затем к ней надо прибавить 9 - так мы получим второе число.
    При этом, разумеется, надо проверить, чтобы первое не было меньше 1, а последнее - больше $num_pages.
    А дальше просто подставить их в for. Вот и всё.
    Ответ написан
    2 комментария
  • Как сделать полное кэширование базы данных?

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

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

    Во-первых, увеличить innodb_buffer_pool_size до 80% от доступной памяти на сервере. Mysql совсем не дура, и прекрасно сама подтянет используемые таблицы в оперативку. И вселенская проблема нашего фантазёра будет решена.
    Отдельно здесь следует упомянуть случай, когда этому буферу не просто задан недостаточный размер, а жалкие 128 мегабайт, поскольку никто не догадался поменять значение, стоявшее в конфиге по умолчанию.

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

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Это один из тех вопросов новичков, на который нельзя давать прямой ответ. Он превратится в пустое умствование и говнокод. Соглашусь с комментарием Дмитрий: тут просто не нужен массив. И тем более не нужно городить огород из абстракций. Тут явно нужен банальный VO/DTO, а автору надо перестать пытаться заворачивать привычные массивы в солидно выглядящие объекты, и начать использовать сами объекты.

    Если нам нужна конкретная структура, то и описываем её в конкретном классе, безо всяких интерфейсов:

    final readonly class Parameters
    {
        public function __construct(
            public string $key1,
            public int $key2,
            public DateTimeImmutable $key3,
        ) {}
    }
    public function execute(Parameters $parameters):


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

    При этом если внутри execute() вдруг зачем-то понадобится обратиться к свойствам именно как к массиву, то использовать волшебную функцию get_object_vars().

    Если же речь про валидацию входящих данных, то это вообще совсем другой вопрос.

    Для этого есть либо стандартные валидаторы, когда на вход подаётся массив и набор правил, вот например как в ларавле, симфони или в десятке отдельно стоящих библиотек, а на выходе - или гарантированно валидная структура данных, или ошибка.

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

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

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

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

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

    Плюс никаких исключений. Странно, что я не обратил на это внимание в прошлый раз, там этот говнокод с try catch просто везде. Самому не надоедает всё время писать эти тру, кетч, ретурн для каждого запроса? И с какой стати пользователь API должен читать, что в БД нету например нужной таблицы? Это ВНУТРЕННЯЯ информация, которая не должна утекать наружу

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

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

    Затруднит. Причём не "в дальнейшем", а вот уже прямо сейчас.
    С нормальной таблицей получить данные пользователя и его баланс можно одним простым запросом. А с зоопарком из таблиц надо что-то колупать с именами таблиц.
    С нормальной таблицей получить аналитику по финансам в целом можно одним простым запросом. А с зоопарком из таблиц каждый раз придётся собирать запрос вручную в цикле и уродовать свой жесткий диск поскольку сразу БД не сможет получить все данные, а её придётся сначала слить все данные всё равно в одну таблицу, а потом уже по ней искать.
    Ответ написан
    Комментировать
  • Есть ли реальные проблемы с безопасностью windows 7 для домашнего использования?

    ipatiev
    @ipatiev
    Потомок старинного рода Ипатьевых-Колотитьевых
    Я думаю, можно подытожить все данные здесь ответы:
    На данный момент о конкретных проблемах с безопасностью для Windows 7 сообществу ничего неизвестно.
    Ответ написан
    Комментировать
  • Выбор между SQL и NoSQL документооринтированной базой данных?

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

    Первое, что надо понять - это что в "веб приложении", да ещё и с "архитектурой", должна быть база данных. Без неё просто не обойтись. А из всего перечисленного базой данных является только постргес.
    (те, кто почему-то до сих пор не избавился от иллюзий, или просто стал жертвой незамысловатой рекламы, могут почитать, почему MongoDB базой данных не является).

    Второе, что надо понимать - это что в нагруженных приложениях база данных никогда не используется одна сама по себе. Для выполнения различных дополнительных задач используются специализированные движки. Например
    • кликхаус для аналитики
    • редис для кэширования
    • эластик для поиска
    • сентри для логов
    • и так далее - вариантов масса

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

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

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

    ipatiev
    @ipatiev
    Потомок старинного рода Ипатьевых-Колотитьевых
    Лучше всего вообще ничего не обновлять.
    При наличии очевидного индекса на article_id, хоть какие-то проблемы подсчёт на лету начнёт создавать на объёмах уровня фейсбука.
    Так что я бы сначала не со счётными палочками колупался, а проверил наличие индекса.
    Ответ написан
    Комментировать
  • Какая конкретная модель адаптера Wi-Fi 5GHz заведётся под Ubuntu (24, если это важно)?

    ipatiev
    @ipatiev Автор вопроса
    Потомок старинного рода Ипатьевых-Колотитьевых
    В итоге догадался посмотреть отзывы в интернет-магазине, и взял ASUS PCE-AX58BT
    Ответ написан
    Комментировать
  • Авторизация PHP. Как исправить Warning: Undefined array key "login" in?

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

    Чтобы исправить первый вариант, и вообще для порядка, надо добавить проверку на метод POST.
    Чтобы получить помощь для второго варианта, надо догадаться добавить в свой вопрос код формы.

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

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

    Лучше потратьтесь на книжку, Джон Дакетт, PHP&MySQL. Там очень толково, на примерах даётся не только язык, но и все важные аспекты программирования - обработка ошибок, отладка, рефакторинг, структура приложения, SQL, обработка изображений, и куча всего остального. В последних главах весь материал даётся на примере создания простой но рабочей социальной сети.
    Ответ написан
    1 комментарий
  • Как правильно настроить права для веб-сервере apache2+php-fpm?

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

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

    Лично я делаю так: веб-сервер работает под своими группой/пользователем, а пхп - под SSH логином пользователя сайта. У вас это может быть видимо этот девопс.
    Чтобы их поженить, просто добавляем пользователя веб-сервера в группу пользователя, usermod -a -G devops www-data, а папочке веб-сервера соответственно выставляются права 750. Веб-серверу нужно только чтение, и он его имеет через группу. Таким образом можно соорудить такой мини-хостинг, разные ползователи не могут ходить в папки друг друга, и при этом нет обычной чехарды, когда пользователь ssh/ftp и пользователь пхп-фпм разные.
    Ответ написан
    2 комментария
  • Почему этот запрос выводит из строя mysql?

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

    Но иногда даже если все нужные индексы есть, база тупит неимоверно. В этом случае надо сразу смотреть значение innodb_buffer_pool_size. По умолчанию значение этого параметра всего 128 мегабайт. А это, грубо говоря, максимальное значение памяти, доступное MySQL для работы.

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

    Разумеется, у mysql есть просто миллион разных настроек, которые тоже влияют на производительность. У Перконы, кажется, даже есть калькулятор, в который ты заливаешь информацию о своей базе, а он тебе выдаёт значения параметров конфигурации. Но это уже такая тонкая настройка, третий уровень. Ничего из этого не будет работать, если innodb_buffer_pool_size недостаточного размера. И его размер рекомендуется выставлять на максимально допустимое значение. Если это выделенный mysql сервер, то 80-90% от физической памяти. Если не выделенный - то столько, сколько не жалко, чтобы не мешать остальным приложениям.
    Ответ написан
    1 комментарий
  • Как выбрать максимальное значение во вложенных массивах?

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

    Перебрать массив и получить максимальное значение - это три строчки. Куда проще-то?

    Есть какая-то функция или сочетание функций

    Есть поговорка. "Дурная голова ногам покою не даёт". И ещё есть принцип, KISS, который переводится как "не надо пытаться выглядеть умнее, чем ты есть, дурачок - опозоришься".

    Самый простой и красивый код - это тот который ты написал сам и понимаешь, как он работает. И сможешь в случае чего исправить. А не побежишь снова на Хабр, "мне тут пацаны дали красивое, а можно чтобы оно ещё и работало?"
    Ответ написан
    2 комментария