Ответы пользователя по тегу PHP
  • Как взять значение из объекта PHP?

    Lobotomist
    @Lobotomist
    Software Developer
    Лично меня от ответа останавливает обилие кода, который нельзя воспроизвести без вашей БД и входных данных.

    Советую вам вычленить проблемный участок кода и оставить в вопросе только его, причем так, чтобы можно было его запустить и воочию наблюдать смущающий вас результат.
    Ответ написан
  • Почему выскакивает ошибка заголовков при формировании csv файла?

    Lobotomist
    @Lobotomist
    Software Developer
    Приглядитесь к ошибке:
    yii\web\HeadersAlreadySentException:
    Headers already sent in /usr/share/nginx/html/project.eng/modules/admin/controllers/ToursController.php on line 1557.
    in /usr/share/nginx/html/project.eng/vendor/yiisoft/yii2/web/Response.php:366


    О чем она говорит? Вы уже отправили заголовки в своем скрипте (ToursController.php on line 1557). А потом в Response.php(339) yii2 пытается снова их отправить и возникает исключение, так как они уже были отправлены.

    Наиболее адекватное решение проблемы - не формировать ответ пользователю руками, а использовать предназначенный для этого класс Response. Как это делать можно прочитать в гайде. Полагаю, вам наиболее уместно отправлять поток `php://output` с помощью sendStreamAsFile.
    Ответ написан
  • Утечка памяти phpQuery. Как исправить?

    Lobotomist
    @Lobotomist
    Software Developer
    Насколько я понимаю, это известная проблема данного парсера: https://github.com/pein0119/phpquery/issues/192

    Так что если хочется использовать именно его нужно
    * либо найти утечку в самом парсере (видимо, метод phpQuery::unloadDocuments не все указатели удаляет, где-то они остаются) и исправить;
    * либо время от времени перезапускать скрипт;
    * либо использовать другую библиотеку, например nokogiri, как вам посоветовал Immortal_pony;

    Причина утечки, я предполагаю, в том, что phpQuery::$documents свойство публичное и когда элементы массива удаляются внутри unloadDocuments на эти объекты все еще ссылаются какие-то из ваших переменных. Ради эксперимента вы можете сделать unset всех переменных (не только doc) и потом unloadDocuments - просто чтобы проверить мое предположение.
    Ответ написан
  • Почему не работает sscanf?

    Lobotomist
    @Lobotomist
    Software Developer
    Проходите тест кое-где? ;)
    Дело в том, что все это является строкой и соответствует первому спецификатору %s. Насколько мне известно, эти спецификаторы все жадные и нет опции сделать их не жадными. Для того, чтобы дефис не попадал, можно вместо %s использовать, например %[^\-].
    Ответ написан
  • Почему проверка регулярного выражения возвращает false?

    Lobotomist
    @Lobotomist
    Software Developer
    Нужно использовать моификатор `u`:

    $re = '#^[A-Я][-а-яё]+ [A-Я]\.$#u';

    Ответ почему так кроется в документации к модификаторам шаблонов PCRE (модификатор u):
    u (PCRE_UTF8)
    Этот модификатор включает дополнительную функциональность PCRE, которая не совместима с Perl: шаблон и целевая строка обрабатываются как UTF-8 строки. Недопустимая целевая строка приводит к тому, что функции preg_* ничего не находят, а неправильный шаблон приводит к ошибке уровня E_WARNING. Пятый и шестой октеты UTF-8 последовательности рассматриваются недопустимыми с PHP 5.3.4 (согласно PCRE 7.3 2007-08-28); ранее они считались допустимыми.


    Без этого модификатора русский символ в кодировке utf-8 занимает два байта и воспринимается как два символа. Это видно на примере:
    // Длинна символа - 2 байта
    echo strlen("А");
    // 2
    
    // Значения 1-го и 2-го байтов:
    echo ord("А"{0});
    // 208
    echo ord("А"{1});
    // 144
    
    // Видно, что символ "А" в регулярном выражении воспринимается как последовательность двух символов
    var_dump(preg_match('#^А$#', chr(208).chr(144)));
    // int(1)


    В случае диапазона символов php воспринимает `[А-Я]` как `[chr(208)chr(144)-chr(208)chr(175)]` и в этот набор символов попадает всё с кодами от 144 до 208:
    var_dump(preg_match('#^[А-Я]$#', chr(150)));
    // int(1)
    Ответ написан
  • Сколько дней в пересечении двух дат?

    Lobotomist
    @Lobotomist
    Software Developer
    Пересечение двух диапазонов дат получить довольно просто - это разница между самым поздним началом и самым ранним концом этих периодов.

    /**
     * Предполагается, что dateStart <= dateEnd. Если это не гарантируется, нужно их привести к такому виду внутри функции.
     */
    function getIntersectDays(DateTime $dateStart1, DateTime $dateEnd1, DateTime $dateStart2, DateTime $dateEnd2) : int {
        $intersectionEnd = min($dateEnd1, $dateEnd2);
        $intersectionStart = max($dateStart1, $dateStart2);
    
        if ($intersectionStart >= $intersectionEnd) return 0;
    
        return (int)$intersectionStart->diff($intersectionEnd)->format('%a');
    }
    Ответ написан
  • Как привести массив в более читабельный вид?

    Lobotomist
    @Lobotomist
    Software Developer
    Например, заменить
    array_push($result,key($r));

    на

    // $r тут - номер слова в исходном массиве $words
    $word = $words[$r];
    $result[$word] = key($r);
    Ответ написан
  • MongoDB GridFS PHP - как прочитать пример?

    Lobotomist
    @Lobotomist
    Software Developer
    Команду можно разделить на части, чтобы проще было разбираться.

    Получаем объект коннекта к базе (MongoDB\Client)
    $connection = new MongoDB\Client;

    Получаем объект MongoDB\Database для работы с конкретной БД. Тут используется магический метод __get, в документации написано:
    Selects a database on the server. This magic method is an alias for the selectDatabase() method.

    То есть следующие команды делают одно и тоже.
    $database = $connection->example;
    $database = $connection->selectDatabase("example");


    Ну и теперь получаем объект MongoDB\GridFS\Bucket для работы с хранилищем файлов в базе $database
    $bucket = $database->selectGridFSBucket();

    Возможно, я как-то не так понял, в чем ваша проблема - так что задавайте доп. вопросы в комментариях
    Ответ написан
  • Как реализовать обработку результатов foreach в PHP?

    Lobotomist
    @Lobotomist
    Software Developer
    Вам стоило бы посмотреть, чему у вас равны переменные на каждом этапе обработки. И тогда вы бы заметили, что $matches[0] хранит в себе строки, а не массивы. И вместо $one[0] вам нужно обращаться к $one.

    preg_match_all( "/(?<=<code>).*?(?=<\/code>)/uis", $content, $matches );
    foreach($matches[0] as $one) {
    	$obr = htmlspecialchars( $one, ENT_QUOTES, 'UTF-8' );
    	$content = str_replace( $one, $obr, $content);
    }
    //</code>


    Еще можно использовать preg_replace_callback. Мне кажется это более логичным.

    $replaceFunc = function($matches) {
    	return htmlspecialchars($matches[0], ENT_QUOTES, 'UTF-8');
    };
    $content = preg_replace_callback("/(?<=<code>).*?(?=<\/code>)/uis", $replaceFunc, $content);
    //</code>


    В конце блоков кода присутствует "< /code>" - похоже, это глюк парсера тостера. Видимо, в связи с < code> внутри регэкспа.

    UPD:
    Ну вот, я только что заметил, что веду себя как некрофил =(
    Ответ написан
  • Выйти из внутреннего и внешнего цикла на php?

    Lobotomist
    @Lobotomist
    Software Developer
    Если отвечать конкрено на вопрос по выходу из цикла - нужно использовать конструкцию break
    break 2

    Но вообще твою задачу лучше решать другим способом. Если я правильно понимаю - у тебя есть много файлов, содержащих почтовые ящики и ты хочешь поместить все уникальные в один файл.
    Судя по тому, что ты считаваешь файл с помощью fle_get_contents - у тебя не очень то большое количество ящиков. Я бы на твоем месте сделал так:

    1. Создал бы ассоциативный массив, где в кач-ве ключей будут почтовые ящики
    2. Прошелся бы по всем файлам, просто устанавливая соответствующим элементам массива какое-нибудь значение, например true.
    3. Записал бы полученную информацию в файл.

    Что-то типа этого (пишу без тестирования - просто чтобы показать принцип)
    // Список файлов для обработки. Получить ты его можешь как угодно, например scandir.
    $files = ['/file1.txt', '/file2.txt'];
    
    $uniqueEmails = [];
    
    foreach ($files as $filePath) {
        $emails = file($filePath);
        foreach($emails as $email) {
            $email = trim($email);
            $uniqueEmails[$email] = true;
        }
    }
    
    $uniqueEmails = array_keys($uniqueEmails);
    $uniqueEmails = implode("\n", $uniqueEmails);
    file_put_contents('/out.txt', $uniqueEmails);
    Ответ написан
  • Как выбрать строку до определенных символов?

    Lobotomist
    @Lobotomist
    Software Developer
    Нужную последовательность нужно сгруппировать с помощью скобок. Конструкция (?=) означает, что то, что в скобках не будет являться частью найденного совпадения.
    ^.+(?=\s\w)

    Можно сделать так:
    ^(.+)\s\w
    Но в этом случае интересующая вас часть строки будет в первой группе, а полное совпадение будет вместе с пробелом и буквой на конце.

    Рекомендую два сервиса, в которых удобно проверять регулярные выражения:
    * pcre.ru (пример для вашего случая)
    * regex101.com
    Ответ написан
  • Почему сессия завершается во время работы с сайтом?

    Lobotomist
    @Lobotomist
    Software Developer
    На первый взгляд - какие-то (или один) php скрипты запускаются со своим маленьким значением gc_maxlifetime. И чем больше людей тем чаще запускается этот скрипт и стираются файлы сессий, которые он считает устаревшими.
    Нужно каким-то образом проверить это предположение.
    1. Установить для сайта другую директорию для хранения сессий. Посмотреть - осталась ли проблема. Если она ушла - значит есть какие-то еще php скрипты, которые удаляют файлы сессий.
    2. Сначала глазами проверить нет ли переопределения gc_maxlifetime перед session_start() во всех скриптах сайта. Если ничего не найдется - можно вынести работу с сессиями в отдельный класс и поставить там временно логирование куда-то настроек сессии при работе с ней. Возможно - найдутся виновные скрипты, которые являются частью сайта.

    И, на всякий случай, укажите, какие значения у следующих настроек во время выполнения?
    var_dump(ini_get('session.cookie_lifetime'));
    var_dump(ini_get('session.gc_maxlifetime'));
    var_dump(ini_get('session.gc_divisor'));
    var_dump(ini_get('session.gc_probability'));
    Ответ написан
  • Как лучше реализовать базу для проекта с конфигурируемыми лентами новостей?

    Lobotomist
    @Lobotomist
    Software Developer
    Раз никто не отвечает напишу кратко свое мнение, хотя оно затрагивает не все вопросы.
    Думаю, что вполне можно попробовать mongodb. Теги, категории и т.п. хранить в самих постах в массивах. Для лент использовать отдельные коллекции, обновлять их в случае изменения пользователем настроек ленты.
    Насчет домашней страницы - я не совсем понял что там можно изменять, так что ничего не скажу.
    Несколько шардов можно сделать, если база будет не справляться. Тут надо будет смотреть узкие места и тогда решать, что именно и как разносить.
    Ответ написан
  • Правильно ли я понимаю?

    Lobotomist
    @Lobotomist
    Software Developer
    Вариантов множество и выбрать можно только зная специфику конкретного проекта. Кроме того, нет предела совершенству и есть очеь много нюансов с которыми можно мириться в одних случаях и нельзя в других.
    Так что не зная проектов нельзя сказать правильно ли вы что-то поняли или нет. По этому я попробую ответить на озвученные вопросы, но полной картины не предоставлю.

    Разработка

    Разработчики могут проверять свой код либо, как вы сказали, на своих компьютерах - тогда нужно, чтобы разработчик мог запустить копию системы на своем компьютере.
    Могут, так же, использовать один тестовый сервер, с которым будут синхронизировать свой исходный код в процессе работы. Это можно делать с помощью rsync или winscp или чего-нибудь еще. Можно для каждого разработчика использовать отдельный поддомен vasya.develop.com, либо отдельную папку develop.com/vasya/

    Тестирование

    Поскольку в одном "билде" может быть несколько разных задач, то есть слито несколько веток - нужно протестировать их все вместе. Для этого нужно после слияния поместить этот код куда-то уже для тестирования (например, test.develop.com или develop.com/test/). Какие-то правки вносить нужно уже в ту ветку, в которую были слиты эти изменения. Тут уже все зависит от того, как вы это организовываете в git. Когда все протестировано можно деплоить код на продакшн.
    Заливать код можно как вы сказали, через ftp/sftp, либо можно при пуше в определенную ветку в центральном репозитории разворачивать копию на тестовом сервере. Например, при пуше в ветку test - на сервере develop.com делается fetch и в папку develop.com/test/ выгружается текущее состояние ветки test. Ну, это все в общих чертах - нужно еще решить, как и откуда будут переноситься настройки, специфичные для отдельного экземпляра системы, если они есть с какой БД этот экземпляр будет работать и т.п.

    БД

    Все зависит от того, как именно вы ее используете и как осуществляете деплой. Могу порекомендовать держать структуру базы и "системные" данные в каком-либо виде в репозитории. Чтобы можно было развернуть "пустую" базу данных только на основании данных в репозитории. Например, можно просто хранить там скрипты по созданию таблиц с нужной структурой и по заполнению какими-то системными данными. Когда вносятся какие-то изменения в структуру - нужно их, соответственно, сделать и в репозитории. Вообще, перенос изменений в базе данных это отдельный большой вопрос - погуглите "миграция базы данных".
    Ответ написан
  • PHP Simple HTML DOM Parser. Почему не могу получить элемент?

    Lobotomist
    @Lobotomist
    Software Developer
    Если посмотреть исходный код странички - станет ясно, почему отмеченный текст не попадает в выборку.
    Например, ты ищешь td с классом recommendation, но не все td в третьем столбце имеют этот класс. Например тут <td>Spirax S4 ATF HDX</td> этого класса нет. Так же ты вообще не берешь данные из столбца, в котором лежат заголовки <th class="tiername tiername">Standard</th>, откуда же им у тебя взяться? =)

    Я бы на твоем месте по какому-нибудь другому принципу парсил. Какую структуру ты в итоге хочешь получить?
    Ответ написан