Команда Yii рада представить новую версию PHP фреймворка: Yii 2.0.14. В неё вошло более сотни улучшений и исправлений, включая исправления безопасности.
В релиз вошли несколько изменений, которые могут повлиять на уже работающие приложения. Эти изменения описаны в UPGRADE.md.
Спасибо сообществу Yii за помощь в выпуске этого обновления!
За процессом разработки можно следить, поставив звёздочку на GitHub. У нас есть много сообществ Yii, где вы можете попросить помощи или поделится своим опытом — мы и тысячи других пользователей Yii будем рады вашему участию.
Этот релиз знаменателен тем, что становится последним релизом в версии Yii 2.0, содержащим улучшения. Это значит, что мы сконцентрируем силы на разработке версии 2.1.x, в которую войдёт много новых улучшений, которые невозможно включить в ветку 2.0.х из-за ограничений по сохранению обратной совместимости. Несмотря на это, ветка 2.0.х будет получать исправления и улучшения безопасности. Сроки окончания поддержки 2.0.х будут объявлены вместе с релизом версии 2.1.
Убедитесь что версия фреймворка в composer.json прописана верно (~2.0.14) и вы не обновитесь на 2.1 случайно, когда он релизнется. 
Ниже мы рассмотрим самые интересные улучшения и исправления релиза. Полный список можно, как обычно, найти в CHANGELOG.
Масштабируемость и параллелизм
Проблемы масштабируемость и параллелизма часто отходят на второй план в начале разработки, но "всплывают" при росте бизнеса. В этом релизе мы нашли и исправили ошибку, которая касалась записи значений в базу данных и обновления ID сессии. При использовании master-slave репликации, yii\web\DbSession, yii\validators\UniqueValidator и yii\validators\ExistValidator могли обращаться к slave-серверу, в то время как корректнее было бы обращаться к master-серверу.
Улучшения валидаторов
В дополнение к сказанному выше, есть ещё несколько улучшений валидаторов.
Во-первых, ExistValidator теперь может проверять существование связей, если установлено свойство targetRelation. Это значит, что теперь можно описать следующую конфигурацию правил валидации:
public function rules()
{
    return [
        [['customer_id'], 'exists', 'targetRelation' => 'customer'],
    ];
}
public function getCustomer()
{
    return $this->hasOne(Customer::class, ['id' => 'customer_id']);
}Во-вторых FileValidator получил новое свойство minFiles указывающее минимальное количество файлов, которые должен загрузить пользователь.
Поведения
yii\behaviors\BlameableBehavior получил новое свойство defaultValue, которое используется в случае, когда ID пользователя не может быть определён. Такое обычно происходит, если модель ActiveRecord используется в консольном приложении.
В yii\behaviors\AttributeTypecastBehavior появилось новое свойство typecastAfterSave. Если его выставить в true значения атрибутов будут приводиться к указанным типам сразу после сохранения модели. Типы будут теми же, что и при загрузке модели из базы.
Было добавлено поведение yii\behaviors\CacheableWidgetBehavior. Оно автоматически кеширует контент виджета в соответствии с настройками зависимостей и времени валидности кеша. Например:
use yii\behaviors\CacheableWidgetBehavior;
public function behaviors()
{
  return [
      [
          'class' => CacheableWidgetBehavior::className(),
          'cacheDuration' => 0,
          'cacheDependency' => [
              'class' => 'yii\caching\DbDependency',
              'sql' => 'SELECT MAX(updated_at) FROM posts',
          ],
      ],
  ];
}Базы данных и ActiveRecord
Этот релиз добавляет много новых вещей, связанных с базами данных и ActiveRecord. Эти улучшения были реализованы силами Дмитрия Науменко, Сергея Макинена, Роберта Корульчыка, Николая Олейникова и других участников сообщества.
Объектный формат описания условия и пользовательские типы данных
Была реализована поддержка пользовательских типов данных. Добавлена поддержка JSON для MySQL и PostgreSQL, а также массивов для PostgreSQL. Чтобы достигнуть этого, внутренняя реализация Query Builder-а была существенно переработана, что также позволило реализовать поддержку описания условий в объектном формате. Поддержка привычного формата описания условий осталась без изменений. Кроме того, форматы можно комбинировать:
$query->andWhere(new OrCondition([
    new InCondition('type', 'in', $types),
    ['like', 'name', '%good%'],
    'disabled=false',
]));Это улучшение даёт несколько преимуществ. Во-первых, теперь команде разработчиков Yii проще поддерживать код, связанный с условиями. Это уже позволило добавить новое условие BetweenColumnsCondition, которое собирает SQL вроде 15 BETWEEN min_age AND max_age. К релизу 2.1, скорее всего, добавится поддержка новых типов условий. Во-вторых, теперь вы можете удобно создавать свои классы условий и использовать их в ваших проектах.
Гибкость Query Builder
Описанные выше изменения позволили принимать yii\db\Query в условиях везде, где можно было передавать yii\db\Expression ранее. Например:
$subquery = (new Query)
    ->select([new Expression(1)])
    ->from('tree')
    ->where(['parent_id' => 1, 'id' => new Expression('tree.parent_id']));
