Команда 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 будет подсвечивать его как устаревший.