Задать вопрос
Ответы пользователя по тегу PHP
  • Как правильно настроить логи php-fpm?

    copist
    @copist
    Empower people to give
    Оставь пользователя www-data и не меняй его

    либо поставь пользователю usersite группу www-data, файлам выдай права 775 (читать писать владельцу и группе)
    либо когда надо поработать с файлами, сделай sudo su www-data и ковыряйся с файлами

    2) Логи ротируются другим сервисом, у которого рутовые права, он сможет.
    3) Файлы сессий никогда сами не удаляются. Удаляй по крону. Крон от пользователя www-data или root
    4) Nginx ещё пользуется кэшированием при проксировании. Есть папки /var/lib/nginx/*** (путь зависит от версии ОС) - обычно nginx может туда писать, но если ты ему пользователя поменяешь, то и на эти папки права надо поменять
    Ответ написан
  • Можно ли обойти ISSET или $_SESSION?

    copist
    @copist
    Empower people to give
    Чтобы понять, как это взломать, надо понимать, как это работает и как такие проверки защищают от взлома.

    if (isset($_POST['go']))
    Для прохождения такой проверки нужно отправить запрос методом POST с параметром go в теле запроса
    curl -d "go=1" -X POST somedomain/someurl

    if (!isset($_SESSION['admin']))
    Сессия - это ресурс на стороне сервера, например файл с определённым именем. К нему нет прямого доступа.

    Для открытия сессии (то есть конкретного файла) используется идентификатор, сохраняемый в cookies. Обычно это PHPSESSID. Например, в куке PHPSESSID записано "2n24161uqm7ctl2tmp8b10sng3" - значит на сервер есть файл например /var/tmp/session_2n24161uqm7ctl2tmp8b10sng3.php (имя файла условное), а внутри записаны все значения массива $_SESSION, закодированные с помощью функции serialize.

    Тут два варианта взлома:
    1. В своём браузере записать в ту же куку то же значение, что и у админа. Для этого конечно, это значение надо у админа украсть.
    2. На сервере открыть файл сессии, декодировать, подставить там параметр "admin" = 1, закодировать и записать обратно в тот же файл.

    Оба вариант трудно реализуемы. Возможно, украсть куку админа будет чуть проще :) Например, украсть его ноутбук :) Или пошариться в его браузере, пока он душ принимает. Шпионство.
    Для таких как мы - шпионов-лузеров - в $_SESSION на стороне сервера при авторизации также записывают UserAgent браузера и IP адрес компьютера админа, чтобы подмена куки не сработала. То есть там не только !isset($_SESSION['admin']) но и несколько других проверок например. Если проверка на user-agent - нужно будет ещё подменить UserAgent своего браузера или в curl передать в заголовках. Если проверка на IP - тупик, IP подменить будет нереально, это на уровне протокола TCP

    Для подмены значений на сервере нужно взломать сервер. То есть либо подсунуть туда хитрый скрипт, который может быть выполнен путём запроса HTTP, он должен сделать описанное выше. Либо авторизоваться через FTP или SSH, затем либо искать файл сессии, чтобы поправить вручную, либо искать этот скрипт с проверкой !isset($_SESSION['admin']), чтобы закомментировать условие. Чтобы войти на сервер, понадобится стырить логин пароль у админа, либо украсть его ноут.

    Иногда сессии хранятся не в файлах, а в других сервисах. Например, в memcached или redis или ещё где угодно.

    В общем, юный хакер, изучай. Взломай для начала свой сервер с такими же проверками :)
    Ответ написан
    Комментировать
  • Почему Incorrect string value при strtolower?

    copist
    @copist
    Empower people to give
    mb_strtolower
    Ответ написан
    Комментировать
  • Всем привет,может кто знает как менять цены на сайте в зависимости от гео?

    copist
    @copist
    Empower people to give
    Есть два способа: 1. взять из браузера, если пользователь разрешит 2. определить по IP адресу
    Оба способа не 100% надёжны, могут показать совершено другую страну, например если посетитель пользуется прокси-сервером

    По втором способу (через IP адрес)
    1. На веб-сервере подключить модуль geo
    В Nginx https://docs.nginx.com/nginx/admin-guide/dynamic-m...
    В Apache https://dev.maxmind.com/geoip/legacy/mod_geoip2/
    В заголовках HTTP запроса будут дополнительные заголовки "страна", "город", "координаты" и другое

    2. В PHP подключить модуль GeoIP
    Тогда станут доступны методы типа "определить город", "определить страну"
    php.net/manual/ru/book.geoip.php

    Или использовать TabGeo https://github.com/dostelon/TabGeo - проект умер

    3. Воспользоваться серверными методами (межсерверный API) - ты ему IP адрес клиента, он тебе - геоинформацию
    https://dev.maxmind.com/geoip/legacy/web-services/
    или ip-api.com https://ipinfo.io geoip-db.com

    4. Аналогично можно использовать клиентскими (из браузера).

    В итоге у тебя либо на сервере (в PHP), либо в клиенте (JavaScript) код страны и по этому коду переключить блоки текстов и цены.
    Ответ написан
  • Что значит :id в запросе "SELECT * FROM db_name WHERE id = :id"?

    copist
    @copist
    Empower people to give
    Артем ответил правильно, но в указанном фрагменте кода https://github.com/igorsimdyanov/php7/blob/fd6d91e... нет биндинга. Это комментарии с заготовками кода. Кто-то написал на будущее. Под id подразумевался скорее всего slug страницы, чтобы из базы извлекать мета-теги. Его, к примеру, можно вычислить из URL текущего запроса или через какую-нибудь компоненту/класс/модуль, отвечающий за отображение текущей страницы.
    Ответ написан
    Комментировать
  • Как сделать шаблонизатор на php?

    copist
    @copist
    Empower people to give
    Сначала - зачем нужен шаблонизатор. Тут подмена понятий. Скорее вопрос такой: как отделить вывод от заголовков. Если не отделять, то может быть такая ошибка

    <html>
    <body>
    <?php
    // начать сессию
    session_start(); // отправить куку PHPSESSID через заголовки HTTP
    // но она не может отправиться, потому что уже начался вывод HTML в строке "<html ..."
    
    // если не авторизован, то отправить на страницу логина
    if (empty($_SESSION['username']))
        header('Location: /login.php'); // но заголовок HTTP тоже не может отправиться, как и кука
    ?>
        <h1>Hello, <?php echo $_SESSION['username'] ?></h1>
    </body>
    </html>


    Проблема решается, если вывод HTML делать после вывода заголовков. Например, использовать буфер
    <?php ob_start(); // открыть буфер ?>
    <html>
    <body>
    <?php
    session_start(); //  кука PHPSESSID отправится, потому что HTML ещё в буфере
    if (empty($_SESSION['username']))
        header('Location: /login.php'); // заголовок HTTP отправится, потому что HTML ещё в буфере
    ?>
        <h1>Hello, <?php echo $_SESSION['username'] ?></h1>
    </body>
    </html>
    <?php ob_end_flush(); // выбросить содержимое буфера наружу и закрыть его ?>


    Однако так придётся писать в каждом месте, где формируется HTML. Можно ли сократить?

    Простейшее представление через буфер

    <?php
    function render($viewPath)
    {
        if (!is_file($viewPath))
            return 'View "'. $viewPath . '" not exists';
        ob_start();
        include($viewPath);
        return ob_get_clean();
    }
    
    session_start();
    if (empty($_SESSION['username']))
        header('Location: /login.php');
    
    $viewsPath = __DIR__.'/views/'; // где лежат представления
    render($viewsPath . 'page.php'); // нарисовать страницу HTML


    <?php
    # page page.php
    ?>
    <html>
    <body>
        <h1>Hello, <?php echo $_SESSION['username'] ?></h1>
    </body>
    </html>


    А дополнительно ещё решают проблему отделения логики от формирования интерфейса. Работа с внешними данными в одном месте, а отображение их - в другом. Для этого в представление передают всё, что нужно показать. И точка. Лишних данных там не надо. В некоторых фреймворках стоит Exception если представление начнёт работать с базой данных, читает данные из входного запроса или пытается отправить заголовки.

    <?php
    function render($viewPath, $vars)
    {
        if (!is_file($viewPath))
            return 'View "'. $viewPath . '" not exists';
        extract($vars); // extract делает из массива набор переменных в локальной области видимости
        ob_start();
        include($viewPath); // эти переменные будут видны внутри подключаемого файла
        return ob_get_clean();
    }
    
    session_start();
    if (empty($_SESSION['username']))
        header('Location: /login.php');
    
    $viewsPath = __DIR__.'/views/'; // где лежат представления
    render($viewsPath . 'page.php', array( // отображаемые данные передаются массивом
        'username' => $_SESSION['username'],
    ));


    # page.php
    <?php
    /**
     * Подсказки для IDE, чтобы не подсвечивал переменные как неопределённые
     * @var string $username
     */
    <html>
    <body>
        <h1>Hello, <?php echo $sername ?></h1>
    </body>
    </html>


    Не правда ли, с представлениями код становится значительно изящнее. А если логика для реализации представлений хранится в отдельном файле-библиотеке, то код становится короче и понятнее.

    В фреймворках вместо функции render может использоваться объектная реализация

    <?php
    class ViewException extends Exception {}
    
    class View
    {
        public $viewsPath = __DIR__.'/views/';
        
        public function __construct($viewsPath = null)
        {
             // настройка представлений
            // например, можно перепределить место хранения представлений
            if (!is_null($viewsPath))
                $this->viewsPath = $viewsPath;
        }
    
        public function render($viewPath, $vars)
        {
            if (!is_file($this->viewsPath . $viewPath))
                throw new ViewException('View "'. $viewPath . '" in folder "'. $this->viewsPath . '" not exists');
            extract($vars);
            ob_start();
            include($this->viewsPath . $viewPath);
            return ob_get_clean();
        }
    }
    
    $view = new View();
    $view->render('page.php', array(
        'username' => $_SESSION['username']
    ));


    Как таблицы шаблонизаирова если в одной 3 столбца, в другой 5?

    Никто в представлениях не запрещает использовать языковые конструкции. Это могут быть конструкции языка PHP или какой-нибудь другой язык, специально написанный для шаблонизатора. Например, в Smarty, Blade, Pug свои языки. Передай в представление количество колонок и сделай цикл :)

    у первой первый столбец должен быть 70% а у второй последний?

    В представлениях можно подключать стили CSS, через которые меняется отображение (колонка 70%).
    У каждого представления может быть свои стили.
    Если нужно, чтобы были некоторые общие стили и дополнительные, нужные только для этой страницы, используют что-то типа буферизирования вывода блока стилей.

    <?php
    class Assets
    {
        public static $cssLinks = array();
        public static $css = array();
    
        // добавить ссылку на файл стилей
        public function addCssLink($link)
        {
            self::$cssLinks[$link] = $link;
        }
    
        // добавить блок стилей
        public function addCss($name, $css) // $name - это чтобы не дублировались блоки стилей, на всякий случай
        {
            self::$css[$name] = $css;
        }
    
        public function renderCss()
        {
            foreach(self::$cssLinks as $url)
                echo '<link href="'.$url.'" rel="stylesheet" type="text/css" />';
            echo '<style type="text/css">';
            foreach(self::$css as $css)
                echo $css;
            echo '</style>';
        }
    }


    # index.php
    
    $assets = new Assets();
    // общие стили
    $assets->addCssLink('https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css');
    
    // ...
    
    $view = new View();
    $view->render('page.php', array(
        'username' => $_SESSION['username']
    ));


    # page.php
    <?php
    /**
     * @var string $username
     */
    $assets = new Assets();
    $assets->addCssLink('/path/to/my/styles.css'); // ещё один файл стилей
    $assets->addCss('h1 { color: red; }'); // или даже что-нибудь микроскопическое, только для этой страницы
    ?>
    <html>
    <head>
    <?php $assets->renderCss() ?>
    </head>
    <body>
        <h1>Hello, <?php echo $username ?></h1>
    </body>
    </html>


    И аналогично про JS.
    Ответ написан
    7 комментариев
  • Как получить название категории: 2 базы данных, в одной (товары) id категории, в другой (категории) id категории и ее название...?

    copist
    @copist
    Empower people to give
    Насколько я понимаю картинку, база одна, `work` называется.
    Нужно запрос на объединение данных из двух таблиц

    SELECT `product`.*, `category`.`name`
    FROM `product`
    JOIN `category` ON `category`.`id` = `product`.`category_id`

    или
    SELECT `product`.*, `category`.`name`
    FROM `product`, `category`
    WHERE `category`.`id` = `product`.`category_id`
    Ответ написан
    3 комментария
  • Нужен ли Guzzle небольшим проектам?

    copist
    @copist
    Empower people to give
    Guzzle - это монстр, конечно.

    Иногда хватает объектной обёртки над curl. Таймуты, заголовки... Я для себя написал библиотеку https://github.com/pvolyntsev/http-requests и уже лет 6 ей пользуюсь, ещё до публикации github пользовался.

    Ещё есть библиотека Requests
    Ответ написан
    Комментировать
  • Как проектировать систему для отображения общего контента на разных субдоменах / доменах?

    copist
    @copist
    Empower people to give
    Предположим, у разных городов свой субдомен. Иногда отличается разметка (шаблон, тема). И данные хранятся в разных базах. А программный код общий. В этом случае надо перенастроить соединение с базой в зависимости от города и поменять шаблон. Можно соединять некоторые общие параметры приложения с параметрами, которые зависят от домена.

    Пример реализации https://github.com/pvolyntsev/toster_449124
    Конфиги в директории config/*.php
    Отображение в шаблонах layout/*.php - в конкретной CMS или фреймворке шаблоны переключаются по-другому
    Пример работы в public/index.php

    Вот по приблизительно такому же принципу на проекте Icons8 выбирается не город, а язык и некоторые другие параметры отображения
    https://ru.icons8.com - на русском
    https://it.icons8.com - на итальянском
    https://icons8.com - по-умолчанию, английский
    Ответ написан
    6 комментариев
  • Php, какая цель использование регулярных выражений?

    copist
    @copist
    Empower people to give
    Ответ на вопрос очень легко гуглится. Это настолько популярная тема, что написано уже килотонны материалов на всех языках. Есть предположение, что Sergey0808 получил вопрос на собеседовании и решил не парить голову. Но судя по ответам, тут только много весёлых ребят, которым нечем было занять пятничный вечер :)



    Практические примеры:
    • Проверка валидности URL, имейла, чисел, даты, времени
    • Поиск URL, email, дат, времени в заданном тексте
    • Удаление HTML тегов или иных шаблонных фрагментов, не несущих ценности
    • Обрамление URL, имейла, числа, даты, времени дополнительными HTML тегами, к которым позже с помощью JS добавляются виджеты URL, email, календарь. Например - открыть URL в другом окне, добавить в календарь событие на дату
    • Разбиение строки на части с замысловатым разделителем
    • Работа различных шаблонизаторов основана на замене регулярных конструкций с помощью preg_replace_callback. Например {{ var }} -> <?php echo $var ?>
    • Маршрутизация URL в серверных фреймворках - какой код выполнить в зависимости от URL. Например если на сервер пришёл запрос /admin/user/edit/1 - то выполнить код из модуля Admin класса UserController метода editAction с входным параметром "1"

    Ответ написан
    Комментировать
  • Как сделать в консоли меняющююся строку?

    copist
    @copist
    Empower people to give
    for($i=0; $i<100; $i++)
    {
      echo "{$i}%\r";
      sleep(1);
    }
    Ответ написан
    Комментировать
  • Какой принцип работы shortcode(шорткодов)?

    copist
    @copist
    Empower people to give
    Используется первый вариант
    Документация https://codex.wordpress.org/Shortcode_API
    Ответ написан
    Комментировать
  • Как нарезки картинки на пазл?

    copist
    @copist
    Empower people to give
    Можно составить карту кусочков в формате SVG и затем разбить картинку на много мелких SVG, каждый из которых в своём кусочке с обрезкой.

    Поисковый запрос в Google "SVG jigsaw puzzle pieces"

    Описание конкретно про паззл "кусочками" dev.inventables.com/2016/02/26/generating-svg-jigs...

    Используемая технология SVG + clipping mask
    https://developer.mozilla.org/en-US/docs/Web/SVG/T... дока
    https://getflywheel.com/layout/css-svg-clipping-an... описание
    https://codepen.io/yoksel/full/fsdbu/ примеры
    Ответ написан
    Комментировать
  • Какой фреймворк выбрать для обучения?

    copist
    @copist
    Empower people to give
    Свой собственный
    Ответ написан
    Комментировать
  • Как разложить данные по ячейкам в PHP?

    copist
    @copist
    Empower people to give
    Можно сделать запрос как ты хочешь, но проще (намного проще) сделать цикл по полученным записям

    $i=0;
    while($row = $db->get_row($result))
    {
       $idvar = "id".(++$i);
       {$idvar} = $row[0];
    }

    После выхода из цикла у тебя будут переменные вида $id1, $id2, $id3 и так далее

    У меня вопрос: а как ты их дальше будешь использовать?
    Ответ написан
  • Php Phalcon как создать списки контроля доступа для пользователей?

    copist
    @copist
    Empower people to give
    Документация
    https://olddocs.phalconphp.com/en/3.0.0/api/Phalco...
    https://olddocs.phalconphp.com/en/3.0.0/api/Phalco...

    Тут основная сложность, не описанная в документациии - это конкретная реализация получения роли текущего пользователя
    $acl->isAllowed("Guests", "Profiles", "show")
    Надо как-то определить, что текущий пользователь имеет роль Guest - это выходит за рамки документации.

    Вариант 1
    Можно хранить код роли в отдельной колонке role таблицы user
    Тогда будет $acl->isAllowed($user->role, "Profiles", "show")

    Вариант 2
    Можно в ACL прописать доступные права для каждого пользователя, например
    $acl->allow("ivanoff", "Profiles", "show");
    Тогда будет
    $acl->isAllowed($usr->username, "Profiles", "show")
    Ответ написан
  • Как переместить папку с помощью composer update?

    copist
    @copist
    Empower people to give
    Ответ описан детально на stackoverflow https://stackoverflow.com/a/14867050
    Нашёл запросом "npm vendor/node_modules", первый результат в списке
    Ответ написан
  • Php Phalcon, как отправить данные в формате json?

    copist
    @copist
    Empower people to give
    0. Убедитесь в /etc/hosts, что домен learning относится к хосту 127.0.0.1
    1. Проверьте вручную через консольную команду curl на том же самом сервере, что http://learning/test/accept возвращает какой-либо ответ
    2. Проверяйте логи веб-сервера
    access.log - был ли вообще запрос между двумя скриптами
    error.log - были ли ошибки
    3. В код sendAction() замените код, чтобы увидеть ошибку
    if( ! $result = curl_exec($curl)) // чтобы проверить на наличие ошибки CURL или HTTP
    {
        trigger_error(curl_error($curl));
    }
    curl_close($curl);
    echo $result; // чтобы видеть результат запроса

    4. В коде acceptAction() добавьте код
    $receive_data = json_decode($json,true);
    if (json_last_error()) // чтобы проверить наличие ошибки декодирования JSON
    {
        trigger_error(json_last_error_msg());
    }


    -------------

    А вообще логика вашего скрипта какая?
    Я вижу, что
    1. Первый скрипт выбирает пользователей из базы (без лимита, это страшно на продакшене)
    2. Этот список пользователей посылается запросом из public function sendAction() на http://learning/test/accept в формате JSON
    3. Этот JSON декодируется и передаётся в $this->view->users
    4. ... зачем?

    -----------

    Уточнённое решение

    На одном сервере получить JSON и показать через шаблон
    class TestController
    {
        public function showAction()
        {
            $json = file_get_contents("http://learning/test/users"); // получить данные со второго сервера
            $users = json_decode($json, true);
            $this->view->users = $users; // передать в представление
        }
    }


    На втором сервере выбрать данные из базы и выдать в формате JSON
    class TestController
    {
        public function usersAction()
        {
            $users = Users::find(); // получить из базы
    
            // подготовиться к выдаче ответа в формате JSON
            $this->view->disable();
            $response = new \Phalcon\Http\Response();
            $response->setStatusCode(200);
            $response->setJsonContent($users);
            $response->setContentType('application/json', 'UTF-8');
            return $response;
        }
    }


    По идее первый должен показать то, что хранится в базе в таблице user на втором сервере
    Ответ написан