(new Query())
    ->from('tree')
    ->where(['or', 'parent_id = 1', $subquery])Upsert
Ещё одним существенным улучшением слоя работы с базами данных стала поддержка UPSERT — атомарной операции, которая создаёт новые записи, если они ещё не существуют (проверяется уникальный ключ), или изменяет существующие записи. К примеру, взгляните на следующий код:
Yii::$app->db->createCommand()->upsert('pages', [
    'name' => 'Front page',
    'url' => 'http://example.com/', // URL уникален
    'visits' => 0,
], [
    'visits' => new \yii\db\Expression('visits + 1'),
], $params)->execute();Он или создаёт новую страницу, или увеличит её счётчик посещений автоматически.
Schema builder и миграции
Schema builder теперь поддерживает типы "tiny integer" и "JSON", так что можно их использовать и в написании миграций:
$this->createTable('post', [
    'id' => $this->primaryKey(),
    'text' => $this->text(),
    'title' => $this->string()->notNull(),
    'attributes' => $this->json(),
    'status' => $this->tinyInteger(),
]);Ещё одно улучшение позволяет создавать и удалять представления (views):
$this->createView(
    'top_10_posts',
    (new \yii\db\Query())
        ->from('post')
        ->orderBy(['rating' => SORT_DESC])
        ->limit(10)
);
$this->dropView('top_10_posts');Новый API кеширования запросов
Ранее было возможно кешировать результат выполнения запроса, оборачивая его в метод Connection::cache(). Теперь есть возможность пользоваться более удобным API:
// На уровне query
(new Query())->cache(7200)->all();
// На уровне AR
User::find()->cache(7200)->all();Связи в Active Record
Active Record теперь сбрасывает связанные модели при изменении атрибута, на котором строится эта связь:
$item = Item::findOne(1);
echo $item->category_id; // 1
echo $item->category->name; // weapons
$item->category_id = 2;
echo $item->category->name; // toysОбработка ошибок
Цели логирования теперь выбрасывают исключение, когда не могут корректно экспортировать лог. Ранее они молча игнорировали ошибку, что могло привести к отсутствию логов, например, из-за неправильных прав на директорию.
Также теперь если HTTP заголовки уже были отправлены, при попытке отправить дополнительные будет выброшено исключение yii\web\HeadersAlreadySentException. Ранее эта ситуация молча игнорировалась.
Теперь возможно настроить обработчик ошибок Yii, изменив свойство $traceLine. Это может быть использовано, например, для генерации ссылок, которые могут быть открыты сразу в среде разработки. Настройка схожа с настройкой ссылок для панели отладки:
'components' => [
    // ...
    'errorHandler' => [
        'errorAction' => 'site/error',
        'traceLine' => '<a href="ide://open?url={file}&line={line}">{html}</a>',
    ],
],Используя свойство yii\web\ErrorAction::$layout, можно удобно изменить шаблон страницы ошибки:
class SiteController extends Controller
{
    // ...
    /**
     * @inheritdoc
     */
    public function actions()
    {
        return [
            'error' => [
                'class' => 'yii\web\ErrorAction',
                'layout' => 'error', // <-- HERE
        ],
    ];
}Безопасность
Было обнаружено и исправлено две уязвимости:
- CVE-2018-6009. Метод 
switchIdentity()вweb/User.phpне пересоздавал токен CSRF при смене пользователя. - CVE-2018-6010. В некоторых случаях было возможно получение отладочной информации из исключений, которые обрабатывал обработчик ошибок.
 
PHP 7.2
Yii 2.0.14 полностью поддерживает PHP 7.2. Мы поправили yii\filters\HttpCache, FileHelper::getExtensionsByMimeType() и yii\web\Session для нормальной работы на всех поддерживаемых версиях PHP.
Виджеты, формы, клиентский JavaScript
Тег <script> больше не содержит свойства type. Выглядит короче, и делает валидаторы HTML5 счастливыми :)
В полях, генерируемых Active Form и Html helper для аттрибутов модели, можно использовать автоматически сгенерированный placeholder:
<?=  Html::activeTextInput($post, 'title', ['placeholder' => true]) ?>На пути к поддержке Bootstrap 4, добавлена возможность указывать, какой элемент будет получать класс-отметку о наличии ошибки валидации:
<?php $form = ActiveForm::begin([
    'validationStateOn' => ActiveForm::VALIDATION_STATE_ON_INPUT, // или VALIDATION_STATE_ON_CONTAINER 
]) ?>Появилась возможность безопасно регистрировать JavaScript переменные из PHP кода:
class SiteController extends Controller
{
    public function actionIndex()
    {
        $this->view->registerJsVar('username', 'SilverFire');
        return $this->render('index');
    }
}Не смотря на то, что этот способ широко используется для передачи данных из PHP в JS, мы всё-таки советуем сначала попробовать воспользоваться возможностью HTML5 – data-атрибутами.
События
Павел Климов добавил поддержку масок для обработки событий, так что теперь можно подписаться на группу событий объектов, которые подпадают под маску.
Это может быть очень полезно для логирования и аудита Новый раздел документации содержит подробное описание и множество примеров использования этой возможности.
API, сериализация и фильтры
При настройке JsonResponseFormatter теперь можно указать тип контента:
'components' => [
    'response' => [
        // ...
        'formatters' => [
            \yii\web\Response::FORMAT_JSON => [
                'class' => \yii\web\JsonResponseFormatter::className(),
                'contentType' => \yii\web\JsonResponseFormatter::CONTENT_TYPE_HAL_JSON,
            ],
        ],
    ],
]Data filter теперь поддерживает условия lt,gt,lte и gte для yii\validators\DateValidator.
yii\base\ArrayableTrait::toArray() теперь поддерживает рекурсию в свойствах $fields и $expand. Запросы к REST APIs с expand могут быть описаны как extra1.extra2 и это будет значить, что нужно развернуть extra1 в первоначальном наборе данных, а затем extra2 в extra1. То есть теперь возможны запросы вроде http://localhost/comments?expand=post.author.
Теперь проще реализовывать поддержку своих заголовков для аутентификации, используя yii\filters\auth\HttpHeaderAuth.
В случае, когда вам нужно сериализировать ошибки валидации в JSON, вы можете использовать новый метод \yii\helpers\Json::errorSummary().
Консоль
Для консольных приложений также появился удобный способ сериализации ошибок валидации моделей:
if (!$model->validate()) {
    echo "Model is not valid:\n";
    echo \yii\helpers\Console::errorSummary($model);
    return ExitCode::DATAERR;
}Улучшен скрипт автодополнения для bash и zsh. Теперь он поддерживает автодополнение для ./yii help.
Вызывая консольные команды, параметры можно указывать как в camelCase, так и kebab-case: --selfUpdate и --self-update будут считаться одним и тем же параметром.
Более того, в дополнение к --<option>=<value> появилась поддержка синтаксиса --<option> <value>.
Маршрутизация
Была добавлена поддержка короткого синтаксиса для описания метода в групповых правилах:
'components' => [
    'urlManager' => [
        // ...
        'rules' => [
            new GroupUrlRule([
                'prefix' => 'file',
                'rules' => [
                    'POST document' => 'document/create',
                ],
            ]),
    ],
],i18n
Был добавлен компонент yii\i18n\Locale с методом getCurrencySymbol(), который возвращает символ валюты в выбранной локали.
Helper'ы
В этом релизе сделаны некоторые интересные улучшения хэлперов.
Два новых метода yii\helpers\FileHelper:
findDirectories()– возвращает найденные директории и поддиректории по указанному пути. Этот метод работает схоже сfindFiles(), но ищет директории.unlink()– удаляет файл или симлинк кроссплатформенно. Как выяснилось, даже там есть особенности.
В yii\helpers\StringHelper добавился метод matchWildcard(), который делает то же самое, что и нативный метод fnmatch(), но с учётом особенностей операционной системы. Подтверждено, что нативная реализация даёт разные результаты на разных ОС.
Добавлен yii\helpers\IpHelper. Он предоставляет методы для определения версии IP адреса, проверки IP адреса или подсети на вхождение в другую подсеть, разворачивания IPv6 адреса до полного формата. Например:
if (!IpHelper::inRange($ip, '192.168.1.0/24')) {
    // deny access
}Контейнер DI
В контейнере появилась возможность переиспользовать описания в свойствах:
'container' => [
    'definitions' => [
        \console\models\TestService::class => [
            'class' => \console\models\TestService::class,
            'model' => Instance::of(\console\models\TestModel::class)
        ],
        \console\models\TestModel::class => [
            'class' => \console\models\TestModel::class,
            'property' => 20,
        ],
    ],
]В этом примере значением свойства model в классе TestService будет объект класса TestModel, сконфигурированный в соответствии с описанием.
Шаблоны приложений
В дополнение к некоторым незначительным улучшениям, шаблон basic получил поддержку Docker и Vagrant.
Подготовка к релизу 2.1
Чтобы упростить переход с 2.0 на 2.1, Brandon Kelly предложил отметить методы и классы, которые уже удалены в ветке 2.1 аннотацией @deprecated в ветке 2.0.х. Такую отметку получили:
yii\base\BaseObject::className()в пользу нативного синтаксиса::class, который не вызывает автозагрузку (поддерживается в PHP >=5.5);- Модули поддержки XCache и Zend data cache;
 - Метод 
yii\BaseYii::powered(); yii\base\InvalidParamExceptionв пользуyii\base\InvalidArgumentException;yii\BaseYii::trace()в пользуyii\BaseYii::debug().
Код, который использует эти методы, будет продолжать работать, но IDE будет подсвечивать его как устаревший.
Комментарии (56)

evetrov
19.02.2018 12:30+1ИМХО самое важное из всего что тут написано — плохие новости:
прекратится поддержка yii 2.0 и придется перелопачивать половину проекта (а может и больше) из-за потери обратной совместимости.
Yii для себя выбирал именно по причине стабильности (после перехода с 1 на 2 версию он до сих пор работает нормально без проблем при обновлениях). А теперь вот какой подарок. И фирмы у которых большие проекты скорее всего будут откладывать переход на yii2.1 до последнего (многие на 1 версии еще живут) ибо бизнесу нужна стабильность и не самые свежие обновления. А ко мне будут требования знаний и 2.0 и 2.1 версий.
Друзья, сделайте что-нибудь что бы переход на 2.1 был не столь болезненным. Спасибо.
SamDark
19.02.2018 12:38+1Уже делаем. В 2.0 всё что нужно помечается как
@deprecated, пишется попутно UPGRADE. Ну и это не 1.1 > 2.0. Не такие страшные изменения будут.

SamDark
19.02.2018 12:39+1Стабильность — это хорошо, но 2.0 уже 4 года. Пора.

GoodGod
19.02.2018 19:13А вы не хотите перейти на эволюционную модель обновлений? А то честно говоря не догоняю чего жать — либо релизов раз в 4 года, либо чаще, но меньше изменений.

SamDark
19.02.2018 19:43+1Так это она и есть в лайт-виде. Это будет не 1.1 > 2.0 когда надо переписать всё, но и не минорный релиз, который можно просто накатить.

zelenin
19.02.2018 12:40+2прекратится поддержка yii 2.0 и придется перелопачивать половину проекта (а может и больше) из-за потери обратной совместимости.
нет, написано, что в будущем (пока неизвестно когда) прекратится поддержка продукта, которому уже 4 года. По темпам разработки 2.1 это еще как минимум год, да и после релиза 2.1 не думаю, что поддержку сразу закроют.

SilverFire Автор
19.02.2018 12:43+1Добавлю, что в отличии от перехода 1.1 > 2.0, обновление 2.0 > 2.1 – это эволюционное, а не революционное изменение. Основные цели релиза:
- отказ от поддержки устаревших версий PHP, которые мешают использованию новых возможностей в коде ядра и усложняют поддержку проекта в долгосрочной перспективе, включая вопросы безопасности.
 - вынос из ядра в отдельные пакеты опциональных составляющих вроде jQuery, DI.
 - архитектурные улучшения и изменение публичного API там, где это требуется для реализации новых возможностей.
 
Мы подготовим детальный гайд по обновлению приложения, чтобы процесс был однозначен и понятен.

sanchezzzhak
19.02.2018 16:03Очень минимальные изменение, у меня ушло на весь проект 15 минут для замены Object на BaseObject
Я делал так: Открыл старый добрый тотал командр Alt+F7 поиск Object -> файлы на панель, далее F4 и через notepad++ быстренько подправил.
можно еще перекинуть все файлы на редактор и через поиск и замену «найти и заменить во всех открытых вкладках».
vtvz_ru
19.02.2018 16:33А если воспользоваться IDE PHPStorm, то можно провести поиск использования класса Object и везде поменять его на BaseObject.

sanchezzzhak
19.02.2018 17:04Можно, но когда аппарат не мощный приходятся использовать старую школу.

vtvz_ru
19.02.2018 18:33Вы правы. Но так как это моя основная работа — веб разработка, то на железо не скуплюсь. Окупается почти сразу

php7
20.02.2018 10:15и придется перелопачивать половину проекта (а может и больше) из-за потери обратной совместимости
Так получите за это деньги :)

