Задать вопрос
  • Как поменять значение во вложенном словаре по списку ключей?

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

    На самом деле выйдет, поскольку в Питоне новая переменная является ссылкой на старую.
    Перебираем все ключи кроме последнего и потом присваиваем ему новое значение
    def changeDictValueByPath(my_dict, path, value):
        tmp_dict = my_dict
        for k in path[:-1]:
            tmp_dict = tmp_dict[k]
        tmp_dict[path[-1]] = value
    
    indices = "first_sec_x"
    source = {"first": {"sec": {"x": 1}}}
    changeDictValueByPath(source, indices.split("_"), 2)
    print(source);
    Ответ написан
  • Почему выдаёт ошибку? Подключился через MAMP PRO к локальному хосту и попытался реализовать форму регистрации, но выдаёт ошибку, почему?

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

    Чтобы узнать, почему РНР код не работает, надо прочитать сообщение об ошибке РНР. В данном случае мы его не видим, а видим только сообщение веб-сервера о том, что РНР завершился с ошибкой.
    Чтобы увидеть ошибку РНР, надо сделать две вещи:
    1. Убедиться, что РНР генерирует сообщения об ошибках. Для этого во-первых, всегда должен стоять максимальный уровень генерации ошибок, error_reporting(E_ALL);, и во-вторых, программист не должен сам себе стрелять в ногу, насильно подавляя сообщения об ошибках. И никогда не использовать оператор подавления ошибок, @.
    2. На время разработки поставить режим вывода ошибок на экран, ini_set('display_errors', 1);. На боевом сервере, соответственно, этот режим должен быть выключен, а логирование наоборот - включено.

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

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

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

    Исходя из здравого смысла, в данном случае (когда из потенциально большого массива значений надо выбрать несколько случайных) чтобы избежать повторений, надо запоминать все уже использованные значения. Для этого подойдёт массив.
    А для проверки идеально подойдет цикл do while, это редкий случай его использования. просто генерируем новые ключи до тех пор, пока не найдётся ещё не использованный.
    do {
        $key = rand(2,$count);
    } while (array_key_exists($key,$exists));
    $exists[$key] = true;


    Важно не забыть определить пустой массив $exists перед началом цикла.

    Так же этот метод отлично подойдёт, если случайные значения не берутся из готового списка, а генерируются на лету.

    Если же количество требуемых случайных значений сравнимо с количеством всех данных, то удобнее будет сначала сгенерировать массив всех ключей и перемешать его
    $keys = range(2, $count);
    shuffle($keys);
    for ($a=0; $a < 10; $a++) {
        $key = $keys[$i];
    Ответ написан
    7 комментариев
  • Почему SQL запрос возвращает список?

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

    Потому что функция execute_read_query написана так, чтобы возвращать список.

    > Как быть?

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

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

    И ответ зависит от двух вводных, которые мы не знаем:
    1. по какой причине элемент 'test' может отсутствовать
    2. какие ещё значения могут быть у $arr['test']

    Если совсем всё равно на всё и пишем говнокод-стайл лишь бы покороче, то, как правильно написано в соседнем ответе - empty().

    Если элемент обязательно должен присутствовать, то либо убираем проверку на isset совсем, либо - если это интерактивное приложение - выносим отдельно, и при отсутствии элемента сообщаем об ошибке. Пусть даже и тупо 400 кодом.

    Если у $arr['test'] не могут быть другие значения, кроме true, то тоже проверить, и выбросить ошибку, если значение какое-то другое.
    Ответ написан
    Комментировать
  • Что именно делает этот код?

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

    По факту это такой магический геттер на стероидах.
    Отрезает от имени метода первые три буквы и ищет свойство с получившимся именем.
    Свойства добавляются в класс в конструкторе через массив.

    Если свойство не найдено, ищет в массиве имя класса, объект которого надо создать и вернуть.
    Для этого зачем-то меняет регистр с camelCase на snake_case и ищет элемент массива с таким именем по вышеприведённой карте классов. То есть getForwardFrom превращается в forward_from, и по этому индексу возвращается имя класса. почему нельзя было сразу написать в том же регистре - загадка

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

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

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

    Если под "сессией" имелось в виду общее хранилище для всех пользователей, то оно называется "база данных". И в этом смысле "БД не предлагать" выглядит беспочвенным капризом. Как правильно заметил N в комментарии, такое заявление звучит как "Как кушать суп? Ложку не предлагать."
    Такие заявления надо всегда подробно обосновывать. И в этом случае вам либо действительно подскажут, как обойтись без БД, либо объяснят, что это просто блажь.

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

    ipatiev
    @ipatiev
    Потомок старинного рода Ипатьевых-Колотитьевых
    Нет, нельзя.
    Вопрос не имеет смысла.
    SQL - это язык запросов к базе данных. База данных находится на сервере базы данных.
    РНР - это язык для формирования динамических ответов на НТТР сервере. РНР может обращаться к серверу базы данных, используя язык SQL.
    РНР не "обрабатывает данные аякса". РНР обрабатывает НТТР запросы. Любые, сделанные как напрямую браузером, так и через AJAX. И особой разницы между ними не видит.
    AJAX - это технология выполнения запросов к НТТР серверу из программы на Яваскрипте, исполняющейся в браузере. Браузер может обратиться только к НТТР серверу, но не к серверу баз данных.
    Чтобы из Яваскрипта в браузере обратиться к базе данных, надо запросить НТТР сервер, который запустит программу на РНР (или другом серверном языке), которая обратится к серверу БД, получит результат, и вернёт его в ответ на НТТР запрос.
    Ответ написан
    Комментировать
  • Какую версию PHP учить и по какой книге?

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

    Если говорить об изучении с нуля, как можно понять из заголовка, то я рекомендую книгу Jon Duckett, PHP&HTML. Но её, вроде бы, не переводили на русский. Из доступного - Котеров, но она конечно уже подустарела. Хотя там хорошо даются такие основы, как НТТР.

    Но в вашем случае вопрос об изучении РНР явно не идёт. А скорее о повышении квалификации.
    Плюс вопрос "Какую версию учить" тоже не стоит - у седьмой версии поддержка кончается осенью. Но в целом различия между версиями в РНР не настолько принципиальны, чтобы можно было учить какую-то конкретную "версию". Чисто синтаксические нововведения в языке лучше всего изучать по разделам "Новая функциональность" каждой версии отсюда https://www.php.net/manual/ru/appendices.php

    Но начиная с определенного уровня изучение РНР уже перестаёт быть изучением РНР, а скорее программирования в целом - ООП, паттернов проектирования и т.д. Тут уже книжки по РНР не нужны, сами пхпшники учат это по явовским учебникам - Фаулера, Мартина и пр.
    Хотя конечно к зарабатыванию денех на PHP + Битрикс это всё не имеет уже ни малейшего отношения.
    Ответ написан
    Комментировать
  • Как обратиться к элементу масива php?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Я бы сначала трансформировал массив в словарь
    $rates = [];
    foreach ($array["exchangeRate"] as $row) {
        $rates[$row['currency']] = $row;
    }

    и потом просто обращался по индексу,
    $currency = "BYN";
    echo $rates[$currency]["saleRateNB"];
    Ответ написан
    Комментировать
  • Как mysql/mariadb сортирует одинаковые значения?

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

    Если сразу после добавления, то может в порядке вставки, но это не точно, поскольку запись может идти не линейно, а на место записей, помеченных, как удалённые. Плюс после удалений/обновлений физический порядок записей в файле тоже может меняться, и соответственно, порядок выдачи.
    В целом ответ - никак не сортирует, берет первую попавшуюся.
    Ответ написан
    Комментировать
  • Почему один MySQL-сервер выбрасывает "Field 'some_field' doesn't have a default value", а другой просто инсертит пустое значение в 'some_field'?

    ipatiev
    @ipatiev
    Потомок старинного рода Ипатьевых-Колотитьевых
    Объяснение очень легко гуглится по тексту ошибки.
    За это поведение отвечает Mysql strict mode
    На одном сервере этот режим включён, на втором - выключен.
    Ответ написан
    1 комментарий
  • Оптимизация mysql, слишком большие таблицы?

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

    А в целом, я думаю, тут решение одно - нанять грамотного сисадмина/DBA
    Чтобы лечил причину, а не следствие (то есть разбирался - почему "крашнулась" таблица, и делал так, чтобы это не повторялось)
    Чтобы не писал ерунды типа "место кончилось" (как будто под линуксом проблема перекинуть файлы сразу на другую машину)
    А если уж и писал в Спортлото, то делал это чётко и подробно, с максимумом технических подробностей - какие физические характеристики серверов, какая операционка, какая БД, все версии с точностью до последней буквы, какой тип таблиц, с какой ошибкой "крашнулось", какую "ошибку" пишет при переносе.
    Ответ написан
  • Как реализовать добавление записи в связанные таблицы MySQL?

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

    function textlog($connect, $chat_id, $text, $user_id) {
        if (!$chat_id) {
            return false;
        }
        $sql = "INSERT INTO textlog (chat_id, phone, user_id) VALUES (?,?,?)";
        $stmt = $connect->prepare($sql);
        $stmt->bind_param("sss",$chat_id, $text, $user_id);
        $stmt->execute();
        return true;
    }
    Ответ написан
    1 комментарий
  • Получить значение ключа из многомерного массива?

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

    Дальше читаем:
    Управляющая конструкция foreach существует специально для массивов. Она предоставляет возможность легко пройтись по массиву.

    Переходим по ссылке и снова читаем:
    foreach (iterable_expression as $value)
        statement
    foreach (iterable_expression as $key => $value)
        statement
    Первый цикл перебирает массив, задаваемый с помощью iterable_expression. На каждой итерации значение текущего элемента присваивается переменной $value.

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

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

    spoiler
    Сам по себе ход мысли в вопросе очень характерный.
    Звучит примерно так: "Купил машину, что-то плохо тянуть стала. Думаю докупить упряжку лошадей, чтобы запрягать спереди. Лошадиные силы ведь прибавятся! Посматриваю ещё на воздушных змеев, лыжи, и дополнительный омыватель". То есть вместо простых и очевидных действий по диагностике, формулированию конкретных проблем, и последующему ремонту машины мы фантазируем себе набор каких-то бессмысленных и хаотичных телодвижений. Которые мало того что вообще никак не помогут, но скорее всего ухудшат ситуацию.

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

    В итоге из всех невнятных жалоб становится понятно, что с самой БД, судя по всему, проблем нет. А есть только один участок, к которому есть вопросы - поиск. Есть идея реализовать его через Эластик, но есть сомнения. При том что Озон там, МВидео и прочих мастодонтов Эластик устраивает, а вот нашему магазинчику с 300К записей он не угодил. Сразу вспоминается анекдот про нового русского и 600-й мерс с засорившейся пепельницей. Не тянет Эластик? Будем менять на Монгу!

    Я думаю, что в таких ситуациях в первую очередь надо установить в систему здравый смысл. Перестать метаться с безумными фантазиями, а подойти к вопросу логически: есть вопросы к поиску? Значит надо поставить поисковый движок. поисковый движок - это в 99% случаев - Эластик. К нему есть вопросы? Отлично. Максимально подробно формулируем эти вопросы - не забывая привести индексы, конфиги, запросы - и задаём конкретный вопрос по оптимизации работы Эластика.

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


    P.S. Не удивлюсь, если в итоге выяснится, что вся проблема сводится к истории, которая случилась в одном маленьком интернет-магазинчике: там тоже купили аж 3 сервера по 256Г мозгов в каждом, мастер-слейв, все дела... И не поменяли дефолтное значение innodb_buffer_pool_size в 128М. И что характерно, этот "кластер" даже тащил какое-то время, пока не случилась 10х нагрузка.
    Ответ написан
    2 комментария
  • Как заставить PHP обрабатывать строку, игнорируя управляющие последовательности?

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

    Вопрос действительно "тупой". И причём путаница тут уже в самом начале.
    Утверждение
    Если я не создаю строковую переменную как литерал в коде, а беру из файла или GET-запроса и т. д., то функциями в PHP она обрабатывается как строка в двойных кавычках.

    является неверным.

    Надо всегда проверять свои предположения на практике.
    То есть на самом деле вся проблема сводится к тому, что ещё до попадания в переменную $_GET['text'], текст был сконвертирован. В случае же, если в адресной строке будет передано значение \xbc, то оно и будет в переменной и прекрасно запишется в базу

    Есть вероятность, что в переменную приезжает один символ, но в кодировке utf-8. И strlen честно показывает длину в несколько байт, что добавляет путаницы ещё больше.
    Ответ написан
    Комментировать
  • Правильно ли реализован класс для работы с базой данных по принципу 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 комментария