From 57c92fc646df95fdde4eba4a7045b8f64c14583c Mon Sep 17 00:00:00 2001 From: Guy Sartorelli Date: Mon, 15 Jan 2024 11:22:40 +1300 Subject: [PATCH] DOC Document added generic types --- en/04_Changelogs/5.2.0.md | 95 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/en/04_Changelogs/5.2.0.md b/en/04_Changelogs/5.2.0.md index c2b0eff56..83f5036ee 100644 --- a/en/04_Changelogs/5.2.0.md +++ b/en/04_Changelogs/5.2.0.md @@ -14,6 +14,7 @@ title: 5.2.0 (unreleased) - [Buttons to select all files and deselect all files](#bulk-action-buttons) - [New searchable dropdown fields](#searchable-dropdown-field) - [More nuanced permissions for `/dev/*` routes](#dev-route-permissions) + - [Generic typehints](#generics) - [New exception in React forms](#react-forms-exception) - [Other new features](#other-new-features) - [API changes](#api-changes) @@ -182,6 +183,100 @@ Now, all of the controllers which handle these routes that come packaged in a co You can also now optionally implement a `canView()` method on your `BuildTask` implementations to restrict accessed for specific tasks even further. This means you can grant access to *some* tasks to specific users or groups without granting access to *all* tasks. +### Generic typehints {#generics} + +Typehints using PHPStan-style generic types have been added to PHPDocs in many areas of the codebase of supported modules. The primary goal of this is to improve the developer experience by correctly reporting to your IDE what types it should expect, for example when looping through a `DataList`. In many cases your IDE will now know what types to expect without needing you to prompt it with [`@var` annotation comments](https://docs.phpdoc.org/3.0/guide/references/phpdoc/tags/var.html). + +[info] +There are some cases where this goal conflicts with having types that are correctly identified by PHPStan itself (or other static analysis tools). For example conditional return types aren't supported as widely in IDEs as generics themselves are, so we opted to not use conditional return types even when those would result in a more accurate type for static analysis tools. +[/info] + +While you should see some improvements immediately after updating, there are some changes you can make to your own codebase to best use the new generic type hints. + +See [Generics By Examples | PHPStan](https://phpstan.org/blog/generics-by-examples) and [Generics in PHP using PHP DocComments | DEVSENSE](https://blog.devsense.com/2022/generics-in-php-using-phpdoc) for more information about PHP generic typehints. + +#### Generic typehints when returning lists {#generics-return-lists} + +In your project code, any time you return an instance of `SS_List` (such as a `DataList` or `ArrayList`), you can add a generic typehint to declare what kind of object the returned list contains. This example will hint to the IDE that it returns a `DataList` containing `CarouselItem` records: + +```php +use App\Model\CarouselItem; +use SilverStripe\ORM\DataList; + +/** + * @return DataList + */ +function getCarouselItems(): DataList +{ + return CarouselItem::get(); +} +``` + +#### Generic typehints in `Extension` subclasses {#generics-extensions} + +The generic typing on the `Extension` class can be used to tell your IDE what type to expect for the [`$owner`](api:SilverStripe\Core\Extension->owner) property and [`getOwner()`](api:SilverStripe\Core\Extension::getOwner()) method. + +For this to be useful, you need to tell your IDE that your subclass `@extends` the `Extension` class, and tell it what type the owner should be. + +[warning] +Don't forget to include a `use` statement, even if you're not explicitly referencing the type anywhere in your actual code. Your IDE needs the `use` statement to resolve the FQCN for the class you're referencing in the typehint. +[/warning] + +```php +namespace App\Extension; + +use SilverStripe\Core\Extension; +use SilverStripe\SiteConfig\SiteConfig; + +/** + * @extends Extension + */ +class SiteConfigExtension extends Extension +{ + // ... +} +``` + +This is also a useful way to indicate to developers at a glance what type(s) the extension is designed to be applied to. + +For example you might have an extension that can apply to any `SiteTree` class, or to `LeftAndMain` and `GridFieldDetailForm_ItemRequest` classes, which you can indicate using a union typehint: `@extends Extension`. + +#### Generic typehints in `ContentController` subclasses {#generics-contentcontroller} + +If you use the [`data()`](api:SilverStripe\CMS\Controllers\ContentController::data()) method or the [`$dataRecord`](api:SilverStripe\CMS\Controllers\ContentController->dataRecord) property in your page controllers, you may find it useful for your IDE to know specifically what page class that data represents. + +For this to work, you need to make sure your base `PageController` class has a `@template` type to extend. Any time you use `@extends`, the class being extended needs to have a `@template` type so that your IDE knows what the type you're passing in is going to be used for. + +```php +namespace { + + use SilverStripe\CMS\Controllers\ContentController; + + /** + * @template T of Page + * @extends ContentController + */ + class PageController extends ContentController + { + // ... + } +} +``` + +```php +namespace App\PageType; + +use PageController; + +/** + * @extends PageController + */ +class HomepageController extends PageController +{ + // ... +} +``` + ### New exception in react forms {#react-forms-exception} A [`LogicException`](https://www.php.net/manual/en/class.logicexception.php) is now thrown by [`FormSchema::getSchema()`](api:SilverStripe\Forms\Schema::getSchema()) if a react component was not found for a field type.