SonicGD
19.02.2018 15:58Добавлена поддержка JSON для MySQL и PostgreSQL
В доке написано, что
After data population, the value from JSON column will be automatically decoded from JSON according to standard JSON decoding rules.
Это прям автоматом включится? То есть если у нас есть модели с jsonb полями и мы ожидаем что там текст, то после апдейта нам там начнут объекты приезжать?
SilverFire Автор
19.02.2018 19:24Да, упустили это. Спасибо, что заметили. Добавлено в UPGRADE.md

SonicGD
19.02.2018 19:31Увлекательно. Будет нехилым таким препятствием для обновления на 2.0.14.

SamDark
19.02.2018 19:43Для тех, кто уже успел JSON использовать — вполне вероятно. Мы не специально...

SonicGD
20.02.2018 08:01Видимо, придётся просто переопределить постгресовую ColumnSchema и убрать там эти касты.

VladimirAndreev
20.02.2018 12:20проще тогда уж в композере написать, что yii нужен версии 2.0.13.1 и запланировать миграцию на 2.1…

SonicGD
20.02.2018 13:26Можно и так, но для в целом и для 2.1 никто не мешает оставить переопределение, если переписывание логики не покажется рентабельным.

VladimirAndreev
20.02.2018 12:20а можно вопрос — а
нахзачем вы тогда это добавили в 2.0.* ветку то? да еще и без возможности отключения этого поведения?
или вы не понимаете, что все проекты, которые уже использовали json-поля, сломали этим обновлением? И если в геттерах еще как-то можно было проверять, а не массив ли там уже в поле, то в сеттере это поведение срабатывает в методе save() и все гарантированно падает, потому что JsonExpression расстраивается до exception, получив строку вместо массива, а преобразование выполнялось в местах разных, но самое позднее — это был метод beforeSave()…
SamDark
20.02.2018 13:47Так вышло. Протащить слом обратной совместимости 2.0.14 специально не планировали. Именно этот кейс не рассматривали, хотя надо было...
Фича в master было смёржена прилично заранее, мы просили всех проверить master за несколько недель до релиза и получили десяток подтверждений от довольно сложных проектов, что всё нормально.

