Вышла версия 2.0.9 PHP-фреймворка Yii. Минорный релиз содержит около 60 небольших улучшений и исправлений. Инструкции по установке можно найти на официальном сайте.


В данной версии есть два изменения, которые, хоть это и маловероятно, могут затронуть ваши приложения. Ознакомьтесь с UPGRADE.md.


Спасибо сообществу Yii за пулл-реквесты и обсуждения.
Этот релиз вышел благодаря вам!


За разработкой фреймворка можно наблюдать на GitHub. Также у нас есть Twitter
и Facebook.



Далее мы рассмотрим самые интересные улучшения подробней. Полный список изменений и исправлений можно найти в CHANGELOG.


Фильтр action


\yii\base\ActionFilter теперь поддерживает маски для only и except, что полезно когда
фильтр навешивается на модуль или приложение целиком:


return [
    'as filter' => [
        'class' => 'app\filters\SomeFilter',
        'only' => [
            'particular/*', // все действия контроллера 'particular'
            '*/captcha', // все действия 'captcha' всех контроллеров
        ],
    ],
    // ...
];

Улучшения производительности


  • Улучшили производительность перевода сообщений при использовании базы данных. Добавили нужные индексы.
  • Схема Oracle теперь считывается быстрее.

Построитель схемы и миграции


Был улучшен построитель схемы, который используется в миграциях. Добавили новый метод null() чтобы указывать возможность записи null явно. Метод применяется автоматически, если значение по умолчанию — null.


$type = $this->string(42)->null();

Также добавили метод для своего SQL:


$type = $this->string(15)->notNull()->append('collate ascii_bin')->append('character set ascii');

Синтаксис команды для генерации миграций был немного изменён: _table и _column теперь обязательны:


./yii migrate/create create_user_table
./yii migrate/create add_name_column_to_user_table

Провайдеры данных и виджеты


Все улучшения в данном релизе касаются заголовков. В \yii\data\ArrayDataProvider добавили свойство $modelClass, через которое можно указать модель для получения заголовков полей. В дополнение \yii\grid\DataColumn, который определяет поведение для всех столбцов с данными, теперь пытается получить заголовки из filterModel грида.


Рефакторинг


Из интерфейса ManagerInterface RBAC выделили CheckAccessInterface, который может быть полезен при реализации своей проверки доступа.


\yii\web\User::loginByCookie() отрефакторен для большей расширяемости.


Asset-ы


При перечислении файлов в пакетах asset-ов теперь можно задать путь в null. В этом случае файлы не регистрируются. Это полезно, например, для регистрации дополнительных файлов для рабочего окружения:


namespace common\assets;
use yii\web\AssetBundle;

class ReactAsset extends AssetBundle
{
    public $sourcePath = null;

    public $js = [
        YII_ENV_DEV ? "//fb.me/react-15.0.1.js" : "//fb.me/react-15.0.1.min.js",
        YII_ENV_DEV ? "//fb.me/react-dom-15.0.1.js" : "//fb.me/react-dom-15.0.1.min.js",
        YII_ENV_DEV ? "//cdnjs.cloudflare.com/ajax/libs/babel-core/5.6.15/browser.js" : null,
    ];
}

Логирование


\yii\log\Target::$logVars теперь можно настроить более тонко:


  • _SESSION — пишем глобальную переменную сессии. Всё как и было.
  • _SESSION.id — пишем только id из сессии.
  • !_SESSION.secret — не пишем ключ secret из сессии.

Логика такой фильтрации вынесена в \yii\helpers\ArrayHelper::filter(). При необходимости можно использовать у себя.


Markdown


Тип синтаксиса по умолчанию для yii\helpers\Markdown теперь можно задать через $defaultFlavor.

Поделиться с друзьями
-->

