Мы постарались задокументировать все возможные критические изменения. Поскольку некоторые из них находятся в малоизвестных частях фреймворка, только часть этих изменений может повлиять на ваше приложение. Приблизительное время обновления: 30 минут.
Требуется PHP 8.0.2
Минимальная поддерживаемая версия PHP Laravel теперь 8.0.2.
PHP Типы возвращаемого значения
Язык начинает требовать определения типа возвращаемого значения для методов, таких как offsetGet
, offsetSet
, и т.п. По этой причине Laravel 9 реализовал эти типы в своей кодовой базе. Как правило, это не должно влиять на написанный пользователем код, однако, если вы переопределяете один из этих методов, расширяя базовые классы Laravel, то вам нужно будет добавить типы возвращаемых значений в код вашего приложения или пакета:
count(): int
getIterator(): Traversable
getSize(): int
jsonSerialize(): array
offsetExists($key): bool
offsetGet($key): mixed
offsetSet($key, $value): void
offsetUnset($key): void
Кроме того, типа возвращаемых значений были добавлены в методы, реализующие PHP SessionHandlerInterface
. Опять же, маловероятно, что это изменение повлияет на ваше приложение или пакет:
open($savePath, $sessionName): bool
close(): bool
read($sessionId): string|false
write($sessionId, $data): bool
destroy($sessionId): bool
gc($lifetime): int
Зависимости Composer
Вы должны обновить следующие зависимости в файле composer.json вашего приложения:
laravel/framework к ^9.0
nunomaduro/collision к ^6.0
Кроме того, замените facade/ignition
на "spatie/laravel-ignition": "^1.0"
в этом же файле.
Не забудьте также проверить все сторонние пакеты, используемые в вашем приложении, и убедитесь, что они поддерживают Laravel 9.
Контракт Application
Метод storagePath
интерфейса Illuminate\Contracts\Foundation\Application
был обновлен для использования аргумента $path
. Если вы реализуете этот интерфейс, вы должны соответствующим образом обновить свою реализацию:
public function storagePath($path = '');
Метод ignore
обработчика исключений
Метод ignore
теперь public
вместо protected
. Этот метод не включен в дефолтный каркас приложения. Однако, если вы задавали этот метод вручную, то должны обновить его видимость до public
:
public function ignore(string $class);
Blade — Ленивые коллекции и переменная $loop
При итерации экземпляра LazyCollection
в Blade-шаблоне переменная $loop
больше недоступна, так как доступ к этой переменной приводит к полной загрузке LazyCollection
в память, что делает использование ленивых коллекций бессмысленным в данном сценарии.
Collections — Контракт Enumerable
Контракт Illuminate\Support\Enumerable
теперь определяет метод sole
. Если вы реализуете этот интерфейс вручную, то обновите свою реализацию, добавив новый метод:
public function sole($key = null, $operator = null, $value = null);
Контракт Container
Контракт Illuminate\Contracts\Container\Container
получил два новых метода: scoped
и scopedIf
. Если вы реализуете этот контракт вручную, то обновите свою реализацию, добавив их.
Контракт ContextualBindingBuilder
Контракт Illuminate\Contracts\Container\ContextualBindingBuilder
получил новый метод giveConfig
. Если вы реализуете этот контракт вручную, то обновите свою реализацию, добавив его.
public function giveConfig($key, $default = null);
Postgres конфигурация «Schema»
Параметр конфигурации schema
, используемый для настройки путей поиска соединений Postgres в файле конфигурации config/database.php
вашего приложения, должен быть переименован в search_path
.
Кастомные касты и null
В предыдущих версиях Laravel метод set
кастомных кастов не вызывался, если для атрибута приведения было установлено значение null
. Однако такое поведение не соответствовало документации Laravel. В Laravel 9.x метод set
класса каста будет вызываться с null
в аргументе $value
. Поэтому вы должны убедиться, что ваши касты смогут правильно отработать этот сценарий:
/** * Prepare the given value for storage. * * @param \Illuminate\Database\Eloquent\Model $model * @param string $key * @param AddressModel $value * @param array $attributes * @return array */ public function set($model, $key, $value, $attributes) { if (! $value instanceof AddressModel) { throw new InvalidArgumentException('The given value is not an Address instance.'); } return [ 'address_line_one' => $value->lineOne, 'address_line_two' => $value->lineTwo, ]; }
Методы Belongs To Many firstOrNew
, firstOrCreate
и updateOrCreate
Методы отношения belongsToMany: firstOrNew
, firstOrCreate
и updateOrCreate
принимают массив атрибутов в качестве первого аргумента. В предыдущих версиях Laravel этот массив сравнивался со «сводной»/промежуточной таблицей для существующих записей.
Однако такое поведение было неожидаемым и, как правило, нежелательным. Вместо этого эти методы теперь сравнивают массив атрибутов с таблицей связанной модели:
$user->roles()->updateOrCreate([ 'name' => 'Administrator', ]);
Кроме того, метод firstOrCreate
теперь принимает массив $values
в качестве второго аргумента. Этот массив будет объединен с первым аргументом метода ( $attributes
) при создании связанной модели, если она еще не существует. Это изменение делает этот метод совместимым с методом firstOrCreate
, предлагаемыми другими типами отношений:
$user->roles()->firstOrCreate([ 'name' => 'Administrator', ], [ 'created_by' => $user->id, ]);
Метод touch
Теперь метод touch
принимает атрибут. Если вы ранее переопределяли этот метод, то вам следует обновить его, добавив новый аргумент:
public function touch($attribute = null);
Контракт Encrypter
В контракт Illuminate\Contracts\Encryption\Encrypter
добавлен метод getKey
. Если вы реализуете этот интерфейс вручную, то обновить свою реализацию:
public function getKey();
Фасады — метод getFacadeAccessor
Метод getFacadeAccessor
всегда должен возвращать ключ привязки контейнера. В предыдущих версиях Laravel этот метод мог возвращать экземпляр объекта, однако такое поведение больше не поддерживается. Если вы создавали свои собственные фасады, то убедитесь, что этот метод возвращает строку привязки контейнера:
/** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return Example::class; }
Переменная окружения FILESYSTEM_DRIVER
Переменная FILESYSTEM_DRIVER
была переименована в FILESYSTEM_DISK
для более точного отражения ее назначения. Это изменение затрагивает только каркас приложения, однако вы можете обновить переменные окружения своего приложения, если хотите.
Flysystem 3.x
Laravel 9 переходит с Flysystem 1.x на 3.x. Под капотом Flysystem поддерживает все методы работы с файлами, предоставляемые фасадом Storage
. В связи с этим в вашем приложении могут потребоваться некоторые изменения; однако мы постарались сделать этот переход максимально плавным.
Требования
Перед использованием драйверов S3 или SFTP необходимо установить соответствующий пакет через Composer:
Amazon S3
composer require --with-all-dependencies league/flysystem-aws-s3-v3 "^3.0"
SFTP
composer require league/flysystem-sftp-v3 "^3.0"
Перезапись существующих файлов
Операции записи put
, write
, writeStream
теперь перезаписывают существующие файлы по умолчанию. Если вы не хотите перезаписывать существующие файлы, то вам следует вручную проверить существование файла перед выполнением операции записи.
Чтение несуществующих файлов
Попытка чтения из несуществующего файла теперь возвращает null
. В предыдущих версиях Laravel выбрасывалось исключение Illuminate\Contracts\Filesystem\FileNotFoundException
.
Удаление несуществующих файлов
Попытка применения метода delete
к несуществующему файлу теперь возвращает true
.
Кэшированные адаптеры
Flysystem больше не поддерживает «cached adapters
». Поэтому они были удалены из Laravel, и любая соответствующая конфигурация (например, ключ cache
в конфигурации диска) может быть удалена.
Кастомные файловые системы
Небольшие изменения были внесены в шаги, необходимые для регистрации кастомных драйверов файловой системы. Если вы создавали свои собственные драйверы или использовали пакеты, создающие таковые, то необходимо обновить свой код и зависимости. Например, в Laravel 8.x кастомный драйвер файловой системы может быть зарегистрирован следующим образом:
use Illuminate\Support\Facades\Storage; use League\Flysystem\Filesystem; use Spatie\Dropbox\Client as DropboxClient; use Spatie\FlysystemDropbox\DropboxAdapter; Storage::extend('dropbox', function ($app, $config) { $client = new DropboxClient( $config['authorization_token'] ); return new Filesystem(new DropboxAdapter($client)); });
А в Laravel 9.x колбэк метода Storage::extend
должен возвращать экземпляр Illuminate\Filesystem\FilesystemAdapter
напрямую:
use Illuminate\Filesystem\FilesystemAdapter; use Illuminate\Support\Facades\Storage; use League\Flysystem\Filesystem; use Spatie\Dropbox\Client as DropboxClient; use Spatie\FlysystemDropbox\DropboxAdapter; Storage::extend('dropbox', function ($app, $config) { $adapter = new DropboxAdapter(new DropboxClient( $config['authorization_token'] );); return new FilesystemAdapter( new Filesystem($adapter, $config), $adapter, $config ); });
Хелперы
data_get
& итерируемые объекты
Раньше хелпер data_get
можно было использовать для извлечения вложенных данных только из массивов и экземпляров Collection
. А теперь этот хелпер может извлекать вложенные данные из всех итерируемых объектов.
str
Laravel 9 теперь включает глобальныйх хелпер str
. Если вы задаете глобальный хелпер str
в своем приложении, то вы должны переименовать или удалить его, чтобы он не конфликтовал с основным Laravel-хелпером.
Методы when/unless
Как вы, возможно, знаете, методы when
и unless
предлагаются различными классами по всему фреймворку. Эти методы можно использовать для условного выполнения действия, если логическое значение первого аргумента метода оценивается как true
или false
:
$collection->when(true, function ($collection) { $collection->merge([1, 2, 3]); });
Таким образом, в предыдущих версиях Laravel передача замыкания методам when
или unless
означала, что условная операция всегда будет выполняться, поскольку нечеткое сравнение с объектом замыкания (или любым другим объектом) всегда оценивается как true
. Это часто приводило к неожиданным результатам, поскольку разработчики ожидали, что результат замыкания будет использоваться в качестве логического значения, определяющего, выполняется ли условное действие.
В Laravel 9 любые замыкания, переданные методам when
или unless
, будут выполнены, а значение, возвращаемое замыканием, будет считаться логическим значением, используемым методами when
и unless
:
$collection->when(function ($collection) { // Это замыкание выполняется... return false; }, function ($collection) { // Не выполняется, так как первое замыкание вернуло false... $collection->merge([1, 2, 3]); });
HTTP Client
Дефолтный таймаут
HTTP-клиент теперь имеет дефолтный тайм-аут в 30 секунд. Другими словами, если сервер не отвечает в течение 30 секунд, то будет выброшено исключение. Раньше дефолтного таймаута не было, из-за чего запросы иногда подвисали на неопределенное время.
Если вы хотите указать более длительный тайм-аут для запроса, то можете сделать это с помощью метода timeout
:
$response = Http::timeout(120)->get(...);
Symfony Mailer
Одно из самых больших изменений в Laravel 9 это переход от SwiftMailer, который не поддерживается с декабря 2021 года, к Symfony Mailer. Однако мы постарались сделать этот переход как можно более плавным для ваших приложений. Внимательно ознакомьтесь со списком изменений ниже, чтобы убедиться, что ваше приложение полностью совместимо.
Требования
Пакет aws/aws-sdk-php
больше не потребуется при использовании Amazon SES, и его можно удалить, если он не требуется для других частей вашего приложения. Вместо этого вашему приложению нужен пакет symfony/amazon-mailer
:
composer require symfony/amazon-mailer
Чтобы продолжить использование Mailgun, вашему приложению потребуется пакет symfony/mailgun-mailer
:
composer require symfony/mailgun-mailer
Пакет wildbit/swiftmailer-postmark
должен быть удален из вашего приложения. Вместо этого вашему приложению нужен пакет symfony/postmark-mailer
:
composer require symfony/postmark-mailer
Обновленные типы возвращаемых значений
Методы send
, html
, text
и plain
больше не возвращают количество получателей. Вместо этого возвращается экземпляр Illuminate\Mail\SentMessage
. Этот объект содержит экземпляр Symfony\Component\Mailer\SentMessage
, доступный через метод getSymfonySentMessage
.
Переименованные «Swift» методы
Различные методы, связанные со SwiftMailer, некоторые из которых не были задокументированы, сейчас переименованы в их аналоги Symfony Mailer. Например, метод withSwiftMessage
переименован в withSymfonyMessage
:
// Laravel 8.x... $this->withSwiftMessage(function ($message) { $message->getHeaders()->addTextHeader( 'Custom-Header', 'Header Value' ); }); // Laravel 9.x... use Symfony\Component\Mime\Email; $this->withSymfonyMessage(function (Email $message) { $message->getHeaders()->addTextHeader( 'Custom-Header', 'Header Value' ); });
Пожалуйста, внимательно изучите документацию Symfony Mailer для всех возможных взаимодействий с объектом Symfony\Component\Mime\Email
.
Нижерасположенный список содержит более подробный обзор переименованных методов. Многие из них являются низкоуровневыми, используемыми для прямого взаимодействия со SwiftMailer/Symfony Mailer, поэтому могут не использоваться в большинстве приложений Laravel:
Message::getSwiftMessage(); Message::getSymfonyMessage(); Mailable::withSwiftMessage($callback); Mailable::withSymfonyMessage($callback); MailMessage::withSwiftMessage($callback); MailMessage::withSymfonyMessage($callback); Mailer::getSwiftMailer(); Mailer::getSymfonyTransport(); Mailer::setSwiftMailer($swift); Mailer::setSymfonyTransport(TransportInterface $transport); MailManager::createTransport($config); MailManager::createSymfonyTransport($config);
Прокси-методы Illuminate\Mail\Message
Обычно отсутствующие методы в Illuminate\Mail\Message
проксировались базовому Swift_Message
. Но теперь, вместо этого, они будут проксироваться в Symfony\Component\Mime\Email
. Таким образом, любой код, который ранее полагался на SwiftMailer, должен быть обновлен до соответствующих аналогов Symfony Mailer.
Опять же, многие приложения могут и не взаимодействовать с этими методами, поскольку они не описаны в документации Laravel:
// Laravel 8.x... $message ->setFrom('taylor@laravel.com') ->setTo('example@example.org') ->setSubject('Order Shipped') ->setBody('<h1>HTML</h1>', 'text/html') ->addPart('Plain Text', 'text/plain'); // Laravel 9.x... $message ->from('taylor@laravel.com') ->to('example@example.org') ->subject('Order Shipped') ->html('<h1>HTML</h1>') ->text('Plain Text');
Генерируемые идентификаторы сообщений
SwiftMailer предлагал возможность задать собственный домен для включения в сгенерированные идентификаторы сообщений с помощью параметра конфигурации mime.idgenerator.idright
. Но это не поддерживается Symfony Mailer. Вместо этого он автоматически генерирует идентификатор сообщения на основе отправителя.
Принудительные переподключения
Теперь больше невозможно принудительно переподключиться к транспорту (например, когда мейлер работает через демона). Вместо этого Symfony Mailer попытается автоматически переподключиться к транспорту и выбросит исключение, если это не удастся.
Параметры SMTP-stream
Определение параметров потока для SMTP больше не поддерживается. Вместо этого вы должны задать соответствующие параметры непосредственно в конфигурации, если они поддерживаются. Например, чтобы отключить проверку пира TLS:
'smtp' => [ // Laravel 8.x... 'stream' => [ 'ssl' => [ 'verify_peer' => false, ], ], // Laravel 9.x... 'verify_peer' => false, ],
Чтобы узнать больше о доступных параметрах конфигурации, ознакомьтесь с документацией Symfony Mailer.
Несмотря на вышеприведенный пример, не рекомендуется отключать проверку SSL, поскольку это создает возможность атак типа «man-in-the-middle».
SMTP auth_mode
Определение SMTP auth_mode
в файле конфигурации mail
больше не требуется. Режим аутентификации будет автоматически согласован между Symfony Mailer и SMTP-сервером.
Неудавшиеся отправления
Теперь невозможно получить список получателей, которым не получилось отправить письмо. Вместо этого, , если сообщение не будет отправлено, то выбросится исключение Symfony\Component\Mailer\Exception\TransportExceptionInterface
. Вместо того, чтобы полагаться на получение невалидных адресов электронной почты после отправки сообщения, мы рекомендуем вам проверять их перед отправкой сообщения.
Пакеты
Каталог lang
В новых приложениях Laravel папка resources/lang
теперь находится в корневой папке проекта (lang
). Если ваш пакет публикует языковые файлы в этот каталог, то вы должны убедиться, что ваш пакет использует app()->langPath()
, а не захардкоженный старый путь.
Очереди
Библиотека opis/closure
Зависимость Laravel от opis/closure
была заменена на laravel/serializable-closure
. Это не должно привести к каким-либо критическим изменениям в вашем приложении, если только вы не взаимодействуете с библиотекой opis/closure
напрямую. Кроме того, были удалены классы Illuminate\Queue\SerializableClosureFactory
и Illuminate\Queue\SerializableClosure
, ранее объявленные устаревшими. Если вы взаимодействуете с библиотекой opis/closure
напрямую или используете любой из удаленных классов, то вместо этого используйте Laravel Serializable Closure.
Метод failed
провайдера Failed Job
Метод flush
, заданный интерфейсом Illuminate\Queue\Failed\FailedJobProviderInterface
, теперь принимает аргумент $age
, определяющий, сколько в днях должно просуществовать невыполненное задание, прежде чем оно будет сброшено командой queue:flush
. Если вы реализуете вручную интерфейс FailedJobProviderInterface
, то убедитесь, что ваша реализация обновлена для использования нового аргумента:
public function flush($age = null);
Сессия
Метод getSession
Класс Symfony\Component\HttpFoundaton\Request
, расширяемым Laravel-классом Illuminate\Http\Request
, предлагает метод getSession
для получения текущего обработчика хранилища сессии. Этот метод не задокументирован, так как большинство приложений Laravel взаимодействуют с сессией через метод session
.
Ранее метод getSession
возвращал экземпляр Illuminate\Session\Store
или null
. Однако из-за того, что в релизе Symfony 6.x используется тип возвращаемого значения Symfony\Component\HttpFoundation\Session\SessionInterface
, теперь метод getSession
корректно возвращает SessionInterface
или выбрасывает исключение \Symfony\Component\HttpFoundation\Exception\SessionNotFoundException
, если сессия недоступна.
Тестирование
Метод assertDeleted
Все вызовы метода assertDeleted
должны быть заменены на assertModelMissing
.
Доверенные прокси
Если вы обновляете свой проект c Laravel 8 до Laravel 9, импортируя имеющийся код в совершенно новый каркас приложения на Laravel 9, то вам может потребоваться обновить мидлвар «trusted proxy
».
В файле app/Http/Middleware/TrustProxies.php
обновите use Fideloper\Proxy\TrustProxies as Middleware
до use Illuminate\Http\Middleware\TrustProxies as Middleware
.
Валидация
Метод validated
из FormRequest
Метод validated
теперь принимает аргументы $key
и $default
. Если вы вручную переопределяли этот метод, то вам следует обновить его:
public function validated($key = null, $default = null)
Правило password
Правило password
, проверяющее соответствие введенного пароля текущему, было переименовано в current_password
.
Непроверенные ключи массива
В предыдущих версиях Laravel приходилось вручную указывать валидатору исключать непроверенные ключи массива из проверенных данных, особенно в сочетании с правилом array
, в котором не указан список разрешенных ключей.
В Laravel 9 непроверенные ключи теперь всегда исключаются из проверенных данных, даже если в правиле array
не указаны разрешенные ключи. Как правило, такое поведение и является наиболее ожидаемым. Предыдущий метод excludeUnvalidatedArrayKeys
был добавлен в Laravel 8 только как временная мера для сохранения обратной совместимости.
Хотя это и не рекомендуется, но вы можете вернуться к предыдущего поведению, как в Laravel 8.x, вызвав новый метод includeUnvalidatedArrayKeys
в методе boot
одного из провайдеров вашего приложения:
use Illuminate\Support\Facades\Validator; /** * Register any application services. * * @return void */ public function boot() { Validator::includeUnvalidatedArrayKeys(); }
Разное
Мы также рекомендуем вам просмотреть все изменения в репозитории laravel/laravel на GitHub. Некоторые из этих изменений описаны в данном руководстве по обновлению, некоторые, такие как изменения в файлах конфигурации или комментарии, не включены. Вы можете легко просмотреть их с помощью инструмента сравнения GitHub и выбрать, какие обновления важны именно для вас.
Автор: Laravel
Перевод: Алексей Широков
Наш Телеграм-канал — следите за новостями о Laravel.