Ответы пользователя по тегу PHP
  • Можно ли в php при присвоении переменной значения - функции, в качестве аргумента этой функции отправить эту самую переменную? Как это написать?

    onqu
    @onqu
    weasy
    ($validation = Validation::factory($_POST))
        ->rule('user', 'CustomValidation::is_login', array(':value', $validation));
    Ответ написан
    Комментировать
  • Каким должен быть правильный контроллер?

    onqu
    @onqu
    weasy
    Никаким. Его не должно быть. Вообще. Только сразу камнями не закидываете.

    Разъясню.

    1. Контроллеры в большинстве случаев не имеют состояний, а объект без состояния лишь дополнительное пространство имен для функций.

    2. Реализации
    В небольших приложениях в контроллеры наваливают бизнес логику, чтобы не тратить время на построение архитектуры. Оно себя оправдывает. Да мы лишаем себя возможности модульных тестов, но функциональные остаются рабочими. И это является противоречием назначению контроллера.
    class RobotsControlller
    {
        public function inactiveRobots()
        {
            $robots = Robots::find()->where(['active' => false])->all();
            foreach ($robots as $robot) {
                $robot->active = true;
                $robot->save();
            }
    
            return $this->render('robots', [
                'robots' => $robots
            ]);
        }
    }


    В проектах средней величины уже пытаются выносить бизнес логику в сервисы. Сервисный слой это и есть модель из множества классов. В контроллере вызывается один-два из этих сервисов и данные передаются в представление. И все бы хорошо, связующее звено для сервисов..., но это грамотно завуалированная бизнес логика, которую отчетливо видно лишь в крупных проектах.
    class RobotsControlller
    {
        public function inactiveRobots()
        {
            $container = $this->getContainer();
            $robotsService = $container->getRobotsService();
            $robots = $robotsService->getInactiveRobots();
    
            $stationService = $container->getStationService();
            $stationService->acivateRobots($robots);
    
            return $this->render('robots', [
                'robots' => $robots
            ]);
        }
    }


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

    3. Как же все-таки правильно.
    Отказываться от контроллеров и пилить свой велосипед на текущий момент слишком затратно по времени на всех проектах меньше больших. Мой ответ будет прост - использовать здравый смысл, сохранять семантику и не забывать об удобстве. Называете вещи своими именами и придерживайтесь однородности. Если строите api на типах resource (get | delete | put | post) то придерживайтесь этого стиля, не мешайте со стандартными экшенами. Разобравшись в одном будет проще понять работу всего остального.

    4. Примеры из жизни
    Рассмотрите любые примеры из реальной жизни. Например, процедуру покупки продуктов в супермаркете или покупки билета в метро. Контроллером является кассир, который принимает входящие данные (товары и деньги) и отдает представление (чек). Но со временем роль кассира начинает выполнять автомат, покупатель сам пробивает товары или покупает билет на проезд. Контроллер поменялся. Через еще некоторое время билет покупать уже не требуется, есть просто телефон с NFC, а в супермаркете просто получаем товары по заранее отправленному с этого же телефона заказу.

    В каждом MVC много других маленьких MVC, скажете вы, ища оправдания необходимости контроллера.

    p.s. можно начинать кидать камни)
    Ответ написан
    3 комментария
  • В чем суть роутера на php?

    onqu
    @onqu
    weasy
    1. Здесь пугают всякими контроллерами, ларавелями. Давайте жить проще. Для начала дадим определение модному слову роутер. Это маршрутизатор. Что делает маршрутизатор? Правильно. Обрабатывает маршруты, являясь связующим звеном. Маршрутом для web сайта принято считать метод запроса [GET, POST, PUT и другие] и компоненты URI.

    например: https://ru.wikipedia.org/wiki/URI?foo=bar#title
    [схема: https] :// [источник: ru.wikipedia.org] [путь: /wiki/URI] [запрос: ?foo=bar] [фрагмент: #title]


    Но для определения маршрута может браться любая другая информация передаваемая серверу, определение выше это лишь наиболее употребляемые параметры.

    Сама работа, как правило проста: от клиента приходит запрос, маршрутизатор перебирает все заданные ему пути до первого совпадения. При совпадении вызывается определенная вами функция, которая возвращает ответ клиенту.

    2. Он необходим, если в приложении одна точка входа, когда любой запрос приходит на один файл.

    3. Простой пример
    // файл index.php
    
    // Маршруты
    // [маршрут => функция которая будет вызвана]
    $routes = [
        // срабатывает при вызове корня или /index.php
        '/' => 'hello',
        // срабатывает при вызове /about или /index.php/about
        '/about' => 'about',
        // динамические страницы
        '/page' => 'page'
    ];
    
    // возвращает путь запроса
    // вырезает index.php из пути
    function getRequestPath() {
        $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
    
        return '/' . ltrim(str_replace('index.php', '', $path), '/');
    }
    
    // наш роутер, в который передаются маршруты и запрашиваемый путь
    // возвращает функцию если маршшрут совпал с путем
    // иначе возвращает функцию notFound
    function getMethod(array $routes, $path) {
        // перебор всех маршрутов
        foreach ($routes as $route => $method) {
            // если маршрут сопадает с путем, возвращаем функцию
            if ($path === $route) {
                return $method;
            }
        }
    
        return 'notFound';
    }
    
    // функция для корня
    function hello() {
        return 'Hello, world!';
    }
    
    // функция для страницы "/about"
    function about() {
        return 'About us.';
    }
    
    // чуть более сложный пример
    // функция отобразит страницу только если
    // в запросе приходит id и этот id равен
    // 33 или 54
    // [/page?id=33]
    function page() {
    
        $pages = [
            33 => 'Сага о хомячках',
            54 => 'Мыши в тумане'
        ];
    
        if (isset($_GET['id']) && isset($pages[$_GET['id']])) {
            return $pages[$_GET['id']];
        }
    
        return notFound();
    }
    
    // метод, который отдает заголовок и содержание для маршрутов,
    // которые не существуют
    function notFound() {
        header("HTTP/1.0 404 Not Found");
    
        return 'Нет такой страницы';
    }
    
    
    // Роутер
    // получаем путь запроса
    $path = getRequestPath();
    // получаем функцию обработчик
    $method = getMethod($routes, $path);
    // отдаем данные клиенту
    echo $method();


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

    4. Обойтись без него можно. Если каждая страница в вашем приложении будет являться отдельным файлом, который отвечает за отдачу информации.
    index.php
    about.php
    contact.php
    ...


    Это олдскульная структура, в новых проектах почти не применяется.
    Ответ написан
    13 комментариев
  • Утечка памяти при парсинге SAX методом(XML), что я зделал не так?

    onqu
    @onqu
    weasy
    Судя по всему, массив $output разрастается очень сильно, памяти не хватает. Решить можно разными путями:

    1. сделать цикл в методе execute с yield если php >= 5.5, но нужно будет следить за консистентностью массива $output, чтобы удалять отданные данные и дописывать новые
    2. обернуть в ArrayIterator, но также нужно будет писать другой код
    3. передавать в execute callback функцию, которая будет вызываться в методе endElements и если $name будет равен 'class', то есть при закрытии обертки

    private function endElements($parser, $name){
            $this -> deep --;
            if(!empty($name)) {
                $this -> element = null;
            }
    
            // $this->wrapper == 'class'
            if ($name === $this->wrapper && !empty($this->output)) {
                    // отдать данные в коллбэк
                    call_my_callback($this->output);
                    // очистить буфер
                    $this->output = [];
            }
        }


    ps. может возникнуть ошибка в методе characterData, в этот метод данные могут быть дописаны, например:
    fread вернул данные, которые оканчиваются на "<learner>Уче", как итог в данные парсером запишется "Уче", при следующем вызове в метод прилетит "ник1", и перезапишет "Уче".

    нужно делать что-то типа:

    private function characterData($parser, $data){
            if(!empty($data)) {
                if (in_array($this->element, $this->necessary_elements)) {
                    if (!isset($this->output[$this->element])) {
                        $this->output[$this->element] = '';
                    }
                    $this->output[$this->element] .= trim($data);
                }
            }
        }
    Ответ написан
    Комментировать
  • Как можно улучшить этот кусок говнокода на PHP?

    onqu
    @onqu
    weasy
    function getImageData($name, $size) {
        $data = get_field($name);
    
        return array_merge($data, [
            'thumb' => $data['sizes'][$size],
            'width' => $data['sizes'][$size . '-width'],
            'height' => $data['sizes'][$size . '-height'],
        ]);
    }
    
    $image = getImageData('lead_image', 'lead-custom-size');
    
    // view.php
    <img src="<?= $image['url'] ?>" height="<?= $image['height'] ?>" width="<?= $image['width'] ?>">


    git commit -m 'finally got this shit beautified'

    // sarcasm begin
    // Refactored!
        _
      _( )_
     (_(%)_)
       (_)\
           | __
           |/_/
           |
           |
    
    $leadimage 			= get_field('lead_image');
    $bot_first_img 		= get_field('image_#1');
    $bot_second_img 	= get_field('image_#2');
    ...
    
    // sarcasm end
    Ответ написан
    Комментировать
  • Как обратиться к родителю в JSON?

    onqu
    @onqu
    weasy
    Просто передавайте имя родителя в функцию с параметром по умолчанию, да вообще, что угодно. И откажитесь от глобалов, вредно это, кошмары начинают по ночам снится, раннее поседение.

    function recursiveJSON2ARRAY($obj, $parentname = null)
    {
      global $marks;
      foreach ($obj as $v) {
        if (isset($v->children) && is_array($v->children) && count($v->children) > 0) {
          $parentname = $v->name;
          foreach($v->children as $child) {
            if(isset($child->url)){
              $marks[$child->date_added] = array('parentname'=>$parentname, 'name'=>$child->name, 'url'=>$child->url);
            } else {
              recursiveJSON2ARRAY($v->children, $parentname);
            }
          }
        } else {
          if (isset($v->url)) {
            // если $parentname не передали, то он null
            $marks[$v->date_added] = array('parentname'=>$parentname, 'name'=>$v->name, 'url'=>$v->url);
          }
        }
      }
    }
    
    if ($b_bar) {
      recursiveJSON2ARRAY($b_bar);
    }
    Ответ написан
    Комментировать
  • Return в конструкторе PHP?

    onqu
    @onqu
    weasy
    Конструктор не возвращает значений.
    Сигнатура:
    void __construct ([ mixed $args = "" [, $... ]] )

    Читаем php.net/manual/ru/language.oop5.decon.php
    Ответ написан
    Комментировать
  • Как установить 2 версии PHP на LAMP?

    onqu
    @onqu
    weasy
    Использую phpbrew, не серебряная пуля, но довольно удобно. Позволяет быстро менять версию из уже установленных, причем можно держать одинаковые версии с разными расширениями, подключать и отключать расширения. Установка версий проблем не вызывала.
    Ответ написан
    Комментировать
  • Почему $_SERVER['REQUEST_URI'] захватывает лишние url-ы?

    onqu
    @onqu
    weasy
    Скорее всего на сервере (apache, nginx) стоит перенаправление на ваш скрипт (rewrite) если файл не найден, а пишите вы все обращения подряд.
    Ответ написан
    Комментировать
  • Почему в базу записываются нули и пустые строки?

    onqu
    @onqu
    weasy
    К maximw хочу добавить, что функция "header" не прерывает работу скрипта, это всего лишь http заголовок. Прервите его через exit или die.
    if (!isset($_SESSION['login'])){
        header("Location: login.php");
        exit();
    }


    Не буду рассматривать качество кода, но чтобы быть уверенным, что запрос пришел с методом POST, добавьте проверку. (php.net/manual/ru/reserved.variables.server.php)

    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        // что-то сделать
        // можно вернуть обратно, но хорошо бы проверить содержимое $_SERVER['HTTP_REFERER'];
        // header('Location: ' . $_SERVER['HTTP_REFERER'])
        // завершить exit()
    }
    Ответ написан
    Комментировать
  • Как заменить радиокнопки на выпадающий список?

    onqu
    @onqu
    weasy
    Атрибут "name" следует указывать для элемента "select", а не для "option"

    <select class="payment_methods methods" name="payment_method">
        <option value="">Something</option>
        <option value="">Other thing</option>
        ...
    </select>
    Ответ написан
    2 комментария
  • Как получить исходящее значение программы на PHP?

    onqu
    @onqu
    weasy
    Чтобы функция file_get_contents возвращала данные, запрашиваемые через url, нужно включить директиву "allow_url_fopen" в php.ini. Проверить можно через phpinfo().

    www.php.net/manual/ru/filesystem.configuration.php...

    Советую посмотреть документацию по использованию cURL: www.php.net/manual/ru/book.curl.php
    Ответ написан