BoShurik
20.02.2018 15:56Не рассматриваете вариант выпустить хотфикс, где отключите эту возможность? Все-таки минорный релиз не должен ломать BC

PaulZi
20.02.2018 18:34+1То что теперь не нужен JsonBehavior это, конечно, здорово, но могли бы уж точно предположить, что если пользователи ждут на входе строку, то выдавать в новой версии массив — совсем неправильно.

SilverFire Автор
20.02.2018 20:21-1Расскажу, как это было. PR с изменениями на пути к JSON – это более 90 комментов, дифф на 71 файл и овер 3800 строк кода. Там перемешалось много контекстов изменений: исправление одновременно несколько старых проблем, добавление нескольких новых возможностей, оптимизация API, реорганизация кода. Делить его на много мелких не получалось, так как все задачи в рамках этого PR – взаимосвязаны. Вычитать вдумчиво и целиком такой PR не деле оказалось отдельной сложной задачей, на которую не хватало концентрации ни у кого кроме SamDark, за что ему отдельное спасибо.
Я понимаю, что мы (в больше мере – я) косякнули, а этот коммент – лишь попытка оправдаться, но не судите строго. Лучше приходите на GitHub – там всегда не хватает рук и свежего взгляда :)

VladimirAndreev
20.02.2018 21:12Интересно, как они тестировали или как у них работало, если при json string падает с исключением AR::save()

