Команда 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