Комментарии (65)


  1. ilyaplot
    11.07.2016 18:07

    Когда уже Yii перестанет генерировать длиннющие дублирующие where in запросы для join?


    1. SamDark
      11.07.2016 18:11

      А чем они вам мешают?


      1. ilyaplot
        12.07.2016 09:54

        Тем, что сначала выполняется join запрос, а потом in. Зачем выполнять запрос два раза? Неужели никто не сталкивался с ограничением длины запроса? Почему же тогда для каждой ячейки каждой строки не выполнять отдельный запрос?


        1. ilyaplot
          12.07.2016 11:10

          Я бы попросил аргументировать минусы к комментарию. Возможно, я чего-то не понимаю? Как мне кажется, данная ситуация с запросами упрощает взаимодействие AR моделей, но отрицательно сказывается на производительности. Или наоборот? Объясните, пожалуйста.


          1. SamDark
            12.07.2016 13:45

            На производительности это практически не сказывается. Если бы сказывалось значительно, мы бы себе не позволили применить такое решение.


            1. ilyaplot
              13.07.2016 17:51

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


        1. SamDark
          12.07.2016 13:44

          Тем, что сначала выполняется join запрос, а потом in.

          В Yii 1.1 мы пытались вытащить связанные модели из одного JOIN запроса. В большинстве случаев это работало, но не всегда. Особенно для запросов с GROUP, HAVING и т.д.


          Код при этом был довольно сложный. Из за этого возникали непоправимые баги.


          В 2.0 путём переделки запросов на WHERE IN(...) удалось упростить код, обрабатывающий результат в разы и уйти от всех проблем, которые нас приследовали в 1.1.


          Это про причины такого поведения в 2.0.


          Зачем выполнять запрос два раза?

          Затем, что иногда всё-таки надо применить условие по таблице из JOIN.


          Неужели никто не сталкивался с ограничением длины запроса?

          Нет. ActiveRecord не стоит пользоваться для импорта-экспорта. В нормальных ситуациях в WHERE IN оказывается не более сотни ID-шников. Плюс-минус.


          Почему же тогда для каждой ячейки каждой строки не выполнять отдельный запрос?

          Потому что нет необходимости? :)


          1. nepster09
            13.07.2016 11:18
            +1

            Кстате по поводу WHERE IN, в laravel5 так-же используется такой подход, при этом в Yii2 есть with и joinWith.

            Так вот вопрос: WHERE IN используется в основном вместо join для того, чтобы было легче написать код билдера и отойти от некоторых багов при этом производительность примерно одинаковая (что JOIN, что WHERE IN)?


            1. SamDark
              13.07.2016 11:19

              Да. Плюс можно делать аналог JOIN между двумя не связанными хранилищами. Например, MySQL и Redis.


              1. nepster09
                13.07.2016 12:11

                Тогда еще пользуясь случаем спрошу про полиморфические связи. Вроде в Yii2 такой реализации нет, но вот в Laravel5 сделали. Тоесть смысл такой, что есть entity_id и type. Type это алиас, который соответствует определенной таблице, а entity_id соответственно идентификатор сущности. Тогда появляется возможность джоинить разные данные из других таблиц.

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


                1. SamDark
                  13.07.2016 12:18

                  Бывает, конечно, что и кстати, но да, без внешний ключей как-то не очень.


                  На тему сложных отношений есть вот такое: https://github.com/yii2tech/ar-role


                  1. nepster09
                    13.07.2016 12:34

                    А как было бы хуже:
                    1) Отсутствие ключа и абстрактное entity_id
                    или
                    2) Перечисление всех полей для джоина на другие таблицы: student_id, instructor_id, tester_id, но с ключами.


                    1. SamDark
                      13.07.2016 14:33

                      От ситуации зависит.


                      1. nepster09
                        14.07.2016 17:45

                        Понял. Импровизирую. Спасибо.


                1. nemesis1980
                  14.07.2016 14:37
                  +2

                  С with на yii2 тоже не проблема сделать полиморфные, а вот с joinWith проблема


        1. demimurych
          12.07.2016 21:06
          +1

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


      1. ilyaplot
        12.07.2016 10:01

        Да и далеко не только мне они мешают. https://github.com/yiisoft/yii2/issues/4631 https://github.com/yiisoft/yii2/issues/3929 — тут даже знакомые лица фигурируют :)


        1. SamDark
          12.07.2016 13:47

          Павел Климов из core team сделал расширение чтобы убирать дополнительные запросы в качестве эксперимента. Вот что у этого расширения в аннотации:


          3) Despite extra query removal, this extension may not actually increase overall performance. Regular Yii eager join query is very simple and fast, while this extension consumes extra memory and performs extra calculations. Thus in result performance remain almost the same. In most cases usage of this extension is a tradeoff: it reduces load on Database side, while increases it on PHP side.


    1. adamas_antares
      12.07.2016 11:09

      вот за это я и не люблю ORM, для сложных запросов всегда пишу sql запросы сам, orm использую только для простых вещей


      1. ilyaplot
        12.07.2016 11:11

        Иногда очень хочется использовать ORM для many-to-many. В yii 1 удавалось создать максимально эффективный запрос. Во 2 yii уже так просто не выходит.


      1. SamDark
        12.07.2016 13:50

        Для действительно сложных запросов действительно стоит писать SQL. Но AR — штука полезная потому как большинство запросов в любом приложении очень простые.


        1. nepster09
          13.07.2016 11:23

          Вообще как я понял лучше всего завернуть все в репозитории и вообще инкапсулировать все ваши запросы. Тогда разницы на чем вы их пишите не будет. Так как если, что-то просядет, мы быстренько подменим реализацию на другой репозиторий и все должно отработать как следует.
          + Как многие утверждают: за ранее мы не можем узнать, что и где просядет при нагрузках. Тоесть подстава может быть в самом простом запросе счетчика.


          1. SamDark
            13.07.2016 11:58

            Да, так и есть.


  1. MrSSDD
    11.07.2016 19:52
    +3

    Спасибо за Asset-ы


  1. nemesis1980
    12.07.2016 00:51
    +2

    Спасибо. Было бы круто, если в миграциях enum добавили


    1. SamDark
      12.07.2016 13:49

      Закиньте в issue на GitHub, если там этого ещё нет.


  1. Frost12358
    12.07.2016 00:51
    +1

    Спасибо, свойство $modelClass в ArrayDataProvider в самый раз!


  1. littlefuntik
    12.07.2016 00:51
    -2

    Радуете, ребята.
    Вот была одна большая неприятность у сотрудника — он из-за своей невнимательности условие запроса записал в Model::find($condition) вместо Model::find()->where($condition) и получая ActiveQuery думал, что условие запомнилось. Дальше по нему выполнялся метод delete(). И вместо того, что бы удалить несколько записей из БД (по условию) он удалил все записи… а это был продакшн и печаль получилось… Дак вот круто было, что бы из коробки Yii2 он кидать исключение о том, что в find() нет аргументов при их использовании…


    1. symbix
      12.07.2016 06:49
      +4

      С подходом «опа-опа и продакшен» никакие фичи фреймворка не помогут. Поможет внедрение CI, тестирования и код-ревью.


      1. Blumfontein
        12.07.2016 07:07

        Справедливости ради случай с удалением всех строк из таблицы фреймворки действительно могли бы и обрабатывать. Т.е. при генерировании запроса DELETE FROM table_name из querybuilder-a кидать исключение, а если нужно действительно удалить все строки, то завести для этих целей отдельный метод truncate().


        1. symbix
          12.07.2016 07:55
          +2

          delete from table и truncate table — это несколько разные вещи все же. Лучше отдельный метод deleteAll(), а truncate() пусть делает truncate.


        1. Mendel
          12.07.2016 10:30
          +1

          А если прилетит delete с условием которое всегда истина? Что прям все варианты разбирать?
          Не помню как в yii, но у меня есть delete у модельки. Тут не напортачишь. А если вызывается метод массового удаления, то надо быть максимально внимательным и да, правильно говорят — не забывать тестировать.
          Массовое удаление это такая вещь на которой многие спотыкались. И много думалось о том как лучше сделать.
          Лично мое мнение — надо на прикладном уровне в модельке/билдере для удаления делать «корзину», а ядро не трогать, и лишних исключений не делать.
          Оптимизация безопасности delete (и update к слову тоже, а то типа никто никогда не затирал важных атрибутов не тем данным) заканчивается когда делают что без условия ничего не меняется/ну удаляется (хотя на find* спокойно ставится условие вроде where 1=1). Всё остальное как правило из области «одной рукой лечим, другой калечим».


          1. Blumfontein
            12.07.2016 14:11

            >> А если прилетит delete с условием которое всегда истина? Что прям все варианты разбирать?

            Нет, просто «Нет WHERE блока — исключительная ситуация».


            1. Mendel
              12.07.2016 15:04

              Ну допустим.
              Но это не так просто — взял да и запилил.
              Тут нужно продумать, а где бросать исключение?
              На низком уровне оно по идее и так не пропустит, скорее всего подставив дефолтное «1=1».
              У меня в фреймворке для изменяющих запросов по умолчанию подставляется FALSE, что на низком уровне превращается в «1=2».
              И если тут не так, то может и стоит подумать о другом дефолтном поведении на низком уровне.
              Кидать исключение на высоком уровне? Разве что при выполнении/компиляции запроса. Но тут метод универсальный, и делить его на изменяющий и неизменяющий — та еще работа.
              А на уровне построителя — пока на выполнение/генерацию запроса не послали, так в любой момент могут условие добавить, так что некошер. Но ок. Вставили мы куда-то исключение, всё супер. Даже не перетрудились и ничего не поломали. Результат какой? Перечитайте еще раз сообщение с которого начался этот тред — наши пляски с бубном ему бы не помогли. Ведь он потерял условие у find. А тут обязательное условие — однозначное зло ибо ломает целый вагон кейсов где оно не нужно.


        1. zelenin
          12.07.2016 12:08
          +1

          серьезно думаете, что задача фреймворка — «защита от дурака»?


          1. Blumfontein
            12.07.2016 14:06

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

            Фреймворки и библиотеки должны по возможности защищать от типичных ошибок. DELETE и UPDATE без WHERE блока в 99% случаев — ошибочная ситуация. Ну на крайняк WARNING кидать неплохо бы.


            1. zelenin
              12.07.2016 14:18
              +1

              автоэкранирование — одна из задач шаблонизатора и один из самых главных плюсов.


              1. M-A-XG
                12.07.2016 20:04
                -1

                Автоэкранирование в шаблонизаторе это примерно как магические кавычки для защиты от sql-инъекций:
                http://php.net/security.magicquotes

                Или register_globals

                Раньше все дрочили на это.
                Сейчас все проклинают.

                Автоэкранирование не панацея от XSS.

                Все это нужно для низкоквалифицированных разработчиков.


                1. zelenin
                  12.07.2016 20:07
                  +1

                  пруф на проклятия, пожалуйста.


                1. symbix
                  13.07.2016 08:47
                  +1

                  Низкоквалицифированные разработчики как раз проводят аналогии между magic quotes и автоэкранированием в шаблонах.

                  На самом деле, автоэкранирование в шаблонах по сути своей намного ближе к emulated prepares в pdo.


                  1. Mendel
                    13.07.2016 10:42

                    Это сложный идеологический спор о том где лучше экранировать/фильтровать данные для вывода.
                    У обеих сторон есть свои за и против. Но это не имеет отношения к теме обсуждения просто потому что хорошее оно или плохое автоэкранирование в шаблонах, но цель его не в защите от дурака, а в упрощении работы программиста.


                  1. M-A-XG
                    13.07.2016 12:42

                    >автоэкранирование в шаблонах по сути своей намного ближе к emulated prepares в pdo.

                    С чего бы?
                    Подготовленные выражения работают явно.
                    Мы вызываем функционал, который будет экранировать.

                    Да и htmlspecialchars не экранирует…
                    htmlspecialchars преобразует специальные символы в HTML-сущности.

                    «Автоэкранирование» не панацея.
                    А иногда оно лишнее.
                    Например нужно вывести строку, где должен быть кусок html, а остальные данные с базы.
                    Если понядеятся на «автоэкранирование», то html выведется испорченный.
                    Если отключить «автоэкранирование», то с базы может пролезть html.
                    Экранировать нужно вручную то, что нужно.

                    Если используется кеширование, то заэкранировать лучше один раз и сохранить в кеш.
                    Я шаблоны вообще не кеширую, кеширую только «контроллеры».
                    Часто в базе хранится уже обработанный html, или про-htmlspecialchars-енный, или про-strip_tags-енный. Повторная обработка испортит его.

                    А так да, для клепания говносайтов низкоквалифицированными разработчиками автоэкранирование нужно. :)


                    1. SamDark
                      13.07.2016 14:34

                      Часто в базе хранится уже обработанный html, или про-htmlspecialchars-енный, или про-strip_tags-енный. Повторная обработка испортит его.

                      Это зря.


                      1. M-A-XG
                        13.07.2016 16:28

                        Зря повторно обрабатывать или зря хранить обработанный? :)


                        1. SamDark
                          13.07.2016 16:52

                          И то и другое.


                    1. t_kanstantsin
                      13.07.2016 16:45

                      Экранировать по умочалнию или только по запросу каждый решит для себя. Но проблемы надуманные.

                      обработанный html — для таких особых случаев в шаблонизаторе есть особые методы, которые позволяют выводить чистый html.
                      про-htmlspecialchars-енный — а зачем это хранить? Если легаси или он таким приходит, то можно использовать htmlspecialchars_decode
                      strip_tags — а если тексте есть строка " " (неразрывный пробел)? Без htmlspecialchars выведется просто пробел.


                    1. symbix
                      13.07.2016 18:22
                      +2

                      Внимательно посмотрим на эти строки:

                      1) PDO prepares:
                      SELECT * FROM users WHERE username = :username
                      2) Template engine:
                      Username: {{ username }}

                      И в том, и в другом случае в результате получается выражение с корректно отформатированным строковым литералом на целевом языке — SQL и HTML соответственно.

                      Иногда надо и в SQL-запрос подставить SQL-выражение, и в HTML кусок HTML. Это в обоих случаях записывается особым образом.


    1. p0vidl0
      12.07.2016 08:01
      +1

      Так вы расскажите «сотруднику», что существуют IDE, которые умеют предупреждать о неверном количестве параметров для метода и не только.


    1. springimport
      12.07.2016 17:22

      Почему в find нельзя передавать параметры?


      1. Mendel
        12.07.2016 17:54

        Кстати да, пропустил этот момент. Это еще один аргумент почему не стоит трогать то что работает, когда изменение глобальное а цель несет ничтожную. Сразу живой пример почти в тему $activeRecord->delete() параметров не подразумевает. Но в прикладной задаче я его переопределил, и при отсутствии параметра идет «удаление в корзину», а при $activeRecord->delete(TRUE) вызывает родителя и действительно убивает. Параметра не было, но он появился.


        1. springimport
          12.07.2016 18:11

          Может это как в ситуации с load в моделях. Там стоит условие is_array, а мне нужно загрузить данные из API, а они object. Я писал в issues по этому поводу, но вразумительного ответа не получил.


          1. Mendel
            12.07.2016 19:41

            Тут скорее пулреквест надо писать.
            Таких мест обычно во всех фреймворках десятки где массив и не обязателен, а подойдет и объект с ArrayAccess да править лень, ибо редко когда на что-то влияет.
            Или делайте преобразование перед лоад или к issues пулреквест прикладывайте.
            Лично я бы на месте мейнтейнеров yii забил бы на такой issues а пулреквест скорее всего принял бы.


  1. mamontovdmitriy
    12.07.2016 11:09
    +1

    Спасибо! А о самом главном (fxp/composer-asset-plugin:^1.2.0) в статье умолчали ;)


    1. SamDark
      12.07.2016 13:54

      Да, об этом действительно забыли, если вы про возросшую производительность и возможность её нарастить ещё :)


      Или вы про то, что надо composer global update сделать?


    1. Lisio
      12.07.2016 19:35

      У себя стали использовать дополнительный репозиторий asset-packagist.org и все работает быстро и стабильно без дополнительных плагинов.


      1. SamDark
        13.07.2016 02:12

        Да, вариант.


  1. M-A-XG
    12.07.2016 14:54
    -4

    >Улучшили производительность перевода сообщений при использовании базы данных. Добавили нужные индексы.

    Позор, что их до сих пор не было.


    1. SamDark
      12.07.2016 15:59
      +5

      Ну вот теперь они есть. Позор смыт, честь восстановлена :)


  1. BoShurik
    12.07.2016 19:03

    Yii не следует SemVer? Немного странно видеть UPGRADE.md для патчей


    1. SamDark
      13.07.2016 02:13

      Это не патчи. Патчи были бы 2.0.8.1.


      1. BoShurik
        13.07.2016 09:23

        А что тогда означают 2.0.9?


        Согласно документации:


        Given a version number MAJOR.MINOR.PATCH, increment the:

        MAJOR version when you make incompatible API changes,
        MINOR version when you add functionality in a backwards-compatible manner, and
        PATCH version when you make backwards-compatible bug fixes.


        1. Mendel
          13.07.2016 10:48

          Yii всегда плохо соблюдали нумерацию версий, и вроде как писали что и во второй версии они ее не придерживаются.
          Вероятно сложно съехать с уже проложенных рельс, уже распланировано что куда, созданы ветки, куча микроскопической, но работы.


        1. SamDark
          13.07.2016 11:18

          1. BoShurik
            13.07.2016 11:45

            То цитата из документации SemVer.
            Теперь вижу, что не следуете. Точнее следуете по шаблону 2.MAJOR.MINOR.PATCH.
            Только вот в composer разве можно указать "2.0.0.*"? В случае какой-то критической ошибки в безопасности появятся версии 2.0.0.1, 2.0.1.1 и тд? Или она будет устранена в 2.0.10?


            1. SamDark
              13.07.2016 11:58

              Только вот в composer разве можно указать "2.0.0.*"?

              Да.


              В случае какой-то критической ошибки в безопасности появятся версии 2.0.0.1, 2.0.1.1 и тд?

              Да.