DarkByte2015
19.02.2018 16:16Раз уже добавили tinyint в schema builder то почему бы не добавить и longtext?

deanar
19.02.2018 18:17+1Внимание обновляющимся
Похоже, что сломался методQuery::select(): если вызвать его повторно, то вместо переназначения полей произойдет слияние с удалением указанных ранее полей.
Подробнее в issue: https://github.com/yiisoft/yii2/issues/15676

SilverFire Автор
19.02.2018 18:55Спасибо за уведомление, исправлено в коммите 1b3526d8 и войдёт в патч-релиз 2.0.14.1

BeeR
19.02.2018 19:04+1В конце статьи написали что ::className() deprecated, но в примере с CachableWidgetBehavior используете ::className() вместо ::class.

SilverFire Автор
19.02.2018 19:05+1Привычка писать документацию и код с учётом того, что мы ещё поддерживаем PHP 5.4 :)

lllypynby
19.02.2018 19:05А есть идеи убрать всё что связано с HTML в отдельный пакет? Когда REST победит?

SamDark
19.02.2018 19:11Идеи посещали всякие. И эта тоже. В 2.1 не планируется. REST не победит для определённых задач никогда. И не должен.

lllypynby
22.02.2018 08:09Извеняюсь, я не очень правильно выразился. Я имею в виду всякие GridView, ListView, ActiveForm, которые в рамках REST приложение просто висят мёртвым грузом. И есть ли идеи уйти от Jquery к Vue, React, Angular?

