Yii3 успадкував від Yii2 назву й ідеї, але не код. Розробка триває приблизно з 2017 року, і за цей час монолітний фреймворк розібрали на кілька десятків Composer-пакетів під yiisoft/*, кожен зі своїм репозиторієм, версіонуванням і тестами: yiisoft/di, yiisoft/router, yiisoft/cache, yiisoft/validator, yiisoft/view, yiisoft/db, yiisoft/rbac, yiisoft/log, yiisoft/queue і так далі. Застосунок складається з них через yiisoft/config (composer-плагін, який зливає конфіги, надані самими пакетами, з вашими), а запускається через runner: yii-runner-http для класичного FPM, yii-runner-console для CLI, yii-runner-roadrunner для довгоживучого воркера.
Головна відмінність стосується того, звідки клас бере залежності. У Yii2 код сам іде по них: Yii::$app->db, Yii::$app->cache, Yii::$app->user, Yii::$app->getModule('admin'). Такий сервіс-локатор дає кожному класу неявний доступ до всього застосунку, а тест на цей клас вимагає піднятого Yii::$app із правильним конфігом. У Yii3 класу Yii не існує взагалі: контейнер yiisoft/di (PSR-11) збирає обʼєкти за визначеннями з конфігів і передає їх у конструктор, а yiisoft/injector уміє докласти сервіси в аргументи методу-екшена. Решта змін виростає звідти ж. Власні yii\web\Request/Response замінили PSR-7 повідомлення з PSR-17 фабриками, фільтри-поведінки контролера (AccessControl, VerbFilter) стали PSR-15 middleware, які вішаються на застосунок, групу маршрутів або окремий маршрут, а yii\base\Event поступився PSR-14 диспетчеру. Валідація переїхала в самостійний yiisoft/validator з правилами-обʼєктами й PHP-атрибутами, тож масив rules() у моделі більше не єдиний спосіб.
Зникла й магія yii\base\Component. Немає __get, який підхоплює getFoo() і властивості поведінок, немає й самих behaviors як універсального механізму: замість TimestampBehavior пишете явний код або підписуєтесь на подію в PSR-14. Багато класів стали імутабельними з withX()-методами, що прибирає цілий клас багів, коли один сервіс тихо переналаштував інший. yiisoft/active-record існує, але живе окремим пакетом і вимагає явного ConnectionInterface, тож звичка «модель сама знає про Yii::$app->db» більше не працює.
Питання «чи варто чекати» розпадається на два. Пакети Yii3 придатні до вживання поштучно і просто зараз: yiisoft/injector, yiisoft/arrays, yiisoft/html, yiisoft/validator, yiisoft/rbac, yiisoft/cache не тягнуть за собою фреймворк і спокійно ставляться в проєкт на Laravel чи Symfony, а yiisoft/db із окремими пакетами драйверів вийшов у стабільну 1.x. Повний стек (шаблон yiisoft/app, ActiveRecord, частина інтеграцій) станом на момент написання картки не має ні стабільного релізу, ні дати. Перед співбесідою відкрийте Packagist і подивіться на актуальні версії потрібних вам пакетів: цифра, названа вголос, справляє краще враження, ніж переказ старого блог-посту.
Проєкту на Yii2 очікування Yii3 не дає нічого: прямого шляху оновлення все одно не буде, це фактично переїзд на інший фреймворк, а роки чекання накопичать борг, який жоден реліз не спише. Гілка 2.0 тим часом виходить релізами 2.0.5x і лагодить сумісність із PHP 8.3-8.4, тож панікувати через саму версію фреймворка нема через що: дорожчає не Yii2, а екосистема навколо нього й пошук людей. Розумніше вже сьогодні писати доменну логіку без Yii::$app, ховати ActiveRecord за репозиторіями і брати залежності через конструктор (Yii2 це вміє через yii\di\Container). Такий код однаково добре переживе і Yii3, і будь-яку іншу зміну фреймворка, а тестується без піднятого застосунку вже зараз.
// --- Yii2: залежності шукає сам код через глобальний локатор ---
class PostController extends \yii\web\Controller
{
public function actionView(int $id): string
{
// Yii::$app зашитий у метод: підмінити кеш у тесті можна лише конфігом застосунку
$post = \Yii::$app->cache->getOrSet("post:{$id}", static fn () => Post::findOne($id), 300);
if ($post === null) {
throw new \yii\web\NotFoundHttpException();
}
return $this->render('view', ['post' => $post]); // рядок, а не HTTP-відповідь
}
}
// --- Yii3: ті самі залежності приносить контейнер ---
use Psr\Http\Message\ResponseInterface;
use Yiisoft\Cache\CacheInterface; // обгортка над PSR-16 з getOrSet()
use Yiisoft\Router\CurrentRoute;
use Yiisoft\Yii\View\Renderer\ViewRenderer; // пакет yiisoft/yii-view-renderer
final class PostController
{
public function __construct(
private readonly PostRepository $posts, // власний інтерфейс, ActiveRecord тут не видно
private readonly CacheInterface $cache,
private readonly ViewRenderer $view,
) {
}
// Класу Yii у Yii3 немає; аргументи екшена теж збирає контейнер (yiisoft/injector)
public function view(CurrentRoute $route): ResponseInterface
{
$id = (int) $route->getArgument('id'); // маршрут описаний у config/common/routes.php
$post = $this->cache->getOrSet(
"post:{$id}",
fn () => $this->posts->findById($id),
300,
);
// доменний виняток, який middleware перетворить на 404
$post ?? throw new PostNotFound($id);
return $this->view->render('view', ['post' => $post]); // одразу PSR-7 відповідь
}
}
Дайте одне речення, за яким видно, що ви бачили обидва: «У Yii2 залежність шукає код через `Yii::$app`, у Yii3 залежність приносить контейнер у конструктор - решта відмінностей (PSR-15, PSR-14, окремі пакети) виростає саме з цього». Далі одразу назвіть статус: пакети стабільні поштучно, стек - ні, тому на проді у нас Yii2 і код, який не тримається за `Yii::$app`.