Release Note 11
Due to overwhelming support from the community, Winter CMS is updating its foundation framework to the latest Long Term Support (LTS) release. As a result, there are some new requirements to run Winter and some code changes required.
From the proposed Build 1.1.0 your webserver will require PHP 7.2.9 or above to use Winter CMS. After this date, websites using PHP 7.0-7.2.8 will still function normally but will no longer be able to receive updates or install the latest version.
There are various code changes that may be required, including code found in plugins and themes, both private and public depending on what features you are utilizing.
Instructions marked with a √
can be performed immediately to ensure forward compatibility. In other words the change is compatible with all versions of Winter, now and after the release. It is recommended to make these changes as soon as possible.
- Build 1.1.0 is available as a test update from 16 August 2020. Stable release date to be announced.
- PHP 7.2 or greater (recommended PHP 7.4)
- SQLite 3.7.11 or greater (recommended SQLite 3.8.8+)
NOTE: The PHP 7.1 branch, and those before it, are now unsupported.
If you are using any of the following functionality it's highly recommended that you take a look at the relevant section in this guide and make any required changes to your usage:
- Composer
- Configuration files (
/config/*.php
) - Environment variables (
.env
files) - Server configuration (
.htaccess
files) - Any packages made for Laravel
- Laravel package auto-discovery
- Interacting with
Cache
repositories - String based primary keys in models
- Use of
$guarded
in models - Wildcard event listeners
- Catch-all routing
- Using Carbon directly
- Using Jenssegers\Date directly
- Using Symfony directly
- Using League\Csv directly
- Unit Testing
- If using RainLab.Translate, ensure you update to v1.6.x (currently unreleased, manually apply 3e31ce) to resolve
Missing required parameters for [Route: ] [URI: en/{slug}].")
Any required code changes are described below in sections based on related functionality that you may or may not be using. If you are using the described functionality, please review the section and make the required changes.
If you are using composer you will need to make the following changes to your composer.json file:
"require": {
"php": "^7.2",
"winter/rain": "~1.1.1",
"winter/system": "~1.1.1",
"winter/backend": "~1.1.1",
"winter/cms": "~1.1.1",
"laravel/framework": "~6.0",
"wikimedia/composer-merge-plugin": "1.4.1"
},
"config": {
"preferred-install": "dist",
"platform": {
"php": "7.2.9"
}
},
NOTE: See the below section on Unit Testing if you also have a
require-dev
section defined in yourcomposer.json
.
Some new configuration files have been made available as part of the Laravel 6 upgrade. You should add these configuration files to your project's config
folder, and adjust the configuration as necessary. While you're doing that, it is recommended that you review the rest of the configuration files present on GitHub and ensure that your project's copies are up to date as there have been numerous configuration options added over the past year.
A new config option has been added to config/app.php
, loadDiscoveredPackages
, which controls the loading of packages through Laravel's package discovery system. If you do not provide this through your own config/app.php
instance, this will default to false
.
If you are using .env
files with a variable that has a #
inside of an unquoted value; that will now be treated as a comment due to an upgrade to the phpdotenv
library used for parsing them.
If you have any #
characters inside of unquoted environment variables please update them to be quoted instead (ex. DB_PASS=23das#sdfas
must be updated to DB_PASS="23das#sdfas"
).
Additionally, putenv()
no longer changes the value returned by calls to env()
as the env
helper is now considered read-only. If dynamically changing configuration is required, it is recommended to use Config values instead as they can be dynamically changed with Config::set()
IMPORTANT:
.env
files now require any values with spaces in them to be quoted too, it's recommended to just enclose every single value in.env
with double quotes.
With the introduction of core support for the | resize
filter and associated logic, you will need to add the storage/app/resized
directory as an allowed directory to your server configuration in order to load the resized images produced by that functionality.
.htaccess
/ Apache: Add RewriteCond %{REQUEST_FILENAME} !/storage/app/resized/.*
to the ### White listed folders
section, below RewriteCond %{REQUEST_FILENAME} !/storage/app/media/.*
.
site.conf
/ nginx: Add location ~ ^/storage/app/resized { try_files $uri 404; }
to the # Let nginx return 404 if static file doesn't exist
section, below location ~ ^/storage/app/media { try_files $uri 404; }
.
web.config
/ IIS: Add <add input="{REQUEST_FILENAME}" matchType="IsFile" pattern="^/storage/app/resized/.*" negate="true" />
below <add input="{REQUEST_FILENAME}" matchType="IsFile" pattern="^/storage/app/media/.*" negate="true" />
The version of Laravel has been changed from 5.5 LTS to 6.x LTS. If you are using packages made for Laravel you may have to go through and update them to a version compatible with Laravel 6.x.
Starting with the Laravel 6 foundation upgrade, Winter CMS will now default to no longer loading discovered packages through Laravel's package auto-discovery service, as this was having the effect of Laravel packages still being loaded and made active even if the plugin using them had been disabled by the user.
This may mean that plugins that were using packages for Laravel but were not explicitly including them in the boot process for the plugin may no longer have access to these packages. Please note that we recommend that plugins do not rely on auto-discovery, and instead bring in the packages and service providers explicitly through the Plugin.php
file as part of the register()
or boot()
processes.
A new config option has been added to config/app.php
, loadDiscoveredPackages
, which controls the loading of packages through Laravel's package discovery system. If you do not provide this through your own config/app.php
instance, this will default to false
. If you wish to retain the pre-update functionality, you can set this to true
.
Cache TTL (time-to-live) values that are specified as an integer are treated as seconds now, as opposed to minutes, when interacting directly with a cache repository. If you interact with the cache directly, we recommend that you use DateTime
or Carbon
instances to define when your data is to expire (ex. now()->addMinutes(60)
instead of 60
). If you wish to continue using integers, you will need to multiply your integer values by 60 to get the number of seconds.
NOTE: This does not affect the
cms.urlCacheTtl
andcms.parsedPageCacheTTL
configuration values, which will continue to use minutes.
If you are using string based primary keys for your models add protected $keyType = 'string';
to the model class definition to prevent performance optimizations meant for integer key types from negatively affecting your code.
Due to a recent security patch made in the Laravel framework (see discussion), if a model uses the $guarded
property to define attributes that are to be protected from mass assignment; then any attempts to use mass-assignment to populate a property / attribute of the model that does not exist in the database will fail. Example:
MyModel extends Model
{
$guarded = ['id'];
$someProperty = null;
public function setSomePropertyAttribute($value)
{
$this->someProperty = $value
}
}
Calling MyModel::create(['someProperty' => 'data']);
would previously have worked, but will not anymore.
NOTE: Winter does not recommend this pattern. Unless using the
Purgeable
trait, attributes on a model should always correspond directly to the database schema asssociated with that model. It is perfectly acceptable to have public properties on a model that do not correspond to database schema, but avoid abusing methods & functionality designed for model attributes (i.e. attribute getters and setters) as much as possible.
This specifically affected the Winter\Rain\Database\Attach\File
model (and by extension, the System\Models\File
model), which now use the "fillable" attributes property to define the fields available for mass assignment, as opposed to the "guarded" attributes property. If you extend either of these models to provide your own custom File model and wish to have extra fields stored through mass assignment, you will need to copy the $fillable
attribute from the Winter\Rain\Database\Attach\File
model and place it in your own extension, adding any extra fields that you wish to be fillable as well.
The parameters sent to wildcard event listeners in Winter has changed to match what Laravel has done since 5.4. This was overlooked in the 5.5 update but is being applied now. Going forward all wildcard event listeners will receive the name of the event currently being fired as the first parameter and an array of the event arguments as the second parameter.
Example of old wildcard listener:
Event::listen('*', function ($params) {
if (Event::firing() === 'some.specific.event') {
// do stuff with $params
}
});
Example of new wildcard listener:
Event::listen('*', function ($event, $params) {
if ($event === 'some.specific.event') {
// do stuff with $params
}
});
Changes to the routing in Laravel 6 have resulted in the behaviour of catch-all routes being changed in Winter CMS. Previously, a catch-all route could be defined with the following:
Route::any('{slug}', 'Backend\Classes\BackendController@run')->where('slug', '(.*)?');
The definition {slug}
in Laravel 6 is now considered to be a required URL parameter, and will fail with an exception if the router sees an empty parameter. If your plugin uses a similar routing rule, and you would like URLs with an empty parameter to still be routed to your plugin, you must change this definition to be optional by suffixing the parameter name with the question mark (?
) symbol. Eg:
Route::any('{slug?}', 'Backend\Classes\BackendController@run')->where('slug', '(.*)?');
This can be done immediately for your plugin routes, as optional parameters were already available in Laravel 5.5.
The Carbon library has been upgraded from version 1 to version 2. While this should mostly work with existing code, please review the upgrade guide.
The jenssegers/date
library has been removed from Winter entirely as most of its functionality is now present in Carbon 2.0. This should mostly work with existing code unless you were referencing it directly, in which case replace any references to Jenssegers\Date\Date
with Winter\Rain\Argon\Date
.
Symfony has been upgraded to version 4 (except for the Yaml subsystem). If interacting directly with it, please review the upgrade guide
The CSV package provided by The PHP League has been upgraded from version 8 to version 9. We have made the necessary adjustments to Winter CMS in order to accommodate this change, however, if you use the library directly or have extended the ImportModel
and ExportModel
classes, it is strongly recommended that you review the upgrade guide as several methods have been dropped or moved.
The following files have been updated for Laravel 6, however, you may continue to use your current version of these files if you wish:
If you are running unit testing for Winter CMS development, you will need to make some changes to your composer.json file and replace the tests
folder in your installation to get the updates to the unit tests.
composer.json changes required:
"require-dev": {
"phpunit/phpunit": "^8.0|^9.0",
"fzaninotto/faker": "~1.9",
"squizlabs/php_codesniffer": "3.*",
"php-parallel-lint/php-parallel-lint": "^1.0",
"meyfa/phpunit-assert-gd": "^2.0.0",
"dms/phpunit-arraysubset-asserts": "^0.1.0"
},
"autoload-dev": {
"classmap": [
"tests/concerns/InteractsWithAuthentication.php",
"tests/fixtures/backend/models/UserFixture.php",
"tests/TestCase.php",
"tests/PluginTestCase.php"
]
},
"scripts": {
"post-create-project-cmd": [
"php artisan key:generate",
"php artisan package:discover"
],
"post-update-cmd": [
"php artisan winter:util set build",
"php artisan package:discover"
],
"test": [
"phpunit --stop-on-failure"
],
"lint": [
"parallel-lint --exclude vendor --exclude storage --exclude tests/fixtures/plugins/testvendor/goto/Plugin.php ."
],
"sniff": [
"phpcs --colors -nq --report=\"full\" --extensions=\"php\""
]
},
"extra": {
"merge-plugin": {
"include": [
"plugins/*/*/composer.json"
],
"recurse": true,
"replace": false,
"merge-dev": false
}
}
You then need to replace the contents of your project's tests/
directory with the tests/
directory from the repository.
For maxium compatibility you can also replace your phpunit.xml
file with the phpunit.xml
file from the repository
If your plugin contains unit tests, you will need to make some adjustments to your unit tests in order to function with the Laravel 6 upgrade.
- All
setUp
andtearDown
methods in your unit test classes must now have the return typevoid
specified to match PHPUnit 8's requirements.- Change all
public function setUp()
methods topublic function setUp(): void
. - Change all
public function tearDown()
methods topublic function tearDown(): void
.
- Change all
- The
syntaxCheck
attribute for the<phpunit>
tag in yourphpunit.xml
file is now deprecated. This should be removed from yourphpunit.xml
file.
In addition to the changes above, note the following deprecations in PHPUnit 8 - these will be thrown as warnings in your unit tests.
- The
@expectedException
group of docblock annotations are now deprecated. Instead, you should call the following methods inside your unit test method, depending on the annotation:- @expectedException:
$this->expectException(ClassName::class)
- @expectedExceptionCode:
$this->expectExceptionCode(int)
- @expectedExceptionMessage:
$this->expectExceptionMessage(string)
- @expectedException:
- The
assertInternalType()
method is no longer available. Instead, you should use thearrayIs[Type]()
methods, whereType
is the PHP variable type with a capital letter. For example, instead ofassertInternalType('string', ...)
, you should useassertIsString
, orassertInternalType('array', ...)
should beassertIsArray
. - The
assertContains()
method no longer tests whether strings are inside strings - it now will only detect if an item is contained in an array. If you are usingassertContains()
in this fashion, you should instead useassertStringContainsString()
.
For more information on deprecations, see the PHPUnit 8 release notes. Note that although the assertArraySubset()
method was deprecated, we are still maintaining the method through requiring the dms/phpunit-arraysubset-asserts
library, so you may continue to use that assertion.
Migrating from PHP 7.0 to PHP 7.1
Migrating from PHP 7.1 to PHP 7.2
Migrating from PHP 7.2 to PHP 7.3
Migrating from PHP 7.3 to PHP 7.4