alex6636
21.02.2018 10:50Написали бы лучше есчо один js-фреймворк!

SamDark
21.02.2018 12:36+3Горшочек, не вари!

alex6636
21.02.2018 13:28-3надень шляпу на йух

SamDark
21.02.2018 14:23+1Я про то, что JS-фреймворков уже достаточно. Смысла туда лезть с ещё одним никакого. Множество стилей кода, конкурентная среда. В PHP многие фреймворки стали неотличимы друг от друга. Это и хорошо и плохо одновременно и смысл пилить что-то своё, немного не как все, определённо есть. Авторы смотрят на соседей и лучше становятся от этого все фреймворки.

sergiobelya
21.02.2018 12:42+1Спасибо за релиз. Некоторые вещи приходилось раньше писать руками.
Создал тикет на обновление.
Есть вопрос насчёт @deprecated.
Будет ли
заменён на::className()
в генераторах Gii в ветках 2.0? Вижу, что в ветке 2.1 уже заменили github.com/yiisoft/yii2-gii/blob/2.1/src/generators/model/Generator.php::class
Или всё-таки, пока используется совместимость с php-5.4, будет использоваться устаревший метод?
          
 
Skycaptain
а разве обратно несовместимые изменения не предполагают изменения мажорной, а не минорной версии (я про 2.1.х)?
SilverFire Автор
У нас семвер сдвинут на одну позицию вправо, потому получается
2.<major>.<minor>.<patch>Skycaptain
т.е. текущая версия 2.0.1.4?
SilverFire Автор
Нет, 2.0.14.0
DarthLegiON
А какие там изменения обратно несовместимы?
SilverFire Автор
Список изменений, на которые нужно обратить внимание при обновлении описан в UPGRADE.md для 2.1. Перед релизом мы дополнительно структурируем этот список и напишем гайд по обновлению.
DarthLegiON
Я имел в виду версию 2.0.14, думал, что автор комментария имел в виду несовместимые изменения по 2.0.14 и предположил, что это уже не минорный релиз, который нужно помещать в 2.1.x. Запутался)
Skycaptain
все те, что привнесет отказ от поддержки php ниже 7.1