ENH Add generic types (#11108)

There are also a few general corrections to PHPDocs that I noticed along
the way (e.g. adding `|null` when the method is returning a null value.

There are some cases where either the return type or the whole PHPDoc
was duplicated from the parent class - in those cases I've simply
removed the duplication.
This commit is contained in:
Guy Sartorelli 2024-01-17 17:08:26 +13:00 committed by GitHub
parent 583b762217
commit 357ed7ad7e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
100 changed files with 389 additions and 423 deletions

View File

@ -164,7 +164,6 @@ class Director implements TemplateGlobalProvider
// Build list of cleanup promises
$finally = [];
/** @var Kernel $kernel */
$kernel = Injector::inst()->get(Kernel::class);
$kernel->nest();
$finally[] = function () use ($kernel) {
@ -1016,7 +1015,6 @@ class Director implements TemplateGlobalProvider
*/
public static function get_environment_type()
{
/** @var Kernel $kernel */
$kernel = Injector::inst()->get(Kernel::class);
return $kernel->getEnvironment();
}

View File

@ -411,7 +411,6 @@ EOT
// a more specific error description.
$body = $this->getBody();
if ($this->isError() && empty($body)) {
/** @var HandlerInterface $handler */
$handler = Injector::inst()->get(HandlerInterface::class);
$formatter = $handler->getFormatter();
echo $formatter->format([

View File

@ -16,7 +16,6 @@ class FlushMiddleware implements HTTPMiddleware
{
public function process(HTTPRequest $request, callable $delegate)
{
/** @var BaseKernel $kernel */
$kernel = Injector::inst()->get(Kernel::class);
if ((method_exists($kernel, 'isFlushed') && $kernel->isFlushed())) {
// Disable cache when flushing

View File

@ -30,7 +30,6 @@ trait FlushScheduler
{
$flush = array_key_exists('flush', $request->getVars() ?? []) || ($request->getURL() === 'dev/build');
/** @var BaseKernel $kernel */
$kernel = Injector::inst()->get(Kernel::class);
if (!$flush || (method_exists($kernel, 'isFlushed') && $kernel->isFlushed())) {
return false;

View File

@ -13,6 +13,8 @@ use SilverStripe\ORM\DataObject;
* Every object instance gets its own set of extension instances,
* meaning you can set parameters specific to the "owner instance"
* in new Extension instances.
*
* @template T of object
*/
abstract class Extension
{
@ -25,14 +27,14 @@ abstract class Extension
/**
* The object this extension is applied to.
*
* @var Object
* @var T
*/
protected $owner;
/**
* Stack of all parent owners, not including current owner
*
* @var array
* @var array<T>
*/
private $ownerStack = [];
@ -95,7 +97,7 @@ abstract class Extension
/**
* Returns the owner of this extension.
*
* @return Object
* @return T
*/
public function getOwner()
{

View File

@ -966,12 +966,13 @@ class Injector implements ContainerInterface
*
* @throws NotFoundExceptionInterface No entry was found for **this** identifier.
*
* @param string $name The name of the service to retrieve. If not a registered
* @template T of object
* @param class-string<T>|string $name The name of the service to retrieve. If not a registered
* service, then a class of the given name is instantiated
* @param bool $asSingleton If set to false a new instance will be returned.
* If true a singleton will be returned unless the spec is type=prototype'
* @param array $constructorArgs Args to pass in to the constructor. Note: Ignored for singletons
* @return mixed Instance of the specified object
* @return T|mixed Instance of the specified object
*/
public function get($name, $asSingleton = true, $constructorArgs = [])
{
@ -987,12 +988,13 @@ class Injector implements ContainerInterface
/**
* Returns the service, or `null` if it doesnt' exist. See {@link get()} for main usage.
*
* @param string $name The name of the service to retrieve. If not a registered
* @template T of object
* @param class-string<T>|string $name The name of the service to retrieve. If not a registered
* service, then a class of the given name is instantiated
* @param bool $asSingleton If set to false a new instance will be returned.
* If true a singleton will be returned unless the spec is type=prototype'
* @param array $constructorArgs Args to pass in to the constructor. Note: Ignored for singletons
* @return mixed Instance of the specified object
* @return T|mixed Instance of the specified object
*/
protected function getNamedService($name, $asSingleton = true, $constructorArgs = [])
{
@ -1111,9 +1113,9 @@ class Injector implements ContainerInterface
/**
* Magic method to return an item directly
*
* @param string $name
* The named object to retrieve
* @return mixed
* @template T of object
* @param class-string<T>|string $name The named object to retrieve
* @return T|mixed
*/
public function __get($name)
{
@ -1125,9 +1127,10 @@ class Injector implements ContainerInterface
*
* Additional parameters are passed through as
*
* @param string $name
* @template T of object
* @param class-string<T>|string $name
* @param mixed ...$argument arguments to pass to the constructor
* @return mixed A new instance of the specified object
* @return T|mixed A new instance of the specified object
*/
public function create($name, $argument = null)
{
@ -1139,9 +1142,10 @@ class Injector implements ContainerInterface
/**
* Creates an object with the supplied argument array
*
* @param string $name Name of the class to create an object of
* @template T of object
* @param class-string<T>|string $name Name of the class to create an object of
* @param array $constructorArgs Arguments to pass to the constructor
* @return mixed
* @return T|mixed
*/
public function createWithArgs($name, $constructorArgs)
{

View File

@ -148,7 +148,6 @@ class ModuleManifest
{
$modules = $this->getModules();
// Work in reverse priority, so the higher priority modules get later execution
/** @var Module $module */
foreach (array_reverse($modules ?? []) as $module) {
$module->activate();
}
@ -262,7 +261,6 @@ class ModuleManifest
return null;
}
/** @var Module $rootModule */
$rootModule = null;
// Find based on loaded modules

View File

@ -88,7 +88,6 @@ class ModuleResource
*/
public function getURL()
{
/** @var ResourceURLGenerator $generator */
$generator = Injector::inst()->get(ResourceURLGenerator::class);
return $generator->urlForResource($this);
}

View File

@ -51,7 +51,6 @@ class ModuleResourceLoader implements TemplateGlobalProvider
$resource = $this->resolveResource($resource);
// Resolve resource to url
/** @var ResourceURLGenerator $generator */
$generator = Injector::inst()->get(ResourceURLGenerator::class);
return $generator->urlForResource($resource);
}

View File

@ -121,7 +121,7 @@ class FixtureFactory
*
* @param string $class The data class, as specified in your fixture file. Parent classes won't work
* @param string $identifier The identifier string, as provided in your fixture file
* @return int
* @return int|false
*/
public function getId($class, $identifier)
{
@ -162,9 +162,10 @@ class FixtureFactory
/**
* Get an object from the fixture.
*
* @param string $class The data class or table name, as specified in your fixture file. Parent classes won't work
* @template T of DataObject
* @param class-string<T> $class The data class or table name, as specified in your fixture file. Parent classes won't work
* @param string $identifier The identifier string, as provided in your fixture file
* @return DataObject
* @return T|null
*/
public function get($class, $identifier)
{

View File

@ -337,7 +337,6 @@ abstract class FunctionalTest extends SapphireTest implements TestOnly
$actuals = [];
if ($items) {
/** @var SimpleXMLElement $item */
foreach ($items as $item) {
$actuals[$item->asXML()] = true;
}
@ -371,7 +370,6 @@ abstract class FunctionalTest extends SapphireTest implements TestOnly
$actuals = [];
if ($items) {
/** @var SimpleXMLElement $item */
foreach ($items as $item) {
$actuals[] = $item->asXML();
}

View File

@ -464,10 +464,11 @@ abstract class SapphireTest extends TestCase implements TestOnly
/**
* Get an object from the fixture.
*
* @param string $className The data class or table name, as specified in your fixture file. Parent classes won't work
* @template T of DataObject
* @param class-string<T> $className The data class or table name, as specified in your fixture file. Parent classes won't work
* @param string $identifier The identifier string, as provided in your fixture file
*
* @return DataObject
* @return T
*/
protected function objFromFixture($className, $identifier)
{
@ -563,7 +564,6 @@ abstract class SapphireTest extends TestCase implements TestOnly
*/
public function clearEmails()
{
/** @var MailerInterface $mailer */
$mailer = Injector::inst()->get(MailerInterface::class);
if ($mailer instanceof TestMailer) {
$mailer->clearEmails();
@ -584,7 +584,6 @@ abstract class SapphireTest extends TestCase implements TestOnly
*/
public static function findEmail($to, $from = null, $subject = null, $content = null)
{
/** @var MailerInterface $mailer */
$mailer = Injector::inst()->get(MailerInterface::class);
if ($mailer instanceof TestMailer) {
return $mailer->findEmail($to, $from, $subject, $content);
@ -1041,7 +1040,6 @@ abstract class SapphireTest extends TestCase implements TestOnly
*/
public function logOut()
{
/** @var IdentityStore $store */
$store = Injector::inst()->get(IdentityStore::class);
$store->logOut();
}

View File

@ -175,7 +175,6 @@ class FixtureTestState implements TestState
*/
protected function loadFixture($fixtureFile, SapphireTest $test)
{
/** @var YamlFixture $fixture */
$fixture = Injector::inst()->create(YamlFixture::class, $fixtureFile);
$fixture->writeInto($this->getFixtureFactory(get_class($test)));
}

View File

@ -18,7 +18,6 @@ class LoggerState implements TestState
{
public function setUp(SapphireTest $test)
{
/** @var Logger $userLogger */
$userLogger = Injector::inst()->get(LoggerInterface::class);
if ($userLogger && $userLogger instanceof Logger) {
$userLogger->setHandlers([new NullHandler()]);

View File

@ -72,7 +72,6 @@ class SapphireTestState implements TestState
public function tearDown(SapphireTest $test)
{
// Tear down in reverse order
/** @var TestState $state */
foreach (array_reverse($this->states ?? []) as $state) {
$state->tearDown($test);
}
@ -93,7 +92,6 @@ class SapphireTestState implements TestState
public function tearDownOnce($class)
{
// Tear down in reverse order
/** @var TestState $state */
foreach (array_reverse($this->states ?? []) as $state) {
$state->tearDownOnce($class);
}

View File

@ -208,7 +208,6 @@ class TestSession
*/
public function submitForm(string $formID, string $button = null, array $data = [], bool $withSecurityToken = true): HTTPResponse
{
/** @var Crawler $page */
$page = $this->lastPage();
if ($page) {
try {

View File

@ -9,6 +9,7 @@ use SilverStripe\ORM\DatabaseAdmin;
/**
* Hook up static validation to the deb/build process
*
* @extends Extension<DatabaseAdmin>
*/
class DatabaseAdminExtension extends Extension
{

View File

@ -58,7 +58,7 @@ class CheckboxSetField extends MultiSelectField
/**
* Gets the list of options to render in this formfield
*
* @return ArrayList
* @return ArrayList<ArrayData>
*/
public function getOptions()
{

View File

@ -76,7 +76,6 @@ class CompositeField extends FormField
$children = $this->getChildren();
if ($children && $children->count()) {
$childSchema = [];
/** @var FormField $child */
foreach ($children as $child) {
$childSchema[] = $child->getSchemaData();
}
@ -132,7 +131,6 @@ class CompositeField extends FormField
$fieldList = $this->FieldList();
$compositeTitle = '';
$count = 0;
/** @var FormField $subfield */
foreach ($fieldList as $subfield) {
$compositeTitle .= $subfield->getName();
if ($subfield->getName()) {
@ -415,7 +413,6 @@ class CompositeField extends FormField
$clone = clone $this;
if ($clone->getChildren()) {
foreach ($clone->getChildren() as $child) {
/** @var FormField $child */
$child = $child->transform(new ReadonlyTransformation());
$newChildren->push($child);
}
@ -441,7 +438,6 @@ class CompositeField extends FormField
$clone = clone $this;
if ($clone->getChildren()) {
foreach ($clone->getChildren() as $child) {
/** @var FormField $child */
$child = $child->transform(new DisabledTransformation());
$newChildren->push($child);
}
@ -482,7 +478,6 @@ class CompositeField extends FormField
$i = 0;
foreach ($this->children as $child) {
/** @var FormField $child */
if ($child->getName() == $field->getName()) {
return $i;
}
@ -540,7 +535,6 @@ class CompositeField extends FormField
{
$valid = true;
foreach ($this->children as $child) {
/** @var FormField $child */
$valid = ($child && $child->validate($validator) && $valid);
}
return $this->extendValidationResult($valid, $validator);

View File

@ -30,22 +30,18 @@ use SilverStripe\ORM\ValidationResult;
* {
* $compositeValidator->addValidator(RequiredFields::create(['AdditionalContent']));
* }
*
* Class CompositeValidator
*
* @package SilverStripe\Forms
*/
class CompositeValidator extends Validator
{
/**
* @var array|Validator[]
* @var array<Validator>
*/
private $validators;
/**
* CompositeValidator constructor.
*
* @param array|Validator[] $validators
* @param array<Validator> $validators
*/
public function __construct(array $validators = [])
{
@ -146,7 +142,7 @@ class CompositeValidator extends Validator
}
/**
* @return array|Validator[]
* @return array<Validator>
*/
public function getValidators(): array
{
@ -159,8 +155,9 @@ class CompositeValidator extends Validator
* The keys for the return array match the keys in the unfiltered array. You cannot assume the keys will be
* sequential or that the first key will be ZERO.
*
* @param string $className
* @return array|Validator[]
* @template T of Validator
* @param class-string<T> $className
* @return T[]
*/
public function getValidatorsByType(string $className): array
{

View File

@ -168,7 +168,6 @@ class ConfirmedPasswordField extends FormField
// disable auto complete
foreach ($this->getChildren() as $child) {
/** @var FormField $child */
$child->setAttribute('autocomplete', 'off');
}
@ -200,7 +199,6 @@ class ConfirmedPasswordField extends FormField
// Build inner content
$fieldContent = '';
foreach ($this->getChildren() as $field) {
/** @var FormField $field */
$field->setDisabled($this->isDisabled());
$field->setReadonly($this->isReadonly());
@ -321,7 +319,6 @@ class ConfirmedPasswordField extends FormField
public function setRightTitle($title)
{
foreach ($this->getChildren() as $field) {
/** @var FormField $field */
$field->setRightTitle($title);
}
@ -343,7 +340,6 @@ class ConfirmedPasswordField extends FormField
if (is_array($titles) && count($titles ?? []) === $expectedChildren) {
foreach ($this->getChildren() as $field) {
if (isset($titles[0])) {
/** @var FormField $field */
$field->setTitle($titles[0]);
array_shift($titles);
@ -613,7 +609,6 @@ class ConfirmedPasswordField extends FormField
*/
public function performReadonlyTransformation()
{
/** @var ReadonlyField $field */
$field = $this->castedCopy(ReadonlyField::class)
->setTitle($this->title ? $this->title : _t('SilverStripe\\Security\\Member.PASSWORD', 'Password'))
->setValue('*****');

View File

@ -7,7 +7,7 @@ use SilverStripe\ORM\ArrayList;
/**
* A list designed to hold form field instances.
*
* @method FormField[] getIterator()
* @extends ArrayList<FormField>
*/
class FieldList extends ArrayList
{
@ -65,7 +65,6 @@ class FieldList extends ArrayList
{
$stack = $this->toArray();
while (!empty($stack)) {
/** @var FormField $field */
$field = array_shift($stack);
$callback($field);
if ($field instanceof CompositeField) {
@ -399,7 +398,6 @@ class FieldList extends ArrayList
$currentPointer = $this;
foreach ($parts as $k => $part) {
/** @var FormField $currentPointer */
$currentPointer = $currentPointer->fieldByName($part);
}
@ -426,7 +424,6 @@ class FieldList extends ArrayList
$currentPointer = $this;
foreach ($parts as $k => $part) {
$parentPointer = $currentPointer;
/** @var FormField $currentPointer */
$currentPointer = $currentPointer->fieldByName($part);
// Create any missing tabs
if (!$currentPointer) {

View File

@ -138,7 +138,6 @@ class FileField extends FormField implements FileHandleField
File::get_file_extension($_FILES[$this->name]['name'])
);
/** @var File $file */
if ($this->relationAutoSetting) {
// assume that the file is connected via a has-one
$objectClass = DataObject::getSchema()->hasOneComponent(get_class($record), $this->name);

View File

@ -1372,7 +1372,6 @@ class Form extends ViewableData implements HasRequestHandler
return $this;
}
/** @var FormField $field */
foreach ($dataFields as $field) {
$name = $field->getName();
@ -1533,7 +1532,6 @@ class Form extends ViewableData implements HasRequestHandler
$data = [];
if ($dataFields) {
/** @var FormField $field */
foreach ($dataFields as $field) {
if ($field->getName()) {
$data[$field->getName()] = $field->dataValue();

View File

@ -1352,9 +1352,10 @@ class FormField extends RequestHandler
*
* Does not copy custom field templates, since they probably won't apply to the new instance.
*
* @param mixed $classOrCopy Class name for copy, or existing copy instance to update
* @template T
* @param class-string<T>|T $classOrCopy Class name for copy, or existing copy instance to update
*
* @return FormField
* @return T
*/
public function castedCopy($classOrCopy)
{
@ -1397,7 +1398,7 @@ class FormField extends RequestHandler
* Sets the component type the FormField will be rendered as on the front-end.
*
* @param string $componentType
* @return FormField
* @return static
*/
public function setSchemaComponent($componentType)
{
@ -1422,7 +1423,7 @@ class FormField extends RequestHandler
* If you want to pass around ad hoc data use the `data` array e.g. pass `['data' => ['myCustomKey' => 'yolo']]`.
*
* @param array $schemaData - The data to be merged with $this->schemaData.
* @return FormField
* @return static
*/
public function setSchemaData($schemaData = [])
{
@ -1494,7 +1495,7 @@ class FormField extends RequestHandler
* If you want to pass around ad hoc data use the `data` array e.g. pass `['data' => ['myCustomKey' => 'yolo']]`.
*
* @param array $schemaState The data to be merged with $this->schemaData.
* @return FormField
* @return static
*/
public function setSchemaState($schemaState = [])
{

View File

@ -335,7 +335,7 @@ class GridField extends FormField
}
/**
* @return ArrayList
* @return ArrayList<GridFieldComponent>
*/
public function getComponents()
{

View File

@ -31,7 +31,7 @@ class GridFieldConfig
use Configurable;
/**
* @var ArrayList
* @var ArrayList<GridFieldComponent>
*/
protected $components = null;
@ -111,7 +111,7 @@ class GridFieldConfig
}
/**
* @return ArrayList Of GridFieldComponent
* @return ArrayList<GridFieldComponent>
*/
public function getComponents()
{
@ -124,8 +124,9 @@ class GridFieldConfig
/**
* Returns all components extending a certain class, or implementing a certain interface.
*
* @param string $type Class name or interface
* @return ArrayList Of GridFieldComponent
* @template T of GridFieldComponent
* @param class-string<T> $type Class name or interface
* @return ArrayList<T>
*/
public function getComponentsByType($type)
{
@ -141,8 +142,9 @@ class GridFieldConfig
/**
* Returns the first available component with the given class or interface.
*
* @param string $type ClassName
* @return GridFieldComponent
* @template T of GridFieldComponent
* @param class-string<T> $type ClassName
* @return T|null
*/
public function getComponentByType($type)
{

View File

@ -340,7 +340,6 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler
$previousAndNextGroup->addExtraClass('btn-group--circular mr-2');
$previousAndNextGroup->setFieldHolderTemplate(CompositeField::class . '_holder_buttongroup');
/** @var GridFieldDetailForm $component */
$component = $this->gridField->getConfig()->getComponentByType(GridFieldDetailForm::class);
$paginator = $this->getGridField()->getConfig()->getComponentByType(GridFieldPaginator::class);
$gridState = $this->getGridField()->getState();
@ -902,7 +901,7 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler
* see {@link LeftAndMain->Breadcrumbs()} for details.
*
* @param boolean $unlinked
* @return ArrayList
* @return ArrayList<ArrayData>
*/
public function Breadcrumbs($unlinked = false)
{
@ -910,7 +909,7 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler
return null;
}
/** @var ArrayList $items */
/** @var ArrayList<ArrayData> $items */
$items = $this->popupController->Breadcrumbs($unlinked);
if (!$items) {

View File

@ -150,7 +150,6 @@ class GridFieldExportButton extends AbstractGridFieldComponent implements GridFi
return $this->exportColumns;
}
/** @var GridFieldDataColumns $dataCols */
$dataCols = $gridField->getConfig()->getComponentByType(GridFieldDataColumns::class);
if ($dataCols) {
return $dataCols->getDisplayFields($gridField);
@ -224,17 +223,15 @@ class GridFieldExportButton extends AbstractGridFieldComponent implements GridFi
}
}
/** @var GridFieldDataColumns|null $gridFieldColumnsComponent */
$gridFieldColumnsComponent = $gridField->getConfig()->getComponentByType(GridFieldDataColumns::class);
$columnsHandled = ($gridFieldColumnsComponent)
? $gridFieldColumnsComponent->getColumnsHandled($gridField)
: [];
/** @var ArrayList|DataList $items */
/** @var SS_List<ViewableData> $items */
// Remove limit as the list may be paginated, we want the full list for the export
$items = $items->limit(null);
/** @var ViewableData $item */
foreach ($items as $item) {
// Assume item can be viewed if canView() isn't implemented
if (!$item->hasMethod('canView') || $item->canView()) {

View File

@ -185,7 +185,6 @@ class GridFieldFilterHeader extends AbstractGridFieldComponent implements GridFi
return $dataList;
}
/** @var Filterable $dataList */
/** @var array $filterArguments */
$filterArguments = $this->getState($gridField)->Columns->toArray();
if (empty($filterArguments)) {

View File

@ -46,7 +46,6 @@ class GridFieldPageCount extends AbstractGridFieldComponent implements GridField
*/
protected function getPaginator($gridField)
{
/** @var GridFieldPaginator $paginator */
$paginator = $gridField->getConfig()->getComponentByType(GridFieldPaginator::class);
if (!$paginator && GridFieldPageCount::config()->uninherited('require_paginator')) {

View File

@ -156,7 +156,6 @@ class GridFieldPrintButton extends AbstractGridFieldComponent implements GridFie
return $this->printColumns;
}
/** @var GridFieldDataColumns $dataCols */
$dataCols = $gridField->getConfig()->getComponentByType(GridFieldDataColumns::class);
if ($dataCols) {
return $dataCols->getDisplayFields($gridField);
@ -232,7 +231,6 @@ class GridFieldPrintButton extends AbstractGridFieldComponent implements GridFie
$items = $gridField->getManipulatedList();
$itemRows = new ArrayList();
/** @var GridFieldDataColumns $gridFieldColumnsComponent */
$gridFieldColumnsComponent = $gridField->getConfig()->getComponentByType(GridFieldDataColumns::class);
/** @var ViewableData $item */

View File

@ -124,7 +124,6 @@ class GridFieldSortableHeader extends AbstractGridFieldComponent implements Grid
if (!$this->checkDataType($list)) {
return null;
}
/** @var Sortable $list */
$forTemplate = new ArrayData([]);
$forTemplate->Fields = new ArrayList;
@ -247,7 +246,7 @@ class GridFieldSortableHeader extends AbstractGridFieldComponent implements Grid
* {@link DataQuery} first.
*
* @param GridField $gridField
* @param SS_List $dataList
* @param SS_List&Sortable $dataList
* @return SS_List
*/
public function getManipulatedData(GridField $gridField, SS_List $dataList)
@ -256,14 +255,12 @@ class GridFieldSortableHeader extends AbstractGridFieldComponent implements Grid
return $dataList;
}
/** @var Sortable $dataList */
$state = $this->getState($gridField);
if ($state->SortColumn == "") {
return $dataList;
}
// Prevent SQL Injection by validating that SortColumn exists
/** @var GridFieldDataColumns $columns */
$columns = $gridField->getConfig()->getComponentByType(GridFieldDataColumns::class);
$fields = $columns->getDisplayFields($gridField);
if (!array_key_exists($state->SortColumn, $fields) &&

View File

@ -819,7 +819,6 @@ class TinyMCEConfig extends HTMLEditorConfig implements i18nEntityProvider
*/
public function getScriptURL()
{
/** @var TinyMCEScriptGenerator $generator */
$generator = Injector::inst()->get(TinyMCEScriptGenerator::class);
return $generator->getScriptURL($this);
}

View File

@ -84,7 +84,7 @@ class ListboxField extends MultiSelectField
/**
* Gets the list of options to render in this formfield
*
* @return ArrayList
* @return ArrayList<ArrayData>
*/
public function getOptions()
{

View File

@ -103,12 +103,10 @@ class FormSchema
'actions' => []
];
/** @var FormField $action */
foreach ($form->Actions() as $action) {
$schema['actions'][] = $action->getSchemaData();
}
/** @var FormField $field */
foreach ($form->Fields() as $field) {
$schema['fields'][] = $field->getSchemaData();
}
@ -197,10 +195,12 @@ class FormSchema
];
}
/**
* @param iterable<FormField> $fields
*/
protected function getFieldStates($fields)
{
$states = [];
/** @var FormField $field */
foreach ($fields as $field) {
$states[] = $field->getSchemaState();

View File

@ -177,6 +177,8 @@ trait SearchableDropdownTrait
* If a search context has been set via setSearchContext() that will be used
* Will fallback to using the dataobjects default search context if a sourceList has been set
* Otherwise will return null
*
* @return SearchContext<DataObject>|null
*/
public function getSearchContext(): ?SearchContext
{
@ -368,7 +370,6 @@ trait SearchableDropdownTrait
// Don't know what value is, handle gracefully. We should not raise an exception here because
// of there is a bad data for whatever a content editor will not be able to resolve and it will
// render part of the CMS unusable
// /** @var LoggerInterface $logger */
$logger = Injector::inst()->get(LoggerInterface::class);
$logger->warning('Could not determine value in ' . __CLASS__ . '::getValueArray()');
return [];
@ -399,7 +400,7 @@ trait SearchableDropdownTrait
if (!$record->hasMethod($name)) {
throw new LogicException("Relation $name does not exist");
}
/** @var Relation $relation */
/** @var Relation $relationList */
$relationList = $record->$name();
// Use RelationList rather than Relation here since some Relation classes don't allow setting value
// but RelationList does
@ -542,9 +543,9 @@ trait SearchableDropdownTrait
}
/**
* @var ArrayList $options The options list being updated that will become <options>
* @var DataList|ArrayList $items The items to be turned into options
* @var DataList|ArrayList $values The values that have been selected i.e. the value of the Field
* @param ArrayList $options The options list being updated that will become <options>
* @param DataList|ArrayList $items The items to be turned into options
* @param DataList|ArrayList $values The values that have been selected i.e. the value of the Field
*/
private function updateOptionsForSchema(
ArrayList $options,

View File

@ -240,8 +240,7 @@ abstract class SelectField extends FormField
public function performReadonlyTransformation()
{
/** @var LookupField $field */
$field = $this->castedCopy('SilverStripe\\Forms\\LookupField');
$field = $this->castedCopy(LookupField::class);
$field->setSource($this->getSource());
$field->setReadonly(true);
@ -255,14 +254,6 @@ abstract class SelectField extends FormField
return $clone;
}
/**
* Returns another instance of this field, but "cast" to a different class.
*
* @see FormField::castedCopy()
*
* @param string $classOrCopy
* @return FormField
*/
public function castedCopy($classOrCopy)
{
$field = parent::castedCopy($classOrCopy);

View File

@ -175,7 +175,6 @@ abstract class SingleSelectField extends SelectField
*/
public function performReadonlyTransformation()
{
/** @var SingleLookupField $field */
$field = $this->castedCopy(SingleLookupField::class);
$field->setSource($this->getSource());
$field->setReadonly(true);

View File

@ -372,7 +372,6 @@ class TimeField extends TextField
*/
public function performReadonlyTransformation()
{
/** @var TimeField_Readonly $result */
$result = $this->castedCopy(TimeField_Readonly::class);
$result
->setValue(false)

View File

@ -458,7 +458,6 @@ class TreeDropdownField extends FormField
$this->populateIDs();
}
/** @var DataObject|Hierarchy $obj */
$obj = null;
$sourceObject = $this->getSourceObject();
@ -837,7 +836,6 @@ class TreeDropdownField extends FormField
*/
public function performReadonlyTransformation()
{
/** @var TreeDropdownField_Readonly $copy */
$copy = $this->castedCopy(TreeDropdownField_Readonly::class);
$copy->setKeyField($this->getKeyField());
$copy->setLabelField($this->getLabelField());
@ -846,10 +844,6 @@ class TreeDropdownField extends FormField
return $copy;
}
/**
* @param string|FormField $classOrCopy
* @return FormField
*/
public function castedCopy($classOrCopy)
{
$field = $classOrCopy;

View File

@ -281,7 +281,6 @@ class TreeMultiselectField extends TreeDropdownField
*/
public function performReadonlyTransformation()
{
/** @var TreeMultiselectField_Readonly $copy */
$copy = $this->castedCopy(TreeMultiselectField_Readonly::class);
$copy->setKeyField($this->getKeyField());
$copy->setLabelField($this->getLabelField());

View File

@ -29,6 +29,12 @@ use Traversable;
* - sort
* - filter
* - exclude
*
* @template T
* @implements SS_List<T>
* @implements Filterable<T>
* @implements Sortable<T>
* @implements Limitable<T>
*/
class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, Limitable
{
@ -45,13 +51,12 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
/**
* Holds the items in the list
*
* @var array
* @var array<array-key, T>
*/
protected $items = [];
/**
*
* @param array $items - an initial array to fill this object with
* @param array<array-key, T> $items - an initial array to fill this object with
*/
public function __construct(array $items = [])
{
@ -62,14 +67,14 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
/**
* Underlying type class for this list
*
* @var string
* @var class-string<T>|null
*/
protected $dataClass = null;
/**
* Return the class of items in this list, by looking at the first item inside it.
*
* @return string
* @return class-string<T>|null
*/
public function dataClass()
{
@ -85,7 +90,7 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
/**
* Hint this list to a specific type
*
* @param string $class
* @param class-string<T> $class
* @return $this
*/
public function setDataClass($class)
@ -116,6 +121,8 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
/**
* Returns an Iterator for this ArrayList.
* This function allows you to use ArrayList in foreach loops
*
* @return Traversable<T>
*/
public function getIterator(): Traversable
{
@ -130,8 +137,6 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
/**
* Return an array of the actual items that this ArrayList contains.
*
* @return array
*/
public function toArray()
{
@ -164,8 +169,6 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
/**
* Return this list as an array and every object it as an sub array as well
*
* @return array
*/
public function toNestedArray()
{
@ -263,7 +266,7 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
* Merges with another array or list by pushing all the items in it onto the
* end of this list.
*
* @param array|object $with
* @param iterable $with
*/
public function merge($with)
{
@ -342,11 +345,6 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
return array_shift($this->items);
}
/**
* Returns the first item in the list
*
* @return mixed
*/
public function first()
{
if (empty($this->items)) {
@ -356,11 +354,6 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
return reset($this->items);
}
/**
* Returns the last item in the list
*
* @return mixed
*/
public function last()
{
if (empty($this->items)) {
@ -425,7 +418,7 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
/**
* Reverses an {@link ArrayList}
*
* @return ArrayList
* @return static<T>
*/
public function reverse()
{
@ -479,7 +472,6 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
*
* Note that columns may be double quoted as per ANSI sql standard
*
* @return static
* @see SS_List::sort()
* @example $list->sort('Name'); // default ASC sorting
* @example $list->sort('Name DESC'); // DESC sorting
@ -596,8 +588,7 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
* Find the first item of this list where the given key = value
*
* @param string $key
* @param string $value
* @return mixed
* @param mixed $value
*/
public function find($key, $value)
{
@ -607,7 +598,6 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
/**
* Filter the list to include items with these characteristics
*
* @return ArrayList
* @see Filterable::filter()
* @example $list->filter('Name', 'bob'); // only bob in the list
* @example $list->filter('Name', array('aziz', 'bob'); // aziz and bob in list
@ -645,7 +635,6 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
* $list = $list->filterAny('Name:PartialMatch', 'sam');
*
* @param string|array See {@link filter()}
* @return static
*/
public function filterAny()
{
@ -656,7 +645,6 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
/**
* Exclude the list to not contain items with these characteristics
*
* @return ArrayList
* @see SS_List::exclude()
* @example $list->exclude('Name', 'bob'); // exclude bob from list
* @example $list->exclude('Name', array('aziz', 'bob'); // exclude aziz and bob from list
@ -694,6 +682,7 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
* $list = $list->excludeAny('Name:PartialMatch', 'sam');
*
* @param string|array See {@link filter()}
* @return static<T>
*/
public function excludeAny(): static
{
@ -703,6 +692,7 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
/**
* Apply the appropriate filtering or excluding
* @return static<T>
*/
protected function filterOrExclude(array $filters, bool $inclusive = true, bool $any = false): static
{
@ -740,7 +730,6 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
foreach ($this->items as $item) {
$matches = [];
foreach ($filters as $filterKey => $filterValue) {
/** @var SearchFilter $searchFilter */
$searchFilter = $searchFilters[$filterKey];
$extractedValue = $this->extractValue($item, $searchFilter->getFullName());
$hasMatch = null;
@ -840,7 +829,6 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
* Filter this list to only contain the given Primary IDs
*
* @param array $ids Array of integers, will be automatically cast/escaped.
* @return ArrayList
*/
public function byIDs($ids)
{
@ -864,7 +852,7 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
*
* @example $list = $list->filterByCallback(function($item, $list) { return $item->Age == 9; })
* @param callable $callback
* @return ArrayList
* @return static<T>
*/
public function filterByCallback($callback)
{
@ -901,6 +889,7 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
/**
* Returns item stored in list with index $key
* @return T|null
*/
public function offsetGet(mixed $offset): mixed
{

View File

@ -167,8 +167,6 @@ abstract class DBSchemaManager
// Clear update list for client code to mess around with
$this->schemaUpdateTransaction = [];
/** @var Exception $error */
$error = null;
try {
// Yield control to client code
$callback();
@ -425,7 +423,6 @@ abstract class DBSchemaManager
$fieldObj->setTable($table);
if ($fieldObj instanceof DBPrimaryKey) {
/** @var DBPrimaryKey $fieldObj */
$fieldObj->setAutoIncrement($hasAutoIncPK);
}

View File

@ -278,7 +278,6 @@ class MySQLDatabase extends Database implements TransactionManager
$queryParameters = [];
$totalCount = 0;
foreach ($lists as $class => $list) {
/** @var SQLSelect $query */
$query = $list->dataQuery()->query();
// There's no need to do all that joining

View File

@ -198,7 +198,6 @@ class DB
if (!Injector::inst()->has(HTTPRequest::class)) {
return;
}
/** @var HTTPRequest $request */
$request = Injector::inst()->get(HTTPRequest::class);
if ($name) {
$request->getSession()->set(self::ALT_DB_KEY, $name);
@ -226,7 +225,6 @@ class DB
if (!Injector::inst()->has(HTTPRequest::class)) {
return null;
}
/** @var HTTPRequest $request */
$request = Injector::inst()->get(HTTPRequest::class);
// Skip if the session hasn't been started
if (!$request->getSession()->isStarted()) {

View File

@ -13,7 +13,8 @@ use Exception;
/**
* An extension that adds additional functionality to a {@link DataObject}.
*
* @property DataObject $owner
* @template T of DataObject
* @extends Extension<T>
*/
abstract class DataExtension extends Extension
{

View File

@ -35,6 +35,12 @@ use SilverStripe\ORM\Filters\SearchFilterable;
* - removeAll
*
* Subclasses of DataList may add other methods that have the same effect.
*
* @template T of DataObject
* @implements SS_List<T>
* @implements Filterable<T>
* @implements Sortable<T>
* @implements Limitable<T>
*/
class DataList extends ViewableData implements SS_List, Filterable, Sortable, Limitable
{
@ -49,7 +55,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
/**
* The DataObject class name that this data list is querying
*
* @var string
* @var class-string<T>
*/
protected $dataClass;
@ -63,7 +69,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
/**
* A cached Query to save repeated database calls. {@see DataList::getTemplateIteratorCount()}
*
* @var SilverStripe\ORM\Connect\Query
* @var Query
*/
protected $finalisedQuery;
@ -89,7 +95,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* Create a new DataList.
* No querying is done on construction, but the initial query schema is set up.
*
* @param string $dataClass - The DataObject class to query.
* @param class-string<T> $dataClass - The DataObject class to query.
*/
public function __construct($dataClass)
{
@ -102,7 +108,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
/**
* Get the dataClass name for this DataList, ie the DataObject ClassName
*
* @return string
* @return class-string<T>
*/
public function dataClass()
{
@ -150,7 +156,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* alterDataQuery
*
* @param callable $callback
* @return static
* @return static<T>
* @throws Exception
*/
public function alterDataQuery($callback)
@ -187,7 +193,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* Return a new DataList instance with the underlying {@link DataQuery} object changed
*
* @param DataQuery $dataQuery
* @return static
* @return static<T>
*/
public function setDataQuery(DataQuery $dataQuery)
{
@ -201,7 +207,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
*
* @param string|array $keyOrArray Either the single key to set, or an array of key value pairs to set
* @param mixed $val If $keyOrArray is not an array, this is the value to set
* @return static
* @return static<T>
*/
public function setDataQueryParam($keyOrArray, $val = null)
{
@ -242,7 +248,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
*
* @param string|array|SQLConditionGroup $filter Predicate(s) to set, as escaped SQL statements or
* paramaterised queries
* @return static
* @return static<T>
*/
public function where($filter)
{
@ -264,7 +270,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
*
* @param string|array|SQLConditionGroup $filter Predicate(s) to set, as escaped SQL statements or
* paramaterised queries
* @return static
* @return static<T>
*/
public function whereAny($filter)
{
@ -336,7 +342,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* Return a new DataList instance with distinct records or not
*
* @param bool $value
* @return static
* @return static<T>
*/
public function distinct($value)
{
@ -446,6 +452,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
*
* This method accepts raw SQL so could be vulnerable to SQL injection attacks if used incorrectly,
* it's preferable to use sort() instead which does not allow raw SQL
* @return static<T>
*/
public function orderBy(string $orderBy): static
{
@ -473,7 +480,6 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* ->filter('Field:not', null) will generate '"Field" IS NOT NULL'
*
* @param string|array Escaped SQL statement. If passed as array, all keys and values will be escaped internally
* @return $this
*/
public function filter()
{
@ -499,7 +505,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* Return a new instance of the list with an added filter
*
* @param array $filterArray
* @return $this
* @return static<T>
*/
public function addFilter($filterArray)
{
@ -535,7 +541,6 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* // SQL: WHERE (("Name" IN ('bob', 'phil')) OR ("Age" IN ('21', '43'))
*
* @param string|array See {@link filter()}
* @return static
*/
public function filterAny()
{
@ -580,7 +585,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
*
* @example $list = $list->filterByCallback(function($item, $list) { return $item->Age == 9; })
* @param callable $callback
* @return ArrayList (this may change in future implementations)
* @return ArrayList<T>
*/
public function filterByCallback($callback)
{
@ -590,7 +595,6 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
gettype($callback)
));
}
/** @var ArrayList $output */
$output = ArrayList::create();
foreach ($this as $item) {
if (call_user_func($callback, $item, $this)) {
@ -620,7 +624,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* @param string $columnName Quoted column name (by reference)
* @param bool $linearOnly Set to true to restrict to linear relations only. Set this
* if this relation will be used for sorting, and should not include duplicate rows.
* @return $this DataList with this relation applied
* @return static<T> DataList with this relation applied
*/
public function applyRelation($field, &$columnName = null, $linearOnly = false)
{
@ -677,8 +681,6 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
*
* @param string|array
* @param string [optional]
*
* @return $this
*/
public function exclude()
{
@ -718,7 +720,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* @param string|array
* @param string [optional]
*
* @return $this
* @return static<T>
*/
public function excludeAny()
{
@ -747,8 +749,8 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
*
* The $list passed needs to contain the same dataclass as $this
*
* @param DataList $list
* @return static
* @param DataList<DataObject> $list
* @return static<T>
* @throws InvalidArgumentException
*/
public function subtract(DataList $list)
@ -772,7 +774,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* will cause the query to appear first. The default is 20, and joins created automatically by the
* ORM have a value of 10.
* @param array $parameters Any additional parameters if the join is a parameterised subquery
* @return static
* @return static<T>
*/
public function innerJoin($table, $onClause, $alias = null, $order = 20, $parameters = [])
{
@ -791,7 +793,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* will cause the query to appear first. The default is 20, and joins created automatically by the
* ORM have a value of 10.
* @param array $parameters Any additional parameters if the join is a parameterised subquery
* @return static
* @return static<T>
*/
public function leftJoin($table, $onClause, $alias = null, $order = 20, $parameters = [])
{
@ -810,7 +812,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* will cause the query to appear first. The default is 20, and joins created automatically by the
* ORM have a value of 10.
* @param array $parameters Any additional parameters if the join is a parameterised subquery
* @return static
* @return static<T>
*/
public function rightJoin($table, $onClause, $alias = null, $order = 20, $parameters = [])
{
@ -822,8 +824,6 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
/**
* Return an array of the actual items that this DataList contains at this stage.
* This is when the query is actually executed.
*
* @return array
*/
public function toArray()
{
@ -852,12 +852,6 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
return $result;
}
/**
* Walks the list using the specified callback
*
* @param callable $callback
* @return $this
*/
public function each($callback)
{
foreach ($this as $row) {
@ -894,7 +888,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* If called without $row['ID'] set, then a new object will be created rather than rehydrated.
*
* @param array $row
* @return DataObject
* @return T
*/
public function createDataObject($row)
{
@ -946,6 +940,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
/**
* Returns an Iterator for this DataList.
* This function allows you to use DataLists in foreach loops
* @return Traversable<T>
*/
public function getIterator(): Traversable
{
@ -1048,10 +1043,10 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
foreach ($this->eagerLoadRelationChains as $relationChain) {
$parentDataClass = $this->dataClass();
$parentIDs = $topLevelIDs;
/** @var Query|array<DataObject|EagerLoadedList> */
$parentRelationData = $query;
$chainToDate = [];
foreach (explode('.', $relationChain) as $relationName) {
/** @var Query|array<DataObject|EagerLoadedList> $parentRelationData */
$chainToDate[] = $relationName;
list(
$relationDataClass,
@ -1457,6 +1452,8 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
*
* IMPORTANT: Calling eagerLoad() will cause any relations on DataObjects to be returned as an EagerLoadedList
* instead of a subclass of DataList such as HasManyList i.e. MyDataObject->MyHasManyRelation() returns an EagerLoadedList
*
* @return static<T>
*/
public function eagerLoad(...$relationChains): static
{
@ -1548,11 +1545,9 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
/**
* Returns the first item in this DataList (instanceof DataObject)
* Returns the first item in this DataList
*
* The object returned is not cached, unlike {@link DataObject::get_one()}
*
* @return DataObject|null
*/
public function first()
{
@ -1568,11 +1563,9 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
}
/**
* Returns the last item in this DataList (instanceof DataObject)
* Returns the last item in this DataList
*
* The object returned is not cached, unlike {@link DataObject::get_one()}
*
* @return DataObject|null
*/
public function last()
{
@ -1605,7 +1598,6 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
*
* @param string $key
* @param string $value
* @return DataObject|null
*/
public function find($key, $value)
{
@ -1616,7 +1608,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* Restrict the columns to fetch into this DataList
*
* @param array $queriedColumns
* @return static
* @return static<T>
*/
public function setQueriedColumns($queriedColumns)
{
@ -1625,12 +1617,6 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
});
}
/**
* Filter this list to only contain the given Primary IDs
*
* @param array $ids Array of integers
* @return $this
*/
public function byIDs($ids)
{
return $this->filter('ID', $ids);
@ -1640,9 +1626,6 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* Return the first DataObject with the given ID
*
* The object returned is not cached, unlike {@link DataObject::get_by_id()}
*
* @param int $id
* @return DataObject|null
*/
public function byID($id)
{
@ -1683,7 +1666,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* Sets the ComponentSet to be the given ID list.
* Records will be added and deleted as appropriate.
*
* @param array $idList List of IDs.
* @param array<int> $idList List of IDs.
*/
public function setByIDList($idList)
{
@ -1716,7 +1699,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* Returns an array with both the keys and values set to the IDs of the records in this list.
* Does not respect sort order. Use ->column("ID") to get an ID list with the current sort.
*
* @return array
* @return array<int>
*/
public function getIDList()
{
@ -1734,13 +1717,13 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* DataList::Create(\SilverStripe\Security\Group::class)->relation("Members")
*
* @param string $relationName
* @return HasManyList|ManyManyList
* @return RelationList
*/
public function relation($relationName)
{
$ids = $this->column('ID');
$singleton = DataObject::singleton($this->dataClass);
/** @var HasManyList|ManyManyList $relation */
/** @var RelationList $relation */
$relation = $singleton->$relationName($ids);
return $relation;
}
@ -1753,8 +1736,8 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
/**
* Add a number of items to the component set.
*
* @param array $items Items to add, as either DataObjects or IDs.
* @return $this
* @param array<DataObject> $items Items to add, as either DataObjects or IDs.
* @return static<T>
*/
public function addMany($items)
{
@ -1767,8 +1750,8 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
/**
* Remove the items from this list with the given IDs
*
* @param array $idList
* @return $this
* @param array<int> $idList
* @return static<T>
*/
public function removeMany($idList)
{
@ -1782,7 +1765,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* Remove every element in this DataList matching the given $filter.
*
* @param string|array $filter - a sql type where filter
* @return $this
* @return static<T>
*/
public function removeByFilter($filter)
{
@ -1795,7 +1778,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
/**
* Shuffle the datalist using a random function provided by the SQL engine
*
* @return $this
* @return static<T>
*/
public function shuffle()
{
@ -1805,7 +1788,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
/**
* Remove every element in this DataList.
*
* @return $this
* @return static<T>
*/
public function removeAll()
{
@ -1819,7 +1802,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* This method are overloaded by HasManyList and ManyMany list to perform more sophisticated
* list manipulation
*
* @param mixed $item
* @param DataObject|int $item
*/
public function add($item)
{
@ -1831,7 +1814,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* Return a new item to add to this DataList.
*
* @param array $initialFields
* @return DataObject
* @return T
*/
public function newObject($initialFields = null)
{
@ -1867,7 +1850,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
/**
* Reverses a list of items.
*
* @return static
* @return static<T>
*/
public function reverse()
{
@ -1888,6 +1871,8 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
* Returns item stored in list with index $key
*
* The object returned is not cached, unlike {@link DataObject::get_one()}
*
* @return T|null
*/
public function offsetGet(mixed $key): ?DataObject
{
@ -1922,7 +1907,7 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
*
* @param int $chunkSize
* @throws InvalidArgumentException If `$chunkSize` has an invalid size.
* @return Generator|DataObject[]
* @return iterable<T>
*/
public function chunkedFetch(int $chunkSize = 1000): iterable
{

View File

@ -109,7 +109,6 @@ use stdClass;
*/
class DataObject extends ViewableData implements DataObjectInterface, i18nEntityProvider, Resettable
{
/**
* Human-readable singular name.
* @var string
@ -525,7 +524,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
// Create unsaved raw duplicate
$map = $this->toMap();
unset($map['Created']);
/** @var static $clone */
$clone = Injector::inst()->create(static::class, $map, false, $this->getSourceQueryParams());
$clone->ID = 0;
@ -628,7 +626,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$source = $sourceObject->getComponents($relation);
$dest = $destinationObject->getComponents($relation);
/** @var DataObject $item */
foreach ($source as $item) {
// Don't write on duplicate; Wait until ParentID is available later.
// writeRelations() will eventually write these records when converting
@ -742,9 +739,10 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* there is no record, or the record has no ID. In this case, we do have an ID but
* we still need to repopulate the defaults.
*
* @param string $newClassName The name of the new class
* @template T of DataObject
* @param class-string<T> $newClassName The name of the new class
*
* @return DataObject The new instance of the new class, The exact type will be of the class name provided.
* @return T The new instance of the new class, The exact type will be of the class name provided.
*/
public function newClassInstance($newClassName)
{
@ -963,7 +961,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* Returns the associated database record - in this case, the object itself.
* This is included so that you can call $dataOrController->data() and get a DataObject all the time.
*
* @return DataObject Associated database record
* @return static Associated database record
*/
public function data()
{
@ -1012,7 +1010,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* whitelist the allowed keys.
*
* @param array $data A map of field name to data values to update.
* @return DataObject $this
* @return static $this
*/
public function update($data)
{
@ -1021,7 +1019,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if (strpos($key ?? '', '.') !== false) {
$relations = explode('.', $key ?? '');
$fieldName = array_pop($relations);
/** @var static $relObj */
$relObj = $this;
$relation = null;
foreach ($relations as $i => $relation) {
@ -1029,6 +1026,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
// as the updater wouldn't know which object to write to (or create)
if ($relObj->$relation() instanceof DataObject) {
$parentObj = $relObj;
/** @var static $relObj */
$relObj = $relObj->$relation();
// If the intermediate relationship objects haven't been created, then write them
if ($i < sizeof($relations ?? []) - 1 && !$relObj->ID || (!$relObj->ID && $parentObj !== $this)) {
@ -1073,7 +1071,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* use the write() method.
*
* @param array $data A map of field name to data values to update.
* @return DataObject $this
* @return static $this
*/
public function castedUpdate($data)
{
@ -1097,7 +1095,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* Caution: Does not delete the merged object.
* Caution: Does now overwrite Created date on the original object.
*
* @param DataObject $rightObj
* @param static $rightObj Object to merge in
* @param string $priority left|right Determines who wins in case of a conflict (optional)
* @param bool $includeRelations Merge any existing relations (optional)
* @param bool $overwriteWithEmpty Overwrite existing left values with empty right values.
@ -1151,7 +1149,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if ($includeRelations) {
if ($manyMany = $this->manyMany()) {
foreach ($manyMany as $relationship => $class) {
/** @var DataObject $leftComponents */
$leftComponents = $leftObj->getManyManyComponents($relationship);
$rightComponents = $rightObj->getManyManyComponents($relationship);
if ($rightComponents && $rightComponents->exists()) {
@ -1263,7 +1260,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
*
* @param bool $recursive True if recursive
* @param ArrayList $list Optional list to add items to
* @return ArrayList list of objects
* @return ArrayList<DataObject> list of objects
*/
public function findCascadeDeletes($recursive = true, $list = null)
{
@ -1303,7 +1300,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* Called by the constructor when creating new records.
*
* @uses DataExtension::populateDefaults()
* @return DataObject $this
* @return static $this
*/
public function populateDefaults()
{
@ -1654,7 +1651,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
*
* @param bool $recursive Recursively write components
* @param array $skip List of DataObject references to skip
* @return DataObject $this
* @return static $this
*/
public function writeComponents($recursive = false, $skip = [])
{
@ -2339,7 +2336,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* Generates a SearchContext to be used for building and processing
* a generic search form for properties on this object.
*
* @return SearchContext
* @return SearchContext<static>
*/
public function getDefaultSearchContext()
{
@ -3306,7 +3303,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @param string|array $limit A limit expression to be inserted into the LIMIT clause.
* @param string $containerClass The container class to return the results in.
*
* @return DataList The objects matching the filter, in the class specified by $containerClass
* @return DataList<static> The objects matching the filter, in the class specified by $containerClass
*/
public static function get(
$callerClass = null,
@ -3372,7 +3369,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @param boolean $cache Use caching
* @param string|array|null $sort Passed to DataList::sort() so that DataList::first() returns the desired item
*
* @return DataObject|null The first item matching the query
* @return static|null The first item matching the query
*/
public static function get_one($callerClass = null, $filter = "", $cache = true, $sort = "")
{
@ -3423,7 +3420,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
*
* @param boolean $persistent When true will also clear persistent data stored in the Cache system.
* When false will just clear session-local cached data
* @return DataObject $this
* @return static $this
*/
public function flushCache($persistent = true)
{
@ -3524,7 +3521,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
/**
* Get the base class for this object
*
* @return string
* @return class-string<DataObject>
*/
public function baseClass()
{
@ -4356,7 +4353,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @param bool $recursive True if recursive
* @param ArrayList $list If specified, items will be added to this list. If not, a new
* instance of ArrayList will be constructed and returned
* @return ArrayList The list of related objects
* @return ArrayList<DataObject> The list of related objects
*/
public function findRelatedObjects($source, $recursive = true, $list = null)
{
@ -4391,7 +4388,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
// Recurse if necessary
if ($recursive) {
foreach ($newItems as $item) {
/** @var DataObject $item */
$item->findRelatedObjects($source, true, $list);
}
}
@ -4404,8 +4400,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* Items already present in the list will be skipped.
*
* @param ArrayList $list Items to merge into
* @param mixed $items List of new items to merge
* @return ArrayList List of all newly added items that did not already exist in $list
* @param iterable<DataObject>|DataObject|null $items List of new items to merge
* @return ArrayList<DataObject> List of all newly added items that did not already exist in $list
*/
public function mergeRelatedObjects($list, $items)
{
@ -4417,7 +4413,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$items = [$items];
}
/** @var DataObject $item */
foreach ($items as $item) {
$this->mergeRelatedObject($list, $added, $item);
}
@ -4439,7 +4434,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
*/
public function getUniqueKey(): string
{
/** @var UniqueKeyInterface $service */
$service = Injector::inst()->get(UniqueKeyInterface::class);
$keyComponents = $this->getUniqueKeyComponents();

View File

@ -144,7 +144,7 @@ class DataObjectSchema
*
* @param string|object $class
*
* @return string
* @return class-string<DataObject>
* @throws InvalidArgumentException
*/
public function baseDataClass($class)
@ -261,7 +261,7 @@ class DataObjectSchema
*
* @param string $table
*
* @return string|null The FQN of the class, or null if not found
* @return class-string<DataObject>|null The FQN of the class, or null if not found
*/
public function tableClass($table)
{
@ -714,7 +714,7 @@ class DataObjectSchema
* @param string $candidateClass
* @param string $fieldName
*
* @return string
* @return class-string<DataObject>|null
*/
public function classForField($candidateClass, $fieldName)
{

View File

@ -1054,7 +1054,6 @@ class DataQuery
}
// Join table with associated has_one
/** @var DataObject $model */
$foreignKey = $schema->getRemoteJoinField($localClass, $localField, $type, $polymorphic);
$localIDColumn = $schema->sqlColumnForField($localClass, 'ID', $localPrefix);
if ($polymorphic) {

View File

@ -21,6 +21,13 @@ use Traversable;
*
* Note that when this list represents a relation, adding items to or removing items from this list will NOT
* affect the underlying relation in the database. This list is read-only.
*
* @template T of DataObject
* @implements Relation<T>
* @implements SS_List<T>
* @implements Filterable<T>
* @implements Sortable<T>
* @implements Limitable<T>
*/
class EagerLoadedList extends ViewableData implements Relation, SS_List, Filterable, Sortable, Limitable
{
@ -28,11 +35,13 @@ class EagerLoadedList extends ViewableData implements Relation, SS_List, Filtera
/**
* List responsible for instantiating the actual DataObject objects from eager-loaded data
* @var DataList<T>
*/
private DataList $dataList;
/**
* Underlying DataObject class for this list
* @var class-string<T>
*/
private string $dataClass;
@ -65,6 +74,9 @@ class EagerLoadedList extends ViewableData implements Relation, SS_List, Filtera
*/
private array $manyManyComponent = [];
/**
* @param class-string<T> $dataClass
*/
public function __construct(string $dataClass, string $dataListClass, int|array|null $foreignID = null, array $manyManyComponent = [])
{
if (!is_a($dataListClass, DataList::class, true)) {
@ -144,6 +156,8 @@ class EagerLoadedList extends ViewableData implements Relation, SS_List, Filtera
/**
* Get the dataClass name for this list, ie the DataObject ClassName
*
* @return class-string<T>
*/
public function dataClass(): string
{
@ -179,6 +193,9 @@ class EagerLoadedList extends ViewableData implements Relation, SS_List, Filtera
throw new BadMethodCallException("Can't change the foreign ID for an EagerLoadedList");
}
/**
* @return Traversable<T>
*/
public function getIterator(): Traversable
{
$limitedRows = $this->getFinalisedRows();
@ -354,6 +371,8 @@ class EagerLoadedList extends ViewableData implements Relation, SS_List, Filtera
/**
* Returns item stored in list with offset $key
*
* @return T|null
*/
public function offsetGet(mixed $key): ?DataObject
{
@ -493,6 +512,8 @@ class EagerLoadedList extends ViewableData implements Relation, SS_List, Filtera
/**
* Return a copy of this list which does not contain any items with any of these params
*
* @return static<T>
*/
public function excludeAny(...$args): static
{
@ -509,6 +530,7 @@ class EagerLoadedList extends ViewableData implements Relation, SS_List, Filtera
* Return a new instance of the list with an added filter
*
* @param array $filterArray
* @return static<T>
*/
public function addFilter($filterArray): static
{
@ -522,6 +544,7 @@ class EagerLoadedList extends ViewableData implements Relation, SS_List, Filtera
*
* The $list passed needs to contain the same dataclass as $this
*
* @return static<T>
* @throws InvalidArgumentException
*/
public function subtract(DataList $list): static
@ -577,7 +600,6 @@ class EagerLoadedList extends ViewableData implements Relation, SS_List, Filtera
if (is_array($value) && empty($value)) {
throw new InvalidArgumentException("Cannot filter $column against an empty set");
}
/** @var SearchFilter $searchFilter */
$searchFilter = $searchFilters[$column];
$extractedValue = $this->extractValue($row, $this->standardiseColumn($searchFilter->getFullName()));
$doesMatch = $searchFilter->matches($extractedValue);
@ -612,6 +634,9 @@ class EagerLoadedList extends ViewableData implements Relation, SS_List, Filtera
return null;
}
/**
* @return ArrayList<T>
*/
public function filterByCallback($callback): ArrayList
{
if (!is_callable($callback)) {
@ -708,6 +733,8 @@ class EagerLoadedList extends ViewableData implements Relation, SS_List, Filtera
/**
* Shuffle the items in this list
*
* @return static<T>
*/
public function shuffle(): static
{
@ -862,6 +889,7 @@ class EagerLoadedList extends ViewableData implements Relation, SS_List, Filtera
* At a minimum, $row['ID'] must be set. Unsaved records cannot be eager loaded.
*
* @param array $row
* @return T
*/
public function createDataObject($row): DataObject
{

View File

@ -11,6 +11,9 @@ namespace SilverStripe\ORM;
* @see SS_List
* @see Sortable
* @see Limitable
*
* @template T
* @extends SS_List<T>
*/
interface Filterable extends SS_List
{
@ -26,13 +29,14 @@ interface Filterable extends SS_List
/**
* Return a new instance of this list that only includes items with these characteristics
*
* @return Filterable
* @example $list = $list->filter('Name', 'bob'); // only bob in the list
* @example $list = $list->filter('Name', array('aziz', 'bob'); // aziz and bob in list
* @example $list = $list->filter(array('Name'=>'bob, 'Age'=>21)); // bob with the age 21
* @example $list = $list->filter(array('Name'=>'bob, 'Age'=>array(21, 43))); // bob with the Age 21 or 43
* @example $list = $list->filter(array('Name'=>array('aziz','bob'), 'Age'=>array(21, 43)));
* // aziz with the age 21 or 43 and bob with the Age 21 or 43
*
* @return static<T>
*/
public function filter();
@ -56,20 +60,21 @@ interface Filterable extends SS_List
* // SQL: WHERE (("Name" IN ('bob', 'phil')) OR ("Age" IN ('21', '43'))
*
* @param string|array See {@link filter()}
* @return static
* @return static<T>
*/
public function filterAny();
/**
* Return a new instance of this list that excludes any items with these characteristics
*
* @return Filterable
* @example $list = $list->exclude('Name', 'bob'); // exclude bob from list
* @example $list = $list->exclude('Name', array('aziz', 'bob'); // exclude aziz and bob from list
* @example $list = $list->exclude(array('Name'=>'bob, 'Age'=>21)); // exclude bob that has Age 21
* @example $list = $list->exclude(array('Name'=>'bob, 'Age'=>array(21, 43))); // exclude bob with Age 21 or 43
* @example $list = $list->exclude(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43)));
* // bob age 21 or 43, phil age 21 or 43 would be excluded
*
* @return static<T>
*/
public function exclude();
@ -80,7 +85,7 @@ interface Filterable extends SS_List
*
* @example $list = $list->filterByCallback(function($item, $list) { return $item->Age == 9; })
* @param callable $callback
* @return Filterable
* @return SS_List<T>
*/
public function filterByCallback($callback);
@ -88,7 +93,7 @@ interface Filterable extends SS_List
* Return the first item with the given ID
*
* @param int $id
* @return mixed
* @return T|null
*/
public function byID($id);
@ -96,7 +101,7 @@ interface Filterable extends SS_List
* Filter this list to only contain the given Primary IDs
*
* @param array $ids Array of integers
* @return SS_List
* @return static<T>
*/
public function byIDs($ids);
}

View File

@ -7,6 +7,10 @@ use SilverStripe\View\ArrayData;
/**
* A list decorator that allows a list to be grouped into sub-lists by common
* values of a field.
*
* @template TList
* @template T
* @extends ListDecorator<TList, T>
*/
class GroupedList extends ListDecorator
{
@ -40,7 +44,7 @@ class GroupedList extends ListDecorator
*
* @param string $index
* @param string $children Name of the control under which children can be iterated on
* @return ArrayList
* @return ArrayList<ArrayData>
*/
public function GroupedBy($index, $children = 'Children')
{

View File

@ -6,6 +6,9 @@ use InvalidArgumentException;
/**
* Subclass of {@link DataList} representing a has_many relation.
*
* @template T of DataObject
* @extends RelationList<T>
*/
class HasManyList extends RelationList
{
@ -21,7 +24,7 @@ class HasManyList extends RelationList
* {@link DataList} methods. Addition arguments are used to support {@link add()}
* and {@link remove()} methods.
*
* @param string $dataClass The class of the DataObjects that this will list.
* @param class-string<T> $dataClass The class of the DataObjects that this will list.
* @param string $foreignKey The name of the foreign key field to set the ID filter against.
*/
public function __construct($dataClass, $foreignKey)
@ -43,7 +46,7 @@ class HasManyList extends RelationList
/**
* @param null|int|array|string $id
* @return array
* @return array|null
*/
protected function foreignIDFilter($id = null)
{

View File

@ -23,8 +23,8 @@ use SilverStripe\View\ViewableData;
* obvious example of this is SiteTree.
*
* @property int $ParentID
* @property DataObject|Hierarchy $owner
* @method DataObject Parent()
* @extends DataExtension<DataObject&static>
*/
class Hierarchy extends DataExtension
{
@ -119,7 +119,6 @@ class Hierarchy extends DataExtension
public function validate(ValidationResult $validationResult)
{
// The object is new, won't be looping.
/** @var DataObject|Hierarchy $owner */
$owner = $this->owner;
if (!$owner->ID) {
return;
@ -190,7 +189,7 @@ class Hierarchy extends DataExtension
/**
* Get the children for this DataObject filtered by canView()
*
* @return SS_List
* @return SS_List<DataObject&static>
*/
public function Children()
{
@ -212,7 +211,7 @@ class Hierarchy extends DataExtension
/**
* Return all children, including those 'not in menus'.
*
* @return DataList
* @return DataList<DataObject&static>
*/
public function AllChildren()
{
@ -226,7 +225,7 @@ class Hierarchy extends DataExtension
* - Modified children will be marked as "ModifiedOnStage"
* - Everything else has "SameOnStage" set, as an indicator that this information has been looked up.
*
* @return ArrayList
* @return ArrayList<DataObject&static>
*/
public function AllChildrenIncludingDeleted()
{
@ -252,7 +251,7 @@ class Hierarchy extends DataExtension
/**
* Return all the children that this page had, including pages that were deleted from both stage & live.
*
* @return DataList
* @return DataList<DataObject&static>
* @throws Exception
*/
public function AllHistoricalChildren()
@ -293,7 +292,6 @@ class Hierarchy extends DataExtension
*/
public function numChildren($cache = true)
{
$baseClass = $this->owner->baseClass();
$cacheType = 'numChildren';
$id = $this->owner->ID;
@ -355,7 +353,7 @@ class Hierarchy extends DataExtension
return;
}
/** @var Versioned|DataObject $singleton */
/** @var DataObject&static $dummyObject */
$dummyObject = DataObject::singleton($baseClass);
$baseTable = $dummyObject->baseTable();
@ -431,11 +429,10 @@ class Hierarchy extends DataExtension
* @param bool $showAll Include all of the elements, even those not shown in the menus. Only applicable when
* extension is applied to {@link SiteTree}.
* @param bool $skipParentIDFilter Set to true to suppress the ParentID and ID where statements.
* @return DataList
* @return DataList<DataObject&static>
*/
public function stageChildren($showAll = false, $skipParentIDFilter = false)
{
/** @var DataObject|Hierarchy $owner */
$owner = $this->owner;
$hideFromHierarchy = $owner->config()->hide_from_hierarchy;
$hideFromCMSTree = $owner->config()->hide_from_cms_tree;
@ -477,7 +474,7 @@ class Hierarchy extends DataExtension
* @param bool $showAll Include all of the elements, even those not shown in the menus. Only
* applicable when extension is applied to {@link SiteTree}.
* @param bool $onlyDeletedFromStage Only return items that have been deleted from stage
* @return DataList
* @return DataList<DataObject&static>
* @throws Exception
*/
public function liveChildren($showAll = false, $onlyDeletedFromStage = false)
@ -515,7 +512,7 @@ class Hierarchy extends DataExtension
* is returned.
*
* @param string $filter
* @return DataObject
* @return DataObject&static
*/
public function getParent($filter = null)
{
@ -535,7 +532,7 @@ class Hierarchy extends DataExtension
* Return all the parents of this class in a set ordered from the closest to furtherest parent.
*
* @param bool $includeSelf
* @return ArrayList
* @return ArrayList<DataObject&static>
*/
public function getAncestors($includeSelf = false)
{

View File

@ -187,7 +187,7 @@ class MarkedSet
* Get children from this node
*
* @param DataObject $node
* @return SS_List
* @return SS_List<DataObject>
*/
protected function getChildren(DataObject $node)
{
@ -453,7 +453,6 @@ class MarkedSet
$nodeCountThreshold = $this->getNodeCountThreshold();
// Add root node, not-expanded by default
/** @var DataObject|Hierarchy $rootNode */
$rootNode = $this->rootNode;
$this->clearMarks();
$this->markUnexpanded($rootNode);
@ -464,7 +463,6 @@ class MarkedSet
$children = $this->markChildren($node);
if ($nodeCountThreshold && sizeof($this->markedNodes ?? []) > $nodeCountThreshold) {
// Undo marking children as opened since they're lazy loaded
/** @var DataObject|Hierarchy $child */
foreach ($children as $child) {
$this->markClosed($child);
}
@ -542,7 +540,7 @@ class MarkedSet
* Mark all children of the given node that match the marking filter.
*
* @param DataObject $node Parent node
* @return array List of children marked by this operation
* @return array<DataObject> List of children marked by this operation
*/
protected function markChildren(DataObject $node)
{

View File

@ -11,6 +11,9 @@ namespace SilverStripe\ORM;
* @see SS_List
* @see Sortable
* @see Filterable
*
* @template T
* @implements SS_List<T>
*/
interface Limitable extends SS_List
{
@ -23,6 +26,7 @@ interface Limitable extends SS_List
* If `$length` is null, then no limit is applied. If `$length` is 0, then an empty list is returned.
*
* @throws InvalidArgumentException if $length or offset are negative
* @return static<T>
*/
public function limit(?int $length, int $offset = 0): Limitable;
}

View File

@ -10,15 +10,24 @@ use Traversable;
* A base class for decorators that wrap around a list to provide additional
* functionality. It passes through list methods to the underlying list
* implementation.
*
* @template TList of SS_List&Sortable&Filterable&Limitable
* @template T
* @implements SS_List<T>
* @implements Sortable<T>
* @implements Filterable<T>
* @implements Limitable<T>
*/
abstract class ListDecorator extends ViewableData implements SS_List, Sortable, Filterable, Limitable
{
/**
* @var SS_List
* @var TList<T>
*/
protected SS_List&Sortable&Filterable&Limitable $list;
/**
* @param TList<T> $list
*/
public function __construct(SS_List&Sortable&Filterable&Limitable $list)
{
$this->setList($list);
@ -26,6 +35,9 @@ abstract class ListDecorator extends ViewableData implements SS_List, Sortable,
parent::__construct();
}
/**
* @return TList<T>
*/
public function getList(): SS_List&Sortable&Filterable&Limitable
{
return $this->list;
@ -37,7 +49,10 @@ abstract class ListDecorator extends ViewableData implements SS_List, Sortable,
* Useful for keeping a decorator/paginated list configuration intact while modifying
* the underlying list.
*
* @return SS_List
* @template TListA
* @template TA
* @param TListA<TA> $list
* @return static<TListA, TA>
*/
public function setList(SS_List&Sortable&Filterable&Limitable $list): self
{
@ -51,6 +66,9 @@ abstract class ListDecorator extends ViewableData implements SS_List, Sortable,
return $this->list->offsetExists($key);
}
/**
* @return T
*/
public function offsetGet(mixed $key): mixed
{
return $this->list->offsetGet($key);
@ -86,6 +104,9 @@ abstract class ListDecorator extends ViewableData implements SS_List, Sortable,
$this->list->remove($itemObject);
}
/**
* @return Traversable<T>
*/
public function getIterator(): Traversable
{
return $this->list->getIterator();
@ -106,6 +127,9 @@ abstract class ListDecorator extends ViewableData implements SS_List, Sortable,
return $this->list->last();
}
/**
* @return int
*/
public function TotalItems()
{
return $this->list->count();
@ -141,6 +165,9 @@ abstract class ListDecorator extends ViewableData implements SS_List, Sortable,
return $this->list->columnUnique($value);
}
/**
* @return TList<T>
*/
public function each($callback)
{
return $this->list->each($callback);
@ -164,6 +191,8 @@ abstract class ListDecorator extends ViewableData implements SS_List, Sortable,
* @example $list->sort('Name DESC'); // DESC sorting
* @example $list->sort('Name', 'ASC');
* @example $list->sort(array('Name'=>'ASC,'Age'=>'DESC'));
*
* @return TList<T>
*/
public function sort()
{
@ -182,6 +211,8 @@ abstract class ListDecorator extends ViewableData implements SS_List, Sortable,
* @example $list->filter('Name', array('aziz', 'bob'); // aziz and bob in list
* @example $list->filter(array('Name'=>'bob, 'Age'=>21)); // bob or someone with Age 21
* @example $list->filter(array('Name'=>'bob, 'Age'=>array(21, 43))); // bob or anyone with Age 21 or 43
*
* @return TList<T>
*/
public function filter()
{
@ -208,7 +239,8 @@ abstract class ListDecorator extends ViewableData implements SS_List, Sortable,
* // SQL: WHERE (("Name" IN ('bob', 'phil')) OR ("Age" IN ('21', '43'))
*
* @param string|array See {@link filter()}
* @return DataList
*
* @return TList<T>
*/
public function filterAny()
{
@ -222,7 +254,7 @@ abstract class ListDecorator extends ViewableData implements SS_List, Sortable,
*
* @example $list = $list->filterByCallback(function($item, $list) { return $item->Age == 9; })
* @param callable $callback
* @return ArrayList (this may change in future implementations)
* @return ArrayList<T>
*/
public function filterByCallback($callback)
{
@ -241,17 +273,14 @@ abstract class ListDecorator extends ViewableData implements SS_List, Sortable,
return $output;
}
/**
* @return TList<T>
*/
public function limit(?int $length, int $offset = 0): SS_List&Sortable&Filterable&Limitable
{
return $this->list->limit($length, $offset);
}
/**
* Return the first item with the given ID
*
* @param int $id
* @return mixed
*/
public function byID($id)
{
return $this->list->byID($id);
@ -261,7 +290,8 @@ abstract class ListDecorator extends ViewableData implements SS_List, Sortable,
* Filter this list to only contain the given Primary IDs
*
* @param array $ids Array of integers
* @return SS_List
*
* @return TList<T>
*/
public function byIDs($ids)
{
@ -275,6 +305,8 @@ abstract class ListDecorator extends ViewableData implements SS_List, Sortable,
* @example $list->exclude('Name', array('aziz', 'bob'); // exclude aziz and bob from list
* @example $list->exclude(array('Name'=>'bob, 'Age'=>21)); // exclude bob or someone with Age 21
* @example $list->exclude(array('Name'=>'bob, 'Age'=>array(21, 43))); // exclude bob or anyone with Age 21 or 43
*
* @return TList<T>
*/
public function exclude()
{

View File

@ -9,9 +9,13 @@ use SilverStripe\ORM\Queries\SQLDelete;
use SilverStripe\ORM\FieldType\DBComposite;
use InvalidArgumentException;
use Exception;
use SilverStripe\ORM\FieldType\DBField;
/**
* Subclass of {@link DataList} representing a many_many relation.
*
* @template T of DataObject
* @extends RelationList<T>
*/
class ManyManyList extends RelationList
{
@ -50,7 +54,7 @@ class ManyManyList extends RelationList
* the normal {@link DataList} methods. Addition arguments are used to
* support {@link add()} and {@link remove()} methods.
*
* @param string $dataClass The class of the DataObjects that this will list.
* @param class-string<T> $dataClass The class of the DataObjects that this will list.
* @param string $joinTable The name of the table whose entries define the content of this many_many relation.
* @param string $localKey The key in the join table that maps to the dataClass' PK.
* @param string $foreignKey The key in the join table that maps to joined class' PK.
@ -120,12 +124,6 @@ class ManyManyList extends RelationList
$this->dataQuery->addSelectFromTable($this->joinTable, $finalized);
}
/**
* Create a DataObject from the given SQL row.
*
* @param array $row
* @return DataObject
*/
public function createDataObject($row)
{
// remove any composed fields
@ -165,8 +163,6 @@ class ManyManyList extends RelationList
* relationship for some foreign ID
*
* @param int|null|string|array $id
*
* @return array
*/
protected function foreignIDFilter($id = null)
{
@ -285,7 +281,6 @@ class ManyManyList extends RelationList
];
}
/** @var DBField[] $fieldObjects */
$fieldObjects = [];
if ($extraFields && $this->extraFields) {
// Write extra field to manipluation in the same way
@ -293,6 +288,7 @@ class ManyManyList extends RelationList
foreach ($this->extraFields as $fieldName => $fieldSpec) {
// Skip fields without an assignment
if (array_key_exists($fieldName, $extraFields ?? [])) {
/** @var DBField $fieldObject */
$fieldObject = Injector::inst()->create($fieldSpec, $fieldName);
$fieldObject->setValue($extraFields[$fieldName]);
$fieldObject->writeToManipulation($manipulation[$this->joinTable]);
@ -462,7 +458,6 @@ class ManyManyList extends RelationList
],
];
/** @var DBField[] $fieldObjects */
$fieldObjects = [];
// Write extra field to manipluation in the same way
// that DataObject::prepareManipulationTable writes fields
@ -470,6 +465,7 @@ class ManyManyList extends RelationList
if (!array_key_exists($fieldName, $this->extraFields)) {
throw new InvalidArgumentException("Field '$fieldName' is not defined in many_many_extraFields for this relationship");
}
/** @var DBField $fieldObject */
$fieldObject = Injector::inst()->create($this->extraFields[$fieldName], $fieldName);
// Make sure the field assignment is not an array unless the field allows non-scalar values
if (is_array($value) && $fieldObject->scalarValueOnly()) {

View File

@ -9,6 +9,9 @@ use SilverStripe\Core\Injector\Injector;
/**
* ManyManyList backed by a dataobject join table
*
* @template T of DataObject
* @extends RelationList<T>
*/
class ManyManyThroughList extends RelationList
{
@ -21,15 +24,15 @@ class ManyManyThroughList extends RelationList
* Create a new ManyManyRelationList object. This relation will utilise an intermediary dataobject
* as a join table, unlike ManyManyList which scaffolds a table automatically.
*
* @param string $dataClass The class of the DataObjects that this will list.
* @example new ManyManyThroughList('Banner', 'PageBanner', 'BannerID', 'PageID');
*
* @param class-string<T> $dataClass The class of the DataObjects that this will list.
* @param string $joinClass Class name of the joined dataobject record
* @param string $localKey The key in the join table that maps to the dataClass' PK.
* @param string $foreignKey The key in the join table that maps to joined class' PK.
*
* @param array $extraFields Ignored for ManyManyThroughList
* @param string $foreignClass 'from' class
* @param string $parentClass Parent class (should be subclass of 'from')
* @example new ManyManyThroughList('Banner', 'PageBanner', 'BannerID', 'PageID');
*/
public function __construct(
$dataClass,
@ -55,10 +58,6 @@ class ManyManyThroughList extends RelationList
/**
* Don't apply foreign ID filter until getFinalisedQuery()
*
* @param array|integer $id (optional) An ID or an array of IDs - if not provided, will use the current ids as
* per getForeignID
* @return array Condition In array(SQL => parameters format)
*/
protected function foreignIDFilter($id = null)
{
@ -134,7 +133,6 @@ class ManyManyThroughList extends RelationList
// Rather than simple un-associating the record (as in has_many list)
// Delete the actual mapping row as many_many deletions behave.
/** @var DataObject $record */
foreach ($records as $record) {
$record->delete();
}
@ -144,6 +142,9 @@ class ManyManyThroughList extends RelationList
}
}
/**
* @return void
*/
public function removeAll()
{
// Get the IDs of records in the current list
@ -216,7 +217,6 @@ class ManyManyThroughList extends RelationList
$foreignKey = $this->manipulator->getForeignIDKey();
$hasManyList = $this->manipulator->getParentRelationship($this->dataQuery());
$records = $hasManyList->filter($localKey, $itemID);
/** @var DataObject $record */
foreach ($records as $record) {
if ($extraFields) {
foreach ($extraFields as $field => $value) {

View File

@ -9,6 +9,8 @@ use SilverStripe\ORM\Queries\SQLSelect;
/**
* Injected into DataQuery to augment getFinalisedQuery() with a join table
*
* @template TJoin of DataObject
*/
class ManyManyThroughQueryManipulator implements DataQueryManipulator
{
@ -18,7 +20,7 @@ class ManyManyThroughQueryManipulator implements DataQueryManipulator
/**
* DataObject that backs the joining table
*
* @var string
* @var class-string<TJoin>
*/
protected $joinClass;
@ -53,6 +55,10 @@ class ManyManyThroughQueryManipulator implements DataQueryManipulator
/**
* Build query manipulator for a given join table. Additional parameters (foreign key, etc)
* will be inferred at evaluation from query parameters set via the ManyManyThroughList
*
* @param class-string<TJoin> $joinClass
* @param string $foreignClass
* @param string $parentClass
*/
public function __construct(string $joinClass, string $localKey, string $foreignKey, string $foreignClass, string $parentClass)
{
@ -68,7 +74,7 @@ class ManyManyThroughQueryManipulator implements DataQueryManipulator
}
/**
* @return string
* @return class-string<TJoin>
*/
public function getJoinClass()
{
@ -76,7 +82,7 @@ class ManyManyThroughQueryManipulator implements DataQueryManipulator
}
/**
* @param mixed $joinClass
* @param class-string<TJoin> $joinClass
* @return $this
*/
public function setJoinClass($joinClass)
@ -152,7 +158,7 @@ class ManyManyThroughQueryManipulator implements DataQueryManipulator
* Get has_many relationship between parent and join table (for a given DataQuery)
*
* @param DataQuery $query
* @return HasManyList
* @return HasManyList<TJoin>
*/
public function getParentRelationship(DataQuery $query)
{
@ -307,7 +313,7 @@ class ManyManyThroughQueryManipulator implements DataQueryManipulator
/**
* @param string $parentClass
* @return ManyManyThroughQueryManipulator
* @return $this
*/
public function setParentClass($parentClass)
{

View File

@ -13,10 +13,13 @@ use Traversable;
/**
* A decorator that wraps around a data list in order to provide pagination.
*
* @template TList of SS_List
* @template T
* @extends ListDecorator<TList, T>
*/
class PaginatedList extends ListDecorator
{
protected $request;
protected $getVar = 'start';
@ -28,7 +31,7 @@ class PaginatedList extends ListDecorator
/**
* Constructs a new paginated list instance around a list.
*
* @param SS_List $list The list to paginate. The getRange method will
* @param TList<T> $list The list to paginate. The getRange method will
* be used to get the subset of objects to show.
* @param array|ArrayAccess $request Either a map of request parameters or
* request object that the pagination offset is read from.

View File

@ -10,6 +10,10 @@ use Traversable;
/**
* Represents a has_many list linked against a polymorphic relationship.
*
* @template T of DataObject
* @template TForeign of DataObject
* @extends HasManyList<T>
*/
class PolymorphicHasManyList extends HasManyList
{
@ -30,7 +34,7 @@ class PolymorphicHasManyList extends HasManyList
/**
* Retrieve the name of the class this (has_many) relation is filtered by
*
* @return string
* @return class-string<TForeign>
*/
public function getForeignClass()
{
@ -80,10 +84,10 @@ class PolymorphicHasManyList extends HasManyList
/**
* Create a new PolymorphicHasManyList relation list.
*
* @param string $dataClass The class of the DataObjects that this will list.
* @param class-string<T> $dataClass The class of the DataObjects that this will list.
* @param string $foreignField The name of the composite foreign (has_one) relation field. Used
* to generate the ID, Class, and Relation foreign keys.
* @param string $foreignClass Name of the class filter this relation is filtered against
* @param class-string<TForeign> $foreignClass Name of the class filter this relation is filtered against
*/
public function __construct($dataClass, $foreignField, $foreignClass)
{
@ -104,13 +108,6 @@ class PolymorphicHasManyList extends HasManyList
));
}
/**
* Adds the item to this relation.
*
* It does so by setting the relationFilters.
*
* @param DataObject|int $item The DataObject to be added, or its ID
*/
public function add($item)
{
if (is_numeric($item)) {
@ -155,12 +152,6 @@ class PolymorphicHasManyList extends HasManyList
$item->write();
}
/**
* Remove an item from this relation.
* Doesn't actually remove the item, it just clears the foreign key value.
*
* @param DataObject $item The DataObject to be removed
*/
public function remove($item)
{
if (!($item instanceof $this->dataClass)) {

View File

@ -12,6 +12,12 @@ use SilverStripe\ORM\FieldType\DBField;
* @method Relation relation($relationName)
* @method Relation forForeignID($id)
* @method string dataClass()
*
* @template T
* @extends SS_List<T>
* @extends Filterable<T>
* @extends Sortable<T>
* @extends Limitable<T>
*/
interface Relation extends SS_List, Filterable, Sortable, Limitable
{
@ -20,7 +26,7 @@ interface Relation extends SS_List, Filterable, Sortable, Limitable
* Sets the ComponentSet to be the given ID list.
* Records will be added and deleted as appropriate.
*
* @param array $idList List of IDs.
* @param array<int> $idList List of IDs.
*/
public function setByIDList($idList);
@ -29,7 +35,7 @@ interface Relation extends SS_List, Filterable, Sortable, Limitable
*
* Does not return the IDs for unsaved DataObjects
*
* @return array
* @return array<int>
*/
public function getIDList();

View File

@ -10,6 +10,10 @@ use SilverStripe\ORM\DB;
* A DataList that represents a relation.
*
* Adds the notion of a foreign ID that can be optionally set.
*
* @template T of DataObject
* @extends DataList<T>
* @implements Relation<T>
*/
abstract class RelationList extends DataList implements Relation
{
@ -34,8 +38,6 @@ abstract class RelationList extends DataList implements Relation
* every time they're called.
*
* Note that subclasses of RelationList must implement the callback for it to function
*
* @return this
*/
public function addCallbacks(): CallbackList
{
@ -75,8 +77,6 @@ abstract class RelationList extends DataList implements Relation
* when adding records to this list.
*
* Subclasses of RelationList must implement the callback for it to function
*
* @return this
*/
public function removeCallbacks(): CallbackList
{
@ -118,7 +118,7 @@ abstract class RelationList extends DataList implements Relation
*
* @param int|array $id An ID or an array of IDs.
*
* @return static
* @return static<T>
*/
public function forForeignID($id)
{

View File

@ -8,6 +8,10 @@ use IteratorAggregate;
/**
* An interface that a class can implement to be treated as a list container.
*
* @template T
* @extends ArrayAccess<array-key, T>
* @extends IteratorAggregate<array-key, T>
*/
interface SS_List extends ArrayAccess, Countable, IteratorAggregate
{
@ -15,7 +19,7 @@ interface SS_List extends ArrayAccess, Countable, IteratorAggregate
/**
* Returns all the items in the list in an array.
*
* @return array
* @return array<T>
*/
public function toArray();
@ -44,14 +48,14 @@ interface SS_List extends ArrayAccess, Countable, IteratorAggregate
/**
* Returns the first item in the list.
*
* @return mixed
* @return T|null
*/
public function first();
/**
* Returns the last item in the list.
*
* @return mixed
* @return T|null
*/
public function last();
@ -71,7 +75,7 @@ interface SS_List extends ArrayAccess, Countable, IteratorAggregate
*
* @param string $key
* @param mixed $value
* @return mixed
* @return T|null
*/
public function find($key, $value);
@ -87,7 +91,7 @@ interface SS_List extends ArrayAccess, Countable, IteratorAggregate
* Walks the list using the specified callback
*
* @param callable $callback
* @return $this
* @return static<T>
*/
public function each($callback);
}

View File

@ -20,7 +20,8 @@ use Exception;
* CAUTION: Will make all files in your /assets folder searchable by file name
* unless "File" is excluded from FulltextSearchable::enable().
*
* @see http://doc.silverstripe.org/framework/en/tutorials/4-site-search
* @template T of SiteTree|File
* @extends DataExtension<T>
*/
class FulltextSearchable extends DataExtension
{

View File

@ -42,6 +42,8 @@ use SilverStripe\ORM\DataQuery;
* to include.
*
* @see http://doc.silverstripe.com/doku.php?id=searchcontext
*
* @template T of object
*/
class SearchContext
{
@ -51,7 +53,7 @@ class SearchContext
* DataObject subclass to which search parameters relate to.
* Also determines as which object each result is provided.
*
* @var string
* @var class-string<T>
*/
protected $modelClass;
@ -84,7 +86,7 @@ class SearchContext
* in the form of a $_REQUEST object.
* CAUTION: All values should be treated as insecure client input.
*
* @param string $modelClass The base {@link DataObject} class that search properties related to.
* @param class-string<T> $modelClass The base {@link DataObject} class that search properties related to.
* Also used to generate a set of result objects based on this class.
* @param FieldList $fields Optional. FormFields mapping to {@link DataObject::$db} properties
* which are to be searched. Derived from modelclass using
@ -144,7 +146,7 @@ class SearchContext
* Falls back to {@link DataObject::$default_sort} if not provided.
* @param int|array|null $limit
* @param DataList $existingQuery
* @return DataList
* @return DataList<T>
* @throws Exception
*/
public function getQuery($searchParams, $sort = false, $limit = false, $existingQuery = null)
@ -163,6 +165,7 @@ class SearchContext
/**
* Perform a search on the passed DataList based on $this->searchParams.
* @return DataList<T>
*/
private function search(DataList $query): DataList
{
@ -185,6 +188,7 @@ class SearchContext
*
* @param array|bool|string $sort Database column to sort on.
* @param int|array|null $limit
* @return DataList<T>
*/
private function prepareQuery($sort, $limit, ?DataList $existingQuery): DataList
{
@ -248,6 +252,7 @@ class SearchContext
* Use the global general search for searching across multiple fields.
*
* @param string|array $searchPhrase
* @return DataList<T>
*/
private function generalFieldSearch(DataList $query, array $searchableFields, $searchPhrase): DataList
{
@ -293,6 +298,7 @@ class SearchContext
* Search against a single field
*
* @param string|array $searchPhrase
* @return DataList<T>
*/
private function individualFieldSearch(DataList $query, array $searchableFields, string $searchField, $searchPhrase): DataList
{
@ -338,7 +344,7 @@ class SearchContext
* @param array $searchParams
* @param array|bool|string $sort
* @param array|null|string $limit
* @return DataList
* @return DataList<T>
* @throws Exception
*/
public function getResults($searchParams, $sort = false, $limit = null)
@ -365,7 +371,7 @@ class SearchContext
* Accessor for the filter attached to a named field.
*
* @param string $name
* @return SearchFilter
* @return SearchFilter|null
*/
public function getFilter($name)
{
@ -389,7 +395,7 @@ class SearchContext
/**
* Overwrite the current search context filter map.
*
* @param array $filters
* @param SearchFilter[] $filters
*/
public function setFilters($filters)
{
@ -486,7 +492,7 @@ class SearchContext
* for each field. Returns an ArrayList of ArrayData, suitable for
* rendering on a template.
*
* @return ArrayList
* @return ArrayList<ArrayData>
*/
public function getSummary()
{

View File

@ -11,6 +11,9 @@ namespace SilverStripe\ORM;
* @see SS_List
* @see Filterable
* @see Limitable
*
* @template T
* @implements SS_List<T>
*/
interface Sortable extends SS_List
{
@ -27,11 +30,12 @@ interface Sortable extends SS_List
* Return a new instance of this list that is sorted by one or more fields. You can either pass in a single
* field name and direction, or a map of field names to sort directions.
*
* @return static
* @example $list = $list->sort('Name'); // default ASC sorting
* @example $list = $list->sort('Name DESC'); // DESC sorting
* @example $list = $list->sort('Name', 'ASC');
* @example $list = $list->sort(array('Name'=>'ASC,'Age'=>'DESC'));
*
* @return static<T>
*/
public function sort();
@ -39,8 +43,9 @@ interface Sortable extends SS_List
/**
* Return a new instance of this list based on reversing the current sort.
*
* @return Sortable
* @example $list = $list->reverse();
*
* @return static<T>
*/
public function reverse();
}

View File

@ -17,6 +17,10 @@ use Traversable;
* It can store both saved objects (as IDs) or unsaved objects (as instances
* of $dataClass). Unsaved objects are then written when the list is saved
* into an instance of {@link RelationList}.
*
* @template T of DataObject
* @extends ArrayList<T>
* @implements Relation<T>
*/
class UnsavedRelationList extends ArrayList implements Relation
{
@ -38,7 +42,7 @@ class UnsavedRelationList extends ArrayList implements Relation
/**
* The DataObject class name that this relation is querying
*
* @var string
* @var class-string<T>
*/
protected $dataClass;
@ -54,7 +58,7 @@ class UnsavedRelationList extends ArrayList implements Relation
*
* @param string $baseClass
* @param string $relationName
* @param string $dataClass The DataObject class used in the relation
* @param class-string<T> $dataClass The DataObject class used in the relation
*/
public function __construct($baseClass, $relationName, $dataClass)
{
@ -112,7 +116,7 @@ class UnsavedRelationList extends ArrayList implements Relation
/**
* Get the dataClass name for this relation, ie the DataObject ClassName
*
* @return string
* @return class-string<T>
*/
public function dataClass()
{
@ -130,8 +134,6 @@ class UnsavedRelationList extends ArrayList implements Relation
/**
* Return an array of the actual items that this relation contains at this stage.
* This is when the query is actually executed.
*
* @return array
*/
public function toArray()
{
@ -209,8 +211,6 @@ class UnsavedRelationList extends ArrayList implements Relation
* Returns an array with both the keys and values set to the IDs of the records in this list.
* Does not respect sort order. Use ->column("ID") to get an ID list with the current sort.
* Does not return the IDs for unsaved DataObjects.
*
* @return array
*/
public function getIDList()
{
@ -230,11 +230,6 @@ class UnsavedRelationList extends ArrayList implements Relation
return $ids;
}
/**
* Returns the first item in the list
*
* @return mixed
*/
public function first()
{
$item = reset($this->items) ?: null;
@ -247,11 +242,6 @@ class UnsavedRelationList extends ArrayList implements Relation
return $item;
}
/**
* Returns the last item in the list
*
* @return mixed
*/
public function last()
{
$item = end($this->items) ?: null;
@ -291,7 +281,7 @@ class UnsavedRelationList extends ArrayList implements Relation
/**
* Returns a copy of this list with the relationship linked to the given foreign ID.
* @param int|array $id An ID or an array of IDs.
* @return Relation
* @return Relation<T>
*/
public function forForeignID($id)
{

View File

@ -96,7 +96,6 @@ class BasicAuth
try {
if ($request->getHeader('PHP_AUTH_USER') && $request->getHeader('PHP_AUTH_PW')) {
/** @var MemberAuthenticator $authenticator */
$authenticators = Security::singleton()->getApplicableAuthenticators(Authenticator::LOGIN);
foreach ($authenticators as $name => $authenticator) {

View File

@ -152,7 +152,6 @@ class DefaultAdminService
$this->extend('beforeFindOrCreateAdmin', $email, $name);
// Find member
/** @var Member $admin */
$admin = Member::get()
->filter('Email', $email)
->first();

View File

@ -97,7 +97,6 @@ class Group extends DataObject
$doSet = new ArrayList();
$children = Group::get()->filter("ParentID", $this->ID);
/** @var Group $child */
foreach ($children as $child) {
$doSet->push($child);
$doSet->merge($child->getAllChildren());
@ -165,12 +164,10 @@ class Group extends DataObject
$config->removeComponentsByType(GridFieldDeleteAction::class);
$config->addComponent(GridFieldGroupDeleteAction::create($this->ID), GridFieldPageCount::class);
/** @var GridFieldAddExistingAutocompleter $autocompleter */
$autocompleter = $config->getComponentByType(GridFieldAddExistingAutocompleter::class);
$autocompleter
->setResultsFormat('$Title ($Email)')
->setSearchFields(['FirstName', 'Surname', 'Email']);
/** @var GridFieldDetailForm $detailForm */
$detailForm = $config->getComponentByType(GridFieldDetailForm::class);
$detailForm
->setItemEditFormCallback(function ($form) use ($group) {
@ -307,7 +304,7 @@ class Group extends DataObject
* See {@link DirectMembers()} for retrieving members without any inheritance.
*
* @param string $filter
* @return ManyManyList
* @return ManyManyList<Member>
*/
public function Members($filter = '')
{
@ -338,6 +335,7 @@ class Group extends DataObject
/**
* Return only the members directly added to this group
* @return ManyManyList<Member>
*/
public function DirectMembers()
{
@ -650,7 +648,7 @@ class Group extends DataObject
* Returns all of the children for the CMS Tree.
* Filters to only those groups that the current user can edit
*
* @return ArrayList
* @return ArrayList<DataObject>
*/
public function AllChildrenIncludingDeleted()
{

View File

@ -8,6 +8,9 @@ use SilverStripe\ORM\DataExtension;
use SilverStripe\ORM\DataObject;
use SilverStripe\Core\Cache\MemberCacheFlusher;
/**
* @extends DataExtension<Member|Group>
*/
class InheritedPermissionFlusher extends DataExtension implements Flushable
{
/**

View File

@ -417,7 +417,6 @@ class InheritedPermissions implements PermissionChecker, MemberCacheFlusher
->execute();
foreach ($potentiallyInherited as $item) {
/** @var DataObject|Hierarchy $item */
if ($item['ParentID']) {
if (!isset($groupedByParent[$item['ParentID']])) {
$groupedByParent[$item['ParentID']] = [];

View File

@ -14,6 +14,8 @@ use SilverStripe\ORM\ManyManyList;
* @method ManyManyList<Member> EditorMembers()
* @method ManyManyList<Group> ViewerGroups()
* @method ManyManyList<Member> ViewerMembers()
*
* @extends DataExtension<DataObject>
*/
class InheritedPermissionsExtension extends DataExtension
{

View File

@ -81,7 +81,7 @@ class LoginAttempt extends DataObject
* Get all login attempts for the given email address
*
* @param string $email
* @return DataList|LoginAttempt[]
* @return DataList<LoginAttempt>
*/
public static function getByEmail($email)
{

View File

@ -34,7 +34,6 @@ abstract class LoginForm extends Form
{
$this->authenticatorClass = $class;
/** @var FieldList|null $fields */
$fields = $this->Fields();
if (!$fields) {
return $this;

View File

@ -584,7 +584,6 @@ class Member extends DataObject
*/
public static function member_from_autologinhash($hash, $login = false)
{
/** @var Member $member */
$member = static::get()->filter([
'AutoLoginHash' => $hash,
'AutoLoginExpired:GreaterThan' => DBDatetime::now()->getValue(),
@ -610,7 +609,6 @@ class Member extends DataObject
// Exclude expired
if (static::config()->get('temp_id_lifetime')) {
/** @var DataList|Member[] $members */
$members = $members->filter('TempIDExpired:GreaterThan', DBDatetime::now()->getValue());
}
@ -657,7 +655,6 @@ class Member extends DataObject
$label = $editingPassword
? _t(__CLASS__ . '.EDIT_PASSWORD', 'New Password')
: $this->fieldLabel('Password');
/** @var ConfirmedPasswordField $password */
$password = ConfirmedPasswordField::create(
'Password',
$label,
@ -1176,7 +1173,7 @@ class Member extends DataObject
}
/**
* @return ManyManyList|UnsavedRelationList
* @return ManyManyList<Group>|UnsavedRelationList<Group>
*/
public function DirectGroups()
{
@ -1212,7 +1209,6 @@ class Member extends DataObject
$membersList = new ArrayList();
// This is a bit ineffective, but follow the ORM style
/** @var Group $group */
foreach (Group::get()->byIDs($groupIDList) as $group) {
$membersList->merge($group->Members());
}
@ -1332,7 +1328,6 @@ class Member extends DataObject
$rootTabSet = $fields->fieldByName("Root");
/** @var Tab $mainTab */
$mainTab = $rootTabSet->fieldByName("Main");
/** @var FieldList $mainFields */
$mainFields = $mainTab->getChildren();
// Build change password field

View File

@ -66,7 +66,6 @@ class ChangePasswordHandler extends RequestHandler
$request = $this->getRequest();
// Extract the member from the URL.
/** @var Member $member */
$member = null;
if ($request->getVar('m') !== null) {
$member = Member::get()->filter(['ID' => (int)$request->getVar('m')])->first();
@ -153,7 +152,6 @@ class ChangePasswordHandler extends RequestHandler
{
// if there is a current member, they should be logged out
if ($curMember = Security::getCurrentUser()) {
/** @var LogoutHandler $handler */
Injector::inst()->get(IdentityStore::class)->logOut();
}
@ -275,7 +273,6 @@ class ChangePasswordHandler extends RequestHandler
$member->write();
if ($member->canLogin()) {
/** @var IdentityStore $identityStore */
$identityStore = Injector::inst()->get(IdentityStore::class);
$identityStore->logIn($member, false, $this->getRequest());
}

View File

@ -145,7 +145,6 @@ class CookieAuthenticationHandler implements AuthenticationHandler
}
// check if autologin token matches
/** @var Member $member */
$member = Member::get()->byID($uid);
if (!$member) {
return null;
@ -153,7 +152,6 @@ class CookieAuthenticationHandler implements AuthenticationHandler
$hash = $member->encryptWithUserSettings($token);
/** @var RememberLoginHash $rememberLoginHash */
$rememberLoginHash = RememberLoginHash::get()
->filter([
'MemberID' => $member->ID,

View File

@ -115,7 +115,6 @@ class LoginHandler extends RequestHandler
$this->extend('beforeLogin');
// Successful login
/** @var ValidationResult $result */
if ($member = $this->checkLogin($data, $request, $result)) {
$this->performLogin($member, $data, $request);
// Allow operations on the member after successful login
@ -241,7 +240,6 @@ class LoginHandler extends RequestHandler
{
/** IdentityStore */
$rememberMe = (isset($data['Remember']) && Security::config()->get('autologin_enabled'));
/** @var IdentityStore $identityStore */
$identityStore = Injector::inst()->get(IdentityStore::class);
$identityStore->logIn($member, $rememberMe, $request);

View File

@ -165,7 +165,6 @@ class LostPasswordHandler extends RequestHandler
return $dataValidation;
}
/** @var Member $member */
$member = $this->getMemberFromData($data);
// Allow vetoing forgot password requests
@ -241,7 +240,6 @@ class LostPasswordHandler extends RequestHandler
protected function sendEmail($member, $token)
{
try {
/** @var Email $email */
$email = Email::create()
->setHTMLTemplate('SilverStripe\\Control\\Email\\ForgotPasswordEmail')
->setData($member)

View File

@ -90,7 +90,6 @@ class MemberAuthenticator implements Authenticator
if (!$member && $email) {
// Find user by email
$identifierField = Member::config()->get('unique_identifier_field');
/** @var Member $member */
$member = Member::get()
->filter([$identifierField => $email])
->first();

View File

@ -62,7 +62,6 @@ class SessionAuthenticationHandler implements AuthenticationHandler
if (!$id) {
return null;
}
/** @var Member $member */
$member = DataObject::get_by_id(Member::class, $id);
return $member;
}
@ -86,7 +85,6 @@ class SessionAuthenticationHandler implements AuthenticationHandler
// Activate sudo mode on login so the user doesn't have to reauthenticate for sudo
// actions until the sudo mode timeout expires
/** @var SudoModeServiceInterface $service */
$service = Injector::inst()->get(SudoModeServiceInterface::class);
$service->activate($session);
}

View File

@ -11,6 +11,8 @@ use SilverStripe\ORM\Queries\SQLSelect;
/**
* Represents a set of Groups attached to a member.
* Handles the hierarchy logic.
*
* @extends ManyManyList<Group>
*/
class Member_GroupSet extends ManyManyList
{

View File

@ -227,7 +227,6 @@ class PasswordValidator
->where(['"MemberPassword"."MemberID"' => $member->ID])
->sort('"Created" DESC, "ID" DESC')
->limit($historicCount);
/** @var MemberPassword $previousPassword */
foreach ($previousPasswords as $previousPassword) {
if ($previousPassword->checkPassword($password)) {
$error = _t(

View File

@ -127,7 +127,6 @@ class PermissionCheckboxSetField extends FormField
// Get all permissions from roles
if ($record->Roles()->count()) {
foreach ($record->Roles() as $role) {
/** @var PermissionRole $role */
foreach ($role->Codes() as $code) {
if (!isset($inheritedCodes[$code->Code])) {
$inheritedCodes[$code->Code] = [];

View File

@ -169,7 +169,6 @@ class RememberLoginHash extends DataObject
if (static::config()->force_single_token) {
RememberLoginHash::get()->filter('MemberID', $member->ID)->removeAll();
}
/** @var RememberLoginHash $rememberLoginHash */
$rememberLoginHash = RememberLoginHash::create();
do {
$deviceID = $rememberLoginHash->getNewDeviceID();

View File

@ -38,7 +38,6 @@ class RequestAuthenticationHandler implements AuthenticationHandler
public function authenticateRequest(HTTPRequest $request)
{
/** @var AuthenticationHandler $handler */
foreach ($this->getHandlers() as $name => $handler) {
// in order to add cookies, etc
$member = $handler->authenticateRequest($request);

View File

@ -253,7 +253,6 @@ class Security extends Controller implements TemplateGlobalProvider
{
$authenticators = $this->getAuthenticators();
/** @var Authenticator $authenticator */
foreach ($authenticators as $name => $authenticator) {
if (!($authenticator->supportedServices() & $service)) {
unset($authenticators[$name]);
@ -937,7 +936,6 @@ class Security extends Controller implements TemplateGlobalProvider
{
$handlers = [];
$authenticators = $this->getApplicableAuthenticators(Authenticator::RESET_PASSWORD);
/** @var Authenticator $authenticator */
foreach ($authenticators as $authenticator) {
$handlers[] = $authenticator->getLostPasswordHandler(
Controller::join_links($this->Link(), 'lostpassword')
@ -966,7 +964,6 @@ class Security extends Controller implements TemplateGlobalProvider
*/
public function changepassword()
{
/** @var array|Authenticator[] $authenticators */
$authenticators = $this->getApplicableAuthenticators(Authenticator::CHANGE_PASSWORD);
$handlers = [];
foreach ($authenticators as $authenticator) {

View File

@ -47,8 +47,9 @@ class GenericTemplateGlobalProvider implements TemplateGlobalProvider
* <% end_cached %>
* </code>
*
* @param string $className
* @return DataList
* @template T of DataObject
* @param class-string<T> $className
* @return DataList<T>
*/
public static function getDataList($className)
{

View File

@ -619,7 +619,6 @@ class ShortcodeParser
$content = $this->getShortcodeReplacementText($tag);
if ($content) {
/** @var HTMLValue $parsed */
$parsed = HTMLValue::create($content);
$body = $parsed->getBody();
if ($body) {
@ -658,7 +657,6 @@ class ShortcodeParser
// use a proper DOM
list($content, $tags) = $this->replaceElementTagsWithMarkers($content);
/** @var HTMLValue $htmlvalue */
$htmlvalue = Injector::inst()->create(HTMLValue::class, $content);
// Now parse the result into a DOM

View File

@ -242,7 +242,7 @@ class EmbedShortcodeProvider implements ShortcodeHandler
*
* @param array $arguments List of embed arguments
* @param array $exclude List of attribute names to exclude from the resulting list
* @return ArrayList
* @return ArrayList<ArrayData>
*/
private static function buildAttributeListFromArguments(array $arguments, array $exclude = []): ArrayList
{

View File

@ -18,6 +18,7 @@ use SilverStripe\Core\Extensible;
use SilverStripe\Core\Injector\Injectable;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\Debug;
use SilverStripe\Dev\Deprecation;
use SilverStripe\ORM\ArrayLib;
use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\ORM\FieldType\DBHTMLText;
@ -644,10 +645,13 @@ class ViewableData implements IteratorAggregate
* This is useful so you can use a single record inside a <% control %> block in a template - and then use
* to access individual fields on this object.
*
* @deprecated 5.2.0 Will be removed without equivalent functionality
*
* @return ArrayIterator
*/
public function getIterator(): Traversable
{
Deprecation::notice('5.2.0', 'Will be removed without equivalent functionality');
return new ArrayIterator([$this]);
}

View File

@ -15,8 +15,9 @@ use SilverStripe\Core\Manifest\ModuleManifest;
* way to access instance methods which don't rely on instance
* data (e.g. the custom SilverStripe static handling).
*
* @param string $className
* @return mixed
* @template T of object
* @param class-string<T> $className
* @return T|mixed
*/
function singleton($className)
{

View File

@ -18,7 +18,7 @@ class SSListContainsOnlyMatchingItemsTest extends SapphireTest
}
/**
* @return ArrayList|Member[]
* @return ArrayList<Member>
*/
private function getListToMatch()
{

View File

@ -62,7 +62,7 @@ class SSListContainsOnlyTest extends SapphireTest
}
/**
* @return ArrayList|Member[]
* @return ArrayList<Member>
*/
private function getListToMatch()
{

View File

@ -77,7 +77,7 @@ class SSListContainsTest extends SapphireTest
}
/**
* @return ArrayList|Member[]
* @return ArrayList<Member>
*/
private function getListToMatch()
{