升级指南
高影响的变化
中等影响的变化
从8.x升级到9.0
估计的升级时间:30分钟
{tip} 我们试图记录每一个可能是突破性的变化。由于这些突破性的变化有些是在框架的不明显的部分,所以只有一部分的变化可能真正影响到你的应用程序。想节省时间吗? 你可以使用Laravel Shift来帮助你的应用程序自动升级.
更新依赖关系
影响的可能性:高
需要PHP 8.0.2
Laravel现在需要PHP 8.0.2或更高版本。
Composer 依赖
您应该在应用程序的 composer.json
文件中更新以下依赖项:
-
laravel/framework
为^9.0
-
nunomaduro/collision
为^6.0
此外,请将应用程序的 composer.json
文件中的 facade/ignition
替换为 "spatie/laravel-ignition": "^1.0"
。
此外,以下官方扩展新的主要版本已支持 Laravel 9.x。 如果需要使用,您应该在升级前阅读他们各自的升级指南:
- Vonage通知通道(v3.0) (取代Nexmo)
最后, 检查你的应用程序所使用的任何其他第三方扩展,并检查他们最新的版本是否已经支持了Laravel 9。
PHP返回类型
PHP 开始过渡到要求在 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
应用
Application
契约
影响的可能性:低
Illuminate\Contracts\Foundation\Application
接口的 storagePath
方法已更新为接受 $path
参数。 如果你正在实现这个接口,你应该相应地更新你的实现:
public function storagePath($path = '');
异常处理程序ignore
方法
影响的可能性:低
异常处理程序的 ignore
方法现在是 public
而不是 protected
。 此方法不包含在默认应用程序框架中; 但是,如果您手动定义了此方法,则应将其可见性更新为public
:
public function ignore(string $class);
Blade
懒惰集合 & $loop
变量
影响的可能性:低
当在Blade模板中迭代一个LazyCollection
实例时,$loop
变量不再可用,因为访问该变量会导致整个LazyCollection
被加载到内存中,因此在这种情况下,懒惰集合的使用毫无意义。
集合
Enumerable
契约
影响的可能性:低
Illuminate\Support\Enumerable
契约现在定义了 sole
方法。 如果您手动实现此接口,则应更新您的实现以反映此新方法:
public function sole($key = null, $operator = null, $value = null);
reduceWithKeys
方法
reduceWithKeys
方法已被删除,因为reduce
方法提供了同样的功能。你可以简单地更新你的代码,调用reduce
而不是reduceWithKeys
。
reduceMany
方法
reduceMany
方法已被重新命名为reduceSpread
,以便与其他类似方法的命名一致。
容器
Container
契约
影响的可能性:非常低
Illuminate\Contracts\Container\Container
契约有两个方法定义:scoped
和 scopedIf
。 如果您手动实施此合同,您应该更新您的实施以反映这些新方法。
The ContextualBindingBuilder
Contract
影响的可能性:非常低
Illuminate\Contracts\Container\ContextualBindingBuilder
契约现在定义了 giveConfig
方法。 如果您手动实现此接口,则应更新您的实现以反映此新方法:
public function giveConfig($key, $default = null);
数据库
Postgres "Schema" 配置
影响的可能性:中等
在你的应用程序的config/database.php
配置文件中用于配置Postgres连接搜索路径的schema
配置选项应该改名为search_path
。
结构生成器 registerCustomDoctrineType
方法
影响的可能性:低
registerCustomDoctrineType
方法已从 Illuminate\Database\Schema\Builder
类中移除。 你可以使用 DB
门面上的 registerDoctrineType
方法,或者在 config/database.php
配置文件中注册自定义的 Doctrine 类型。
Eloquent
自定义类型转换 & null
影响的可能性:中等
在之前的 Laravel 版本中,如果 cast 属性设置为 null
,则不会调用自定义转换类的 set
方法。 但是,这种行为与 Laravel 文档不一致。 在 Laravel 9.x 中,cast 类的 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版本中, 这个属性数组是与现有记录的 "pivot"/中间表进行比较.
然而, 这种行为是意料之外的, 通常是不需要的. 取而代之的是, 这些方法现在将属性数组与相关模型的表进行比较:
$user->roles()->updateOrCreate([ 'name' => 'Administrator',]);
此外,firstOrCreate
方法现在接受 $values
数组作为其第二个参数。 如果相关模型尚不存在,则此数组将在创建相关模型时与方法的第一个参数 ($attributes
) 合并。 此更改使此方法与其他关系类型提供的 firstOrCreate
方法一致:
$user->roles()->firstOrCreate([ 'name' => 'Administrator',], [ 'created_by' => $user->id,]);
touch
方法
影响的可能性:低
The touch
method now accepts an attribute to touch. If you were previously overwriting this method, you should update your method signature to reflect this new argument:
public function touch($attribute = null);
Encryption
The Encrypter Contract
影响的可能性:低
The Illuminate\Contracts\Encryption\Encrypter
contract now defines a getKey
method. If you are manually implementing this interface, you should update your implementation accordingly:
public function getKey();
Facades
The getFacadeAccessor
Method
影响的可能性:低
The getFacadeAccessor
method must always return a container binding key. In previous releases of Laravel, this method could return an object instance; however, this behavior is no longer supported. If you have written your own facades, you should ensure that this method returns a container binding string:
/** * Get the registered name of the component. * * @return string */protected static function getFacadeAccessor(){ return Example::class;}
Filesystem
The FILESYSTEM_DRIVER
Environment Variable
影响的可能性:低
The FILESYSTEM_DRIVER
environment variable has been renamed to FILESYSTEM_DISK
to more accurately reflect its usage. This change only affects the application skeleton; however, you are welcome to update your own application's environment variables to reflect this change if you wish.
The "Cloud" Disk
影响的可能性:低
The cloud
disk configuration option was removed from the default application skeleton in November of 2020. This change only affects the application skeleton. If you are using the cloud
disk within your application, you should leave this configuration value in your own application's skeleton.
Flysystem 3.x
Likelihood Of Impact: High
Laravel 9.x has migrated from Flysystem 1.x to 3.x. Under the hood, Flysystem powers all of the file manipulation methods provided by the Storage
facade. In light of this, some changes may be required within your application; however, we have tried to make this transition as seamless as possible.
Driver Prerequisites
Before using the S3 or SFTP drivers, you will need to install the appropriate package via the Composer package manager:
- Amazon S3:
composer require --with-all-dependencies league/flysystem-aws-s3-v3 "^3.0"
- SFTP:
composer require league/flysystem-sftp-v3 "^3.0"
Overwriting Existing Files
Write operations such as put
, write
, writeStream
now overwrite existing files by default. If you do not want to overwrite existing files, you should manually check for the file's existence before performing the write operation.
Reading Missing Files
Attempting to read from a file that does not exist now returns null
. In previous releases of Laravel, an Illuminate\Contracts\Filesystem\FileNotFoundException
would have been thrown.
Deleting Missing Files
Attempting to delete
a file that does not exist now returns true
.
Cached Adapters
Flysystem no longer supports "cached adapters". Thus, they have been removed from Laravel and any relevant configuration (such as the cache
key within disk configurations) can be removed.
Custom Filesystems
Slight changes have been made to the steps required to register custom filesystem drivers. Therefore, if you were defining your own custom filesystem drivers, or using packages that define custom drivers, you should update your code and dependencies.
For example, in Laravel 8.x, a custom filesystem driver might be registered like so:
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));});
However, in Laravel 9.x, the callback given to the Storage::extend
method should return an instance of Illuminate\Filesystem\FilesystemAdapter
directly:
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 );});
Helpers
The data_get
Helper & Iterable Objects
影响的可能性:非常低
Previously, the data_get
helper could be used to retrieve nested data on arrays and Collection
instances; however, this helper can now retrieve nested data on all iterable objects.
The str
Helper
影响的可能性:非常低
Laravel 9.x now includes a global str
helper function. If you are defining a global str
helper in your application, you should rename or remove it so that it does not conflict with Laravel's own str
helper.
The when
/ unless
Methods
影响的可能性:中等
As you may know, when
and unless
methods are offered by various classes throughout the framework. These methods can be used to conditionally perform an action if the boolean value of the first argument to the method evaluates to true
or false
:
$collection->when(true, function ($collection) { $collection->merge([1, 2, 3]);});
Therefore, in previous releases of Laravel, passing a closure to the when
or unless
methods meant that the conditional operation would always execute, since a loose comparison against a closure object (or any other object) always evaluates to true
. This often led to unexpected outcomes because developers expect the result of the closure to be used as the boolean value that determines if the conditional action executes.
So, in Laravel 9.x, any closures passed to the when
or unless
methods will be executed and the value returned by the closure will be considered the boolean value used by the when
and unless
methods:
$collection->when(function ($collection) { // This closure is executed... return false;}, function ($collection) { // Not executed since first closure returned "false"... $collection->merge([1, 2, 3]);});
HTTP Client
Default Timeout
影响的可能性:中等
The HTTP client now has a default timeout of 30 seconds. In other words, if the server does not respond within 30 seconds, an exception will be thrown. Previously, no default timeout length was configured on the HTTP client, causing requests to sometimes "hang" indefinitely.
If you wish to specify a longer timeout for a given request, you may do so using the timeout
method:
$response = Http::timeout(120)->get(...);
HTTP Fake & Middleware
影响的可能性:低
Previously, Laravel would not execute any provided Guzzle HTTP middleware when the HTTP client was "faked". However, in Laravel 9.x, Guzzle HTTP middleware will be executed even when the HTTP client is faked.
HTTP Fake & Dependency Injection
影响的可能性:低
In previous releases of Laravel, invoking the Http::fake()
method would not affect instances of the Illuminate\Http\Client\Factory
that were injected into class constructors. However, in Laravel 9.x, Http::fake()
will ensure fake responses are returned by HTTP clients injected into other services via dependency injection. This behavior is more consistent with the behavior of other facades and fakes.
Symfony Mailer
Likelihood Of Impact: High
One of the largest changes in Laravel 9.x is the transition from SwiftMailer, which is no longer maintained as of December 2021, to Symfony Mailer. However, we have tried to make this transition as seamless as possible for your applications. That being said, please thoroughly review the list of changes below to ensure your application is fully compatible.
Driver Prerequisites
To continue using the Mailgun transport, your application should require the symfony/mailgun-mailer
Composer package:
composer require symfony/mailgun-mailer
The wildbit/swiftmailer-postmark
Composer package should be removed from your application. Instead, your application should require the symfony/postmark-mailer
Composer package:
composer require symfony/postmark-mailer
Updated Return Types
The send
, html
, text
, and plain
methods no longer return the number of recipients that received the message. Instead, an instance of Illuminate\Mail\SentMessage
is returned. This object contains an instance of Symfony\Component\Mailer\SentMessage
that is accessible via the getSymfonySentMessage
method or by dynamically invoking methods on the object.
Renamed "Swift" Methods
Various SwiftMailer related methods, some of which were undocumented, have been renamed to their Symfony Mailer counterparts. For example, the withSwiftMessage
method has been renamed to 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' );});
{note} Please thoroughly review the Symfony Mailer documentation for all possible interactions with the
Symfony\Component\Mime\Email
object.
The list below contains a more thorough overview of renamed methods. Many of these methods are low-level methods used to interact with SwiftMailer / Symfony Mailer directly, so may not be commonly used within most Laravel applications:
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);
Proxied Illuminate\Mail\Message
Methods
The Illuminate\Mail\Message
typically proxied missing methods to the underlying Swift_Message
instance. However, missing methods are now proxied to an instance of Symfony\Component\Mime\Email
instead. So, any code that was previously relying on missing methods to be proxied to SwiftMailer should be updated to their corresponding Symfony Mailer counterparts.
Again, many applications may not be interacting with these methods, as they are not documented within the Laravel documentation:
// 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');
Generated Messages IDs
SwiftMailer offered the ability to define a custom domain to include in generated Message IDs via the mime.idgenerator.idright
configuration option. This is not supported by Symfony Mailer. Instead, Symfony Mailer will automatically generate a Message ID based on the sender.
Forced Reconnections
It is no longer possible to force a transport reconnection (for example when the mailer is running via a daemon process). Instead, Symfony Mailer will attempt to reconnect to the transport automatically and throw an exception if the reconnection fails.
SMTP Stream Options
Defining stream options for the SMTP transport is no longer supported. Instead, you must define the relevant options directly within the configuration if they are supported. For example, to disable TLS peer verification:
'smtp' => [ // Laravel 8.x... 'stream' => [ 'ssl' => [ 'verify_peer' => false, ], ], // Laravel 9.x... 'verify_peer' => false,],
To learn more about the available configuration options, please review the Symfony Mailer documentation.
{note} In spite of the example above, you are not generally advised to disable SSL verification since it introduces the possibility of "man-in-the-middle" attacks.
SMTP auth_mode
Defining the SMTP auth_mode
in the mail
configuration file is no longer required. The authentication mode will be automatically negotiated between Symfony Mailer and the SMTP server.
Failed Recipients
It is no longer possible to retrieve a list of failed recipients after sending a message. Instead, a Symfony\Component\Mailer\Exception\TransportExceptionInterface
exception will be thrown if a message fails to send. Instead of relying on retrieving invalid email addresses after sending a message, we recommend that you validate email addresses before sending the message instead.
Packages
The lang
Directory
影响的可能性:中等
In new Laravel applications, the resources/lang
directory is now located in the root project directory (lang
). If your package is publishing language files to this directory, you should ensure that your package is publishing to app()->langPath()
instead of a hard-coded path.
Queue
The opis/closure
Library
影响的可能性:低
Laravel's dependency on opis/closure
has been replaced by laravel/serializable-closure
. This should not cause any breaking change in your application unless you are interacting with the opis/closure
library directly. In addition, the previously deprecated Illuminate\Queue\SerializableClosureFactory
and Illuminate\Queue\SerializableClosure
classes have been removed. If you are interacting with opis/closure
library directly or using any of the removed classes, you may use Laravel Serializable Closure instead.
The Failed Job Provider flush
Method
影响的可能性:低
The flush
method defined by the Illuminate\Queue\Failed\FailedJobProviderInterface
interface now accepts an $hours
argument which determines how old a failed job must be (in hours) before it is flushed by the queue:flush
command. If you are manually implementing the FailedJobProviderInterface
you should ensure that your implementation is updated to reflect this new argument:
public function flush($hours = null);
Session
The getSession
Method
影响的可能性:低
The Symfony\Component\HttpFoundaton\Request
class that is extended by Laravel's own Illuminate\Http\Request
class offers a getSession
method to get the current session storage handler. This method is not documented by Laravel as most Laravel applications interact with the session through Laravel's own session
method.
The getSession
method previously returned an instance of Illuminate\Session\Store
or null
; however, due to the Symfony 6.x release enforcing a return type of Symfony\Component\HttpFoundation\Session\SessionInterface
, the getSession
now correctly returns a SessionInterface
implementation or throws an \Symfony\Component\HttpFoundation\Exception\SessionNotFoundException
exception when no session is available.
Testing
The assertDeleted
Method
影响的可能性:中等
All calls to the assertDeleted
method should be updated to assertModelMissing
.
Trusted Proxies
影响的可能性:低
If you are upgrading your Laravel 8 project to Laravel 9 by importing your existing application code into a totally new Laravel 9 application skeleton, you may need to update your application's "trusted proxy" middleware.
Within your app/Http/Middleware/TrustProxies.php
file, update use Fideloper\Proxy\TrustProxies as Middleware
to use Illuminate\Http\Middleware\TrustProxies as Middleware
.
Next, within app/Http/Middleware/TrustProxies.php
, you should update the $headers
property definition:
// Before...protected $headers = Request::HEADER_X_FORWARDED_ALL; // After...protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB;
Validation
Form Request validated
Method
影响的可能性:低
The validated
method offered by form requests now accepts $key
and $default
arguments. If you are manually overwriting the definition of this method, you should update your method's signature to reflect these new arguments:
public function validated($key = null, $default = null)
The password
Rule
影响的可能性:中等
The password
rule, which validates that the given input value matches the authenticated user's current password, has been renamed to current_password
.
Unvalidated Array Keys
影响的可能性:中等
In previous releases of Laravel, you were required to manually instruct Laravel's validator to exclude unvalidated array keys from the "validated" data it returns, especially in combination with an array
rule that does not specify a list of allowed keys.
However, in Laravel 9.x, unvalidated array keys are always excluded from the "validated" data even when no allowed keys have been specified via the array
rule. Typically, this behavior is the most expected behavior and the previous excludeUnvalidatedArrayKeys
method was only added to Laravel 8.x as a temporary measure in order to preserve backwards compatibility.
Although it is not recommended, you may opt-in to the previous Laravel 8.x behavior by invoking a new includeUnvalidatedArrayKeys
method within the boot
method of one of your application's service providers:
use Illuminate\Support\Facades\Validator; /** * Register any application services. * * @return void */public function boot(){ Validator::includeUnvalidatedArrayKeys();}
杂项
我们还鼓励您查看 laravel/laravel
GitHub 存储库 中的更改。 虽然其中许多更改不是必需的,但您可能希望使这些文件与您的应用程序保持同步。 本升级指南将涵盖其中一些更改,但其他更改(例如对配置文件或注释的更改)将不会涵盖。 您可以使用 GitHub comparison tool 轻松查看更改并选择哪些更新对您很重要。