From 0268b65754efa0e252b2ef8d22c9dd8a819bb168 Mon Sep 17 00:00:00 2001 From: Guy Sartorelli <guy.sartorelli@silverstripe.com> Date: Thu, 22 Aug 2024 14:24:43 +1200 Subject: [PATCH] API Strong typing for the view layer --- src/Control/RSS/RSSFeed_Entry.php | 7 - src/Dev/TaskRunner.php | 2 +- src/Forms/CompositeField.php | 2 +- src/Forms/Form.php | 8 +- src/Forms/FormField.php | 8 +- src/Forms/FormRequestHandler.php | 2 +- src/Forms/NullableField.php | 2 +- src/Forms/ReadonlyField.php | 2 +- src/Forms/RequiredFields.php | 2 +- src/ORM/ArrayList.php | 6 +- src/ORM/DataList.php | 6 +- src/ORM/DataObject.php | 77 +++---- src/ORM/DataObjectInterface.php | 2 +- src/ORM/EagerLoadedList.php | 2 +- src/ORM/FieldType/DBBigInt.php | 2 +- src/ORM/FieldType/DBBoolean.php | 22 +- src/ORM/FieldType/DBClassName.php | 45 ++-- src/ORM/FieldType/DBComposite.php | 97 +++----- src/ORM/FieldType/DBCurrency.php | 32 +-- src/ORM/FieldType/DBDate.php | 103 +++------ src/ORM/FieldType/DBDatetime.php | 104 ++++----- src/ORM/FieldType/DBDecimal.php | 57 ++--- src/ORM/FieldType/DBDouble.php | 2 +- src/ORM/FieldType/DBEnum.php | 93 +++----- src/ORM/FieldType/DBField.php | 211 +++++------------- src/ORM/FieldType/DBFloat.php | 18 +- src/ORM/FieldType/DBForeignKey.php | 32 +-- src/ORM/FieldType/DBHTMLText.php | 78 +++---- src/ORM/FieldType/DBHTMLVarchar.php | 50 ++--- src/ORM/FieldType/DBInt.php | 21 +- src/ORM/FieldType/DBLocale.php | 23 +- src/ORM/FieldType/DBMoney.php | 74 ++---- src/ORM/FieldType/DBMultiEnum.php | 24 +- src/ORM/FieldType/DBPercentage.php | 18 +- src/ORM/FieldType/DBPolymorphicForeignKey.php | 28 +-- src/ORM/FieldType/DBPrimaryKey.php | 41 ++-- src/ORM/FieldType/DBString.php | 53 ++--- src/ORM/FieldType/DBText.php | 45 ++-- src/ORM/FieldType/DBTime.php | 50 ++--- src/ORM/FieldType/DBVarchar.php | 33 +-- src/ORM/FieldType/DBYear.php | 13 +- src/ORM/ListDecorator.php | 6 +- src/View/ArrayData.php | 19 +- src/View/Parsers/HTMLValue.php | 7 +- src/View/Parsers/ShortcodeParser.php | 2 +- src/View/SSTemplateParser.peg | 2 +- src/View/SSTemplateParser.php | 9 +- src/View/ViewableData.php | 195 ++++------------ src/View/ViewableData_Customised.php | 29 ++- src/View/ViewableData_Debugger.php | 18 +- tests/php/Core/ClassInfoTest.php | 2 +- .../ViewableDataContainsTest/TestObject.php | 8 +- tests/php/ORM/DBFieldTest/TestDataObject.php | 4 +- tests/php/ORM/DBFieldTest/TestDbField.php | 2 +- tests/php/ORM/DBStringTest/MyStringField.php | 2 +- .../MockDynamicAssignmentDBField.php | 6 +- tests/php/View/ArrayDataTest.php | 2 +- tests/php/View/SSViewerTest/TestFixture.php | 14 +- tests/php/View/ViewableDataTest/Castable.php | 2 +- tests/php/View/ViewableDataTest/Caster.php | 2 +- .../View/ViewableDataTest/RequiresCasting.php | 2 +- .../View/ViewableDataTest/UnescapedCaster.php | 2 +- 62 files changed, 625 insertions(+), 1207 deletions(-) diff --git a/src/Control/RSS/RSSFeed_Entry.php b/src/Control/RSS/RSSFeed_Entry.php index 5713b84026b..ff6da977f43 100644 --- a/src/Control/RSS/RSSFeed_Entry.php +++ b/src/Control/RSS/RSSFeed_Entry.php @@ -17,13 +17,6 @@ */ class RSSFeed_Entry extends ViewableData { - /** - * The object that represents the item, it contains all the data. - * - * @var mixed - */ - protected $failover; - /** * Name of the title field of feed entries * diff --git a/src/Dev/TaskRunner.php b/src/Dev/TaskRunner.php index 216d6b31327..ecd87c1a26b 100644 --- a/src/Dev/TaskRunner.php +++ b/src/Dev/TaskRunner.php @@ -236,7 +236,7 @@ public function canInit(): bool } return count($this->getTaskList()) > 0; } - + public function providePermissions(): array { return [ diff --git a/src/Forms/CompositeField.php b/src/Forms/CompositeField.php index 15d9abb7a22..7c1cab23f10 100644 --- a/src/Forms/CompositeField.php +++ b/src/Forms/CompositeField.php @@ -514,7 +514,7 @@ public function makeFieldReadonly($field) return false; } - public function debug() + public function debug(): string { $class = static::class; $result = "$class ($this->name) <ul>"; diff --git a/src/Forms/Form.php b/src/Forms/Form.php index 22338c02ce9..7719193bdaf 100644 --- a/src/Forms/Form.php +++ b/src/Forms/Form.php @@ -521,7 +521,7 @@ public function setFieldMessage( return $this; } - public function castingHelper($field, bool $useFallback = true) + public function castingHelper(string $field, bool $useFallback = true): ?string { // Override casting for field message if (strcasecmp($field ?? '', 'Message') === 0 && ($helper = $this->getMessageCastingHelper())) { @@ -1547,10 +1547,8 @@ public function getData() * * This is returned when you access a form as $FormObject rather * than <% with FormObject %> - * - * @return DBHTMLText */ - public function forTemplate() + public function forTemplate(): string { if (!$this->canBeCached()) { HTTPCacheControlMiddleware::singleton()->disableCache(); @@ -1750,7 +1748,7 @@ public function removeExtraClass($class) return $this; } - public function debug() + public function debug(): string { $class = static::class; $result = "<h3>$class</h3><ul>"; diff --git a/src/Forms/FormField.php b/src/Forms/FormField.php index 3c47b5ff92c..fe31dc6add2 100644 --- a/src/Forms/FormField.php +++ b/src/Forms/FormField.php @@ -790,7 +790,7 @@ public function securityTokenEnabled() return $form->getSecurityToken()->isEnabled(); } - public function castingHelper($field, bool $useFallback = true) + public function castingHelper(string $field, bool $useFallback = true): ?string { // Override casting for field message if (strcasecmp($field ?? '', 'Message') === 0 && ($helper = $this->getMessageCastingHelper())) { @@ -1269,7 +1269,7 @@ public function getDescription() /** * @return string */ - public function debug() + public function debug(): string { $strValue = is_string($this->value) ? $this->value : print_r($this->value, true); @@ -1286,10 +1286,8 @@ public function debug() /** * This function is used by the template processor. If you refer to a field as a $ variable, it * will return the $Field value. - * - * @return string */ - public function forTemplate() + public function forTemplate(): string { return $this->Field(); } diff --git a/src/Forms/FormRequestHandler.php b/src/Forms/FormRequestHandler.php index 1071f92aba8..a646944a090 100644 --- a/src/Forms/FormRequestHandler.php +++ b/src/Forms/FormRequestHandler.php @@ -504,7 +504,7 @@ public function validationResult() return $result; } - public function forTemplate() + public function forTemplate(): string { return $this->form->forTemplate(); } diff --git a/src/Forms/NullableField.php b/src/Forms/NullableField.php index e2c35101a19..99669cd0ab8 100644 --- a/src/Forms/NullableField.php +++ b/src/Forms/NullableField.php @@ -166,7 +166,7 @@ public function setName($name) /** * @return string */ - public function debug() + public function debug(): string { $result = sprintf( '%s (%s: %s : <span style="color: red">%s</span>) = ', diff --git a/src/Forms/ReadonlyField.php b/src/Forms/ReadonlyField.php index ef31f3d7b55..68a496a5d61 100644 --- a/src/Forms/ReadonlyField.php +++ b/src/Forms/ReadonlyField.php @@ -56,7 +56,7 @@ public function Type() return 'readonly'; } - public function castingHelper($field, bool $useFallback = true) + public function castingHelper(string $field, bool $useFallback = true): ?string { // Get dynamic cast for 'Value' field if (strcasecmp($field ?? '', 'Value') === 0) { diff --git a/src/Forms/RequiredFields.php b/src/Forms/RequiredFields.php index 3f0e055c0b7..9412e6cfbbe 100644 --- a/src/Forms/RequiredFields.php +++ b/src/Forms/RequiredFields.php @@ -58,7 +58,7 @@ public function removeValidation() * Debug helper * @return string */ - public function debug() + public function debug(): string { if (!is_array($this->required)) { return false; diff --git a/src/ORM/ArrayList.php b/src/ORM/ArrayList.php index ed20230086a..53527089963 100644 --- a/src/ORM/ArrayList.php +++ b/src/ORM/ArrayList.php @@ -110,10 +110,8 @@ public function count(): int /** * Returns true if this list has items - * - * @return bool */ - public function exists() + public function exists(): bool { return !empty($this->items); } @@ -159,7 +157,7 @@ public function each($callback) return $this; } - public function debug() + public function debug(): string { $val = "<h2>" . static::class . "</h2><ul>"; foreach ($this->toNestedArray() as $item) { diff --git a/src/ORM/DataList.php b/src/ORM/DataList.php index 72ccacd7113..23c89029de4 100644 --- a/src/ORM/DataList.php +++ b/src/ORM/DataList.php @@ -868,7 +868,7 @@ public function each($callback) return $this; } - public function debug() + public function debug(): string { $val = "<h2>" . static::class . "</h2><ul>"; foreach ($this->toNestedArray() as $item) { @@ -1702,10 +1702,8 @@ public function last() /** * Returns true if this DataList has items - * - * @return bool */ - public function exists() + public function exists(): bool { return $this->dataQuery->exists(); } diff --git a/src/ORM/DataObject.php b/src/ORM/DataObject.php index 3852d640c0c..f6908686a09 100644 --- a/src/ORM/DataObject.php +++ b/src/ORM/DataObject.php @@ -816,10 +816,8 @@ public function defineMethods() * Returns true if this object "exists", i.e., has a sensible value. * The default behaviour for a DataObject is to return true if * the object exists in the database, you can override this in subclasses. - * - * @return boolean true if this object exists */ - public function exists() + public function exists(): bool { return $this->isInDB(); } @@ -2687,7 +2685,7 @@ public function getFrontEndFields($params = null) return $untabbedFields; } - public function getViewerTemplates($suffix = '') + public function getViewerTemplates(string $suffix = ''): array { return SSViewer::get_templates_by_class(static::class, $suffix, $this->baseClass()); } @@ -2695,11 +2693,8 @@ public function getViewerTemplates($suffix = '') /** * Gets the value of a field. * Called by {@link __get()} and any getFieldName() methods you might create. - * - * @param string $field The name of the field - * @return mixed The field value */ - public function getField($field) + public function getField(string $field): mixed { // If we already have a value in $this->record, then we should just return that if (isset($this->record[$field])) { @@ -2910,12 +2905,8 @@ public function isChanged($fieldName = null, $changeLevel = DataObject::CHANGE_S /** * Set the value of the field * Called by {@link __set()} and any setFieldName() methods you might create. - * - * @param string $fieldName Name of the field - * @param mixed $val New field value - * @return $this */ - public function setField($fieldName, $val) + public function setField(string $fieldName, mixed $value): static { $this->objCacheClear(); //if it's a has_one component, destroy the cache @@ -2934,42 +2925,42 @@ public function setField($fieldName, $val) if ($schema->unaryComponent(static::class, $fieldName)) { unset($this->components[$fieldName]); // Assign component directly - if (is_null($val) || $val instanceof DataObject) { - return $this->setComponent($fieldName, $val); + if (is_null($value) || $value instanceof DataObject) { + return $this->setComponent($fieldName, $value); } // Assign by ID instead of object - if (is_numeric($val)) { + if (is_numeric($value)) { $fieldName .= 'ID'; } } // Situation 1: Passing an DBField - if ($val instanceof DBField) { - $val->setName($fieldName); - $val->saveInto($this); + if ($value instanceof DBField) { + $value->setName($fieldName); + $value->saveInto($this); // Situation 1a: Composite fields should remain bound in case they are // later referenced to update the parent dataobject - if ($val instanceof DBComposite) { - $val->bindTo($this); - $this->setFieldValue($fieldName, $val); + if ($value instanceof DBComposite) { + $value->bindTo($this); + $this->setFieldValue($fieldName, $value); } // Situation 2: Passing a literal or non-DBField object } else { - $this->setFieldValue($fieldName, $val); + $this->setFieldValue($fieldName, $value); } return $this; } - private function setFieldValue(string $fieldName, mixed $val): void + private function setFieldValue(string $fieldName, mixed $value): void { $schema = static::getSchema(); // If this is a proper database field, we shouldn't be getting non-DBField objects - if (is_object($val) && !($val instanceof DBField) && $schema->fieldSpec(static::class, $fieldName)) { + if (is_object($value) && !($value instanceof DBField) && $schema->fieldSpec(static::class, $fieldName)) { throw new InvalidArgumentException('DataObject::setFieldValue: passed an object that is not a DBField'); } - if (!empty($val) && !is_scalar($val)) { + if (!empty($value) && !is_scalar($value)) { $dbField = $this->dbObject($fieldName); if ($dbField && $dbField->scalarValueOnly()) { throw new InvalidArgumentException( @@ -2982,12 +2973,12 @@ private function setFieldValue(string $fieldName, mixed $val): void } // if a field is not existing or has strictly changed - if (!array_key_exists($fieldName, $this->original ?? []) || $this->original[$fieldName] !== $val) { + if (!array_key_exists($fieldName, $this->original ?? []) || $this->original[$fieldName] !== $value) { // At the very least, the type has changed $this->changed[$fieldName] = DataObject::CHANGE_STRICT; - if ((!array_key_exists($fieldName, $this->original ?? []) && $val) - || (array_key_exists($fieldName, $this->original ?? []) && $this->original[$fieldName] != $val) + if ((!array_key_exists($fieldName, $this->original ?? []) && $value) + || (array_key_exists($fieldName, $this->original ?? []) && $this->original[$fieldName] != $value) ) { // Value has changed as well, not just the type $this->changed[$fieldName] = DataObject::CHANGE_VALUE; @@ -2998,7 +2989,7 @@ private function setFieldValue(string $fieldName, mixed $val): void } // Value is saved regardless, since the change detection relates to the last write - $this->record[$fieldName] = $val; + $this->record[$fieldName] = $value; } /** @@ -3029,7 +3020,7 @@ public function setCastedField($fieldName, $value) /** * {@inheritdoc} */ - public function castingHelper($field, bool $useFallback = true) + public function castingHelper(string $field, bool $useFallback = true): ?string { $fieldSpec = static::getSchema()->fieldSpec(static::class, $field); if ($fieldSpec) { @@ -3054,19 +3045,16 @@ public function castingHelper($field, bool $useFallback = true) * Returns true if the given field exists in a database column on any of * the objects tables and optionally look up a dynamic getter with * get<fieldName>(). - * - * @param string $field Name of the field - * @return boolean True if the given field exists */ - public function hasField($field) + public function hasField(string $fieldName): bool { $schema = static::getSchema(); return ( - array_key_exists($field, $this->record ?? []) - || array_key_exists($field, $this->components ?? []) - || $schema->fieldSpec(static::class, $field) - || $schema->unaryComponent(static::class, $field) - || $this->hasMethod("get{$field}") + array_key_exists($fieldName, $this->record ?? []) + || array_key_exists($fieldName, $this->components ?? []) + || $schema->fieldSpec(static::class, $fieldName) + || $schema->unaryComponent(static::class, $fieldName) + || $this->hasMethod("get{$fieldName}") ); } @@ -3214,7 +3202,7 @@ public function canCreate($member = null, $context = []) * * @return string HTML data representing this object */ - public function debug() + public function debug(): string { $class = static::class; $val = "<h3>Database record: {$class}</h3>\n<ul>\n"; @@ -4386,13 +4374,8 @@ public function provideI18nEntities() /** * Returns true if the given method/parameter has a value * (Uses the DBField::hasValue if the parameter is a database field) - * - * @param string $field The field name - * @param array $arguments - * @param bool $cache - * @return boolean */ - public function hasValue($field, $arguments = null, $cache = true) + public function hasValue(string $field, array $arguments = [], bool $cache = true): bool { // has_one fields should not use dbObject to check if a value is given $hasOne = static::getSchema()->hasOneComponent(static::class, $field); diff --git a/src/ORM/DataObjectInterface.php b/src/ORM/DataObjectInterface.php index b4f43d5dfb9..eba6f022d5e 100644 --- a/src/ORM/DataObjectInterface.php +++ b/src/ORM/DataObjectInterface.php @@ -36,7 +36,7 @@ public function delete(); * @param string $fieldName * @return mixed */ - public function __get($fieldName); + public function __get(string $property): mixed; /** * Save content from a form into a field on this data object. diff --git a/src/ORM/EagerLoadedList.php b/src/ORM/EagerLoadedList.php index 8a53c88905d..e006698007c 100644 --- a/src/ORM/EagerLoadedList.php +++ b/src/ORM/EagerLoadedList.php @@ -341,7 +341,7 @@ public function each($callback): static return $this; } - public function debug() + public function debug(): string { // Same implementation as DataList::debug() $val = '<h2>' . static::class . '</h2><ul>'; diff --git a/src/ORM/FieldType/DBBigInt.php b/src/ORM/FieldType/DBBigInt.php index 8d427625cd4..c92c2da69df 100644 --- a/src/ORM/FieldType/DBBigInt.php +++ b/src/ORM/FieldType/DBBigInt.php @@ -15,7 +15,7 @@ class DBBigInt extends DBInt { - public function requireField() + public function requireField(): void { $parts = [ 'datatype' => 'bigint', diff --git a/src/ORM/FieldType/DBBoolean.php b/src/ORM/FieldType/DBBoolean.php index 46374682cc1..c97eccd3d6b 100644 --- a/src/ORM/FieldType/DBBoolean.php +++ b/src/ORM/FieldType/DBBoolean.php @@ -4,21 +4,23 @@ use SilverStripe\Forms\CheckboxField; use SilverStripe\Forms\DropdownField; +use SilverStripe\Forms\FormField; use SilverStripe\ORM\DB; +use SilverStripe\View\ViewableData; /** * Represents a boolean field. */ class DBBoolean extends DBField { - public function __construct($name = null, $defaultVal = 0) + public function __construct(?string $name = null, bool|int $defaultVal = 0) { $this->defaultVal = ($defaultVal) ? 1 : 0; parent::__construct($name); } - public function requireField() + public function requireField(): void { $parts = [ 'datatype' => 'tinyint', @@ -32,17 +34,17 @@ public function requireField() DB::require_field($this->tableName, $this->name, $values); } - public function Nice() + public function Nice(): string { return ($this->value) ? _t(__CLASS__ . '.YESANSWER', 'Yes') : _t(__CLASS__ . '.NOANSWER', 'No'); } - public function NiceAsBoolean() + public function NiceAsBoolean(): string { return ($this->value) ? 'true' : 'false'; } - public function saveInto($dataObject) + public function saveInto(ViewableData $dataObject): void { $fieldName = $this->name; if ($fieldName) { @@ -57,12 +59,12 @@ public function saveInto($dataObject) } } - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { return CheckboxField::create($this->name, $title); } - public function scaffoldSearchField($title = null) + public function scaffoldSearchField(?string $title = null): ?FormField { $anyText = _t(__CLASS__ . '.ANY', 'Any'); $source = [ @@ -71,16 +73,16 @@ public function scaffoldSearchField($title = null) 0 => _t(__CLASS__ . '.NOANSWER', 'No') ]; - return (new DropdownField($this->name, $title, $source)) + return DropdownField::create($this->name, $title, $source) ->setEmptyString($anyText); } - public function nullValue() + public function nullValue(): ?int { return 0; } - public function prepValueForDB($value) + public function prepValueForDB(mixed $value): ?int { if (is_bool($value)) { return $value ? 1 : 0; diff --git a/src/ORM/FieldType/DBClassName.php b/src/ORM/FieldType/DBClassName.php index 4848b05734e..d8008626bca 100644 --- a/src/ORM/FieldType/DBClassName.php +++ b/src/ORM/FieldType/DBClassName.php @@ -6,48 +6,40 @@ use SilverStripe\Core\Config\Config; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DB; +use SilverStripe\View\ViewableData; /** * Represents a classname selector, which respects obsolete clasess. */ class DBClassName extends DBEnum { - /** * Base classname of class to enumerate. * If 'DataObject' then all classes are included. * If empty, then the baseClass of the parent object will be used - * - * @var string|null */ - protected $baseClass = null; + protected ?string $baseClass = null; /** * Parent object - * - * @var DataObject|null */ - protected $record = null; + protected ?DataObject $record = null; - private static $index = true; + private static string|bool $index = true; /** * Create a new DBClassName field * - * @param string $name Name of field * @param string|null $baseClass Optional base class to limit selections - * @param array $options Optional parameters for this DBField instance + * @param array $options Optional parameters for this DBField instance */ - public function __construct($name = null, $baseClass = null, $options = []) + public function __construct(?string $name = null, ?string $baseClass = null, array $options = []) { $this->setBaseClass($baseClass); parent::__construct($name, null, null, $options); } - /** - * @return void - */ - public function requireField() + public function requireField(): void { $parts = [ 'datatype' => 'enum', @@ -69,10 +61,8 @@ public function requireField() /** * Get the base dataclass for the list of subclasses - * - * @return string */ - public function getBaseClass() + public function getBaseClass(): string { // Use explicit base class if ($this->baseClass) { @@ -95,25 +85,20 @@ public function getBaseClass() /** * Get the base name of the current class * Useful as a non-fully qualified CSS Class name in templates. - * - * @return string|null */ - public function getShortName() + public function getShortName(): string { $value = $this->getValue(); if (empty($value) || !ClassInfo::exists($value)) { - return null; + return ''; } return ClassInfo::shortName($value); } /** * Assign the base class - * - * @param string $baseClass - * @return $this */ - public function setBaseClass($baseClass) + public function setBaseClass(?string $baseClass): static { $this->baseClass = $baseClass; return $this; @@ -121,10 +106,8 @@ public function setBaseClass($baseClass) /** * Get list of classnames that should be selectable - * - * @return array */ - public function getEnum() + public function getEnum(): array { $classNames = ClassInfo::subclassesFor($this->getBaseClass()); $dataobject = strtolower(DataObject::class); @@ -132,7 +115,7 @@ public function getEnum() return array_values($classNames ?? []); } - public function setValue($value, $record = null, $markChanged = true) + public function setValue(mixed $value, null|array|ViewableData $record = null, bool $markChanged = true): static { parent::setValue($value, $record, $markChanged); @@ -143,7 +126,7 @@ public function setValue($value, $record = null, $markChanged = true) return $this; } - public function getDefault() + public function getDefault(): string { // Check for assigned default $default = parent::getDefault(); diff --git a/src/ORM/FieldType/DBComposite.php b/src/ORM/FieldType/DBComposite.php index 1fa7001db30..568eb5bf67a 100644 --- a/src/ORM/FieldType/DBComposite.php +++ b/src/ORM/FieldType/DBComposite.php @@ -7,6 +7,7 @@ use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DB; use SilverStripe\ORM\Queries\SQLSelect; +use SilverStripe\View\ViewableData; /** * Extend this class when designing a {@link DBField} that doesn't have a 1-1 mapping with a database field. @@ -29,26 +30,21 @@ abstract class DBComposite extends DBField * holds an array of composite field names. * Don't include the fields "main name", * it will be prefixed in {@link requireField()}. - * - * @config - * @var array */ - private static $composite_db = []; + private static array $composite_db = []; /** * Marker as to whether this record has changed * Only used when deference to the parent object isn't possible */ - protected $isChanged = false; + protected bool $isChanged = false; /** * Either the parent dataobject link, or a record of saved values for each field - * - * @var array|DataObject */ - protected $record = []; + protected array|ViewableData $record = []; - public function __set($property, $value) + public function __set(string $property, mixed $value): void { // Prevent failover / extensions from hijacking composite field setters // by intentionally avoiding hasMethod() @@ -59,7 +55,7 @@ public function __set($property, $value) parent::__set($property, $value); } - public function __get($property) + public function __get(string $property): mixed { // Prevent failover / extensions from hijacking composite field getters // by intentionally avoiding hasMethod() @@ -71,10 +67,8 @@ public function __get($property) /** * Write all nested fields into a manipulation - * - * @param array $manipulation */ - public function writeToManipulation(&$manipulation) + public function writeToManipulation(array &$manipulation): void { foreach ($this->compositeDatabaseFields() as $field => $spec) { // Write sub-manipulation @@ -88,10 +82,8 @@ public function writeToManipulation(&$manipulation) * and {@link $composite_db}, or any additional SQL that is required * to get to these columns. Will mostly just write to the {@link SQLSelect->select} * array. - * - * @param SQLSelect $query */ - public function addToQuery(&$query) + public function addToQuery(SQLSelect &$query): void { parent::addToQuery($query); @@ -109,12 +101,10 @@ public function addToQuery(&$query) /** * Return array in the format of {@link $composite_db}. * Used by {@link DataObject->hasOwnDatabaseField()}. - * - * @return array */ - public function compositeDatabaseFields() + public function compositeDatabaseFields(): array { - return $this->config()->composite_db; + return static::config()->get('composite_db'); } @@ -122,7 +112,7 @@ public function compositeDatabaseFields() * Returns true if this composite field has changed. * For fields bound to a DataObject, this will be cleared when the DataObject is written. */ - public function isChanged() + public function isChanged(): bool { // When unbound, use the local changed flag if (!$this->record instanceof DataObject) { @@ -141,10 +131,8 @@ public function isChanged() /** * Composite field defaults to exists only if all fields have values - * - * @return boolean */ - public function exists() + public function exists(): bool { // By default all fields foreach ($this->compositeDatabaseFields() as $field => $spec) { @@ -156,7 +144,7 @@ public function exists() return true; } - public function requireField() + public function requireField(): void { foreach ($this->compositeDatabaseFields() as $field => $spec) { $key = $this->getName() . $field; @@ -171,12 +159,9 @@ public function requireField() * * {@see ViewableData::obj} * - * @param mixed $value - * @param mixed $record Parent object to this field, which could be a DataObject, record array, or other - * @param bool $markChanged - * @return $this + * @param null|array|ViewableData $record Parent object to this field, which could be a DataObject, record array, or other */ - public function setValue($value, $record = null, $markChanged = true) + public function setValue(mixed $value, null|array|ViewableData $record = null, bool $markChanged = true): static { $this->isChanged = $markChanged; @@ -206,16 +191,14 @@ public function setValue($value, $record = null, $markChanged = true) } /** - * Bind this field to the dataobject, and set the underlying table to that of the owner - * - * @param DataObject $dataObject + * Bind this field to the model, and set the underlying table to that of the owner */ - public function bindTo($dataObject) + public function bindTo(DataObject $dataObject): void { $this->record = $dataObject; } - public function saveInto($dataObject) + public function saveInto(ViewableData $dataObject): void { foreach ($this->compositeDatabaseFields() as $field => $spec) { // Save into record @@ -230,52 +213,44 @@ public function saveInto($dataObject) /** * get value of a single composite field - * - * @param string $field - * @return mixed */ - public function getField($field) + public function getField(string $fieldName): mixed { // Skip invalid fields $fields = $this->compositeDatabaseFields(); - if (!isset($fields[$field])) { + if (!isset($fields[$fieldName])) { return null; } // Check bound object if ($this->record instanceof DataObject) { - $key = $this->getName() . $field; + $key = $this->getName() . $fieldName; return $this->record->getField($key); } // Check local record - if (isset($this->record[$field])) { - return $this->record[$field]; + if (isset($this->record[$fieldName])) { + return $this->record[$fieldName]; } return null; } - public function hasField($field) + public function hasField(string $fieldName): bool { $fields = $this->compositeDatabaseFields(); - return isset($fields[$field]); + return isset($fields[$fieldName]); } /** * Set value of a single composite field - * - * @param string $field - * @param mixed $value - * @param bool $markChanged - * @return $this */ - public function setField($field, $value, $markChanged = true) + public function setField(string $fieldName, mixed $value, bool $markChanged = true): static { $this->objCacheClear(); - if (!$this->hasField($field)) { + if (!$this->hasField($fieldName)) { throw new InvalidArgumentException(implode(' ', [ - "Field $field does not exist.", + "Field $fieldName does not exist.", 'If this was accessed via a dynamic property then call setDynamicData() instead.' ])); } @@ -287,23 +262,20 @@ public function setField($field, $value, $markChanged = true) // Set bound object if ($this->record instanceof DataObject) { - $key = $this->getName() . $field; + $key = $this->getName() . $fieldName; $this->record->setField($key, $value); return $this; } // Set local record - $this->record[$field] = $value; + $this->record[$fieldName] = $value; return $this; } /** * Get a db object for the named field - * - * @param string $field Field name - * @return DBField|null */ - public function dbObject($field) + public function dbObject(string $field): ?DBField { $fields = $this->compositeDatabaseFields(); if (!isset($fields[$field])) { @@ -319,7 +291,7 @@ public function dbObject($field) return $fieldObject; } - public function castingHelper($field, bool $useFallback = true) + public function castingHelper(string $field, bool $useFallback = true): ?string { $fields = $this->compositeDatabaseFields(); if (isset($fields[$field])) { @@ -329,7 +301,7 @@ public function castingHelper($field, bool $useFallback = true) return parent::castingHelper($field, $useFallback); } - public function getIndexSpecs() + public function getIndexSpecs(): array { if ($type = $this->getIndexType()) { $columns = array_map(function ($name) { @@ -341,9 +313,10 @@ public function getIndexSpecs() 'columns' => $columns, ]; } + return []; } - public function scalarValueOnly() + public function scalarValueOnly(): bool { return false; } diff --git a/src/ORM/FieldType/DBCurrency.php b/src/ORM/FieldType/DBCurrency.php index 258d427f4b7..20e612365d3 100644 --- a/src/ORM/FieldType/DBCurrency.php +++ b/src/ORM/FieldType/DBCurrency.php @@ -3,6 +3,8 @@ namespace SilverStripe\ORM\FieldType; use SilverStripe\Forms\CurrencyField; +use SilverStripe\Forms\FormField; +use SilverStripe\View\ViewableData; /** * Represents a decimal field containing a currency amount. @@ -20,22 +22,16 @@ class DBCurrency extends DBDecimal { /** - * @config - * @var string + * The symbol that represents the currency */ - private static $currency_symbol = '$'; - - public function __construct($name = null, $wholeSize = 9, $decimalSize = 2, $defaultValue = 0) - { - parent::__construct($name, $wholeSize, $decimalSize, $defaultValue); - } + private static string $currency_symbol = '$'; /** * Returns the number as a currency, eg “$1,000.00”. */ - public function Nice() + public function Nice(): string { - $val = $this->config()->currency_symbol . number_format(abs($this->value ?? 0.0) ?? 0.0, 2); + $val = static::config()->get('currency_symbol') . number_format(abs($this->value ?? 0.0) ?? 0.0, 2); if ($this->value < 0) { return "($val)"; } @@ -46,22 +42,22 @@ public function Nice() /** * Returns the number as a whole-number currency, eg “$1,000”. */ - public function Whole() + public function Whole(): string { - $val = $this->config()->currency_symbol . number_format(abs($this->value ?? 0.0) ?? 0.0, 0); + $val = static::config()->get('currency_symbol') . number_format(abs($this->value ?? 0.0) ?? 0.0, 0); if ($this->value < 0) { return "($val)"; } return $val; } - public function setValue($value, $record = null, $markChanged = true) + public function setValue(mixed $value, null|array|ViewableData $record = null, bool $markChanged = true): static { $matches = null; if (is_numeric($value)) { $this->value = $value; } elseif (preg_match('/-?\$?[0-9,]+(.[0-9]+)?([Ee][0-9]+)?/', $value ?? '', $matches)) { - $this->value = str_replace(['$', ',', $this->config()->currency_symbol], '', $matches[0] ?? ''); + $this->value = str_replace(['$', ',', static::config()->get('currency_symbol')], '', $matches[0] ?? ''); } else { $this->value = 0; } @@ -69,13 +65,7 @@ public function setValue($value, $record = null, $markChanged = true) return $this; } - /** - * @param string $title - * @param array $params - * - * @return CurrencyField - */ - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { return CurrencyField::create($this->getName(), $title); } diff --git a/src/ORM/FieldType/DBDate.php b/src/ORM/FieldType/DBDate.php index a035d018884..40dcbaac963 100644 --- a/src/ORM/FieldType/DBDate.php +++ b/src/ORM/FieldType/DBDate.php @@ -6,10 +6,12 @@ use InvalidArgumentException; use NumberFormatter; use SilverStripe\Forms\DateField; +use SilverStripe\Forms\FormField; use SilverStripe\i18n\i18n; use SilverStripe\ORM\DB; use SilverStripe\Security\Member; use SilverStripe\Security\Security; +use SilverStripe\View\ViewableData; /** * Represents a date field. @@ -32,15 +34,15 @@ class DBDate extends DBField /** * Standard ISO format string for date in CLDR standard format */ - const ISO_DATE = 'y-MM-dd'; + public const ISO_DATE = 'y-MM-dd'; /** * Fixed locale to use for ISO date formatting. This is necessary to prevent * locale-specific numeric localisation breaking internal date strings. */ - const ISO_LOCALE = 'en_US'; + public const ISO_LOCALE = 'en_US'; - public function setValue($value, $record = null, $markChanged = true) + public function setValue(mixed $value, null|array|ViewableData $record = null, bool $markChanged = true): static { $value = $this->parseDate($value); if ($value === false) { @@ -58,7 +60,7 @@ public function setValue($value, $record = null, $markChanged = true) * @param mixed $value * @return string|null|false Formatted date, null if empty but valid, or false if invalid */ - protected function parseDate($value) + protected function parseDate(mixed $value): string|null|false { // Skip empty values if (empty($value) && !is_numeric($value)) { @@ -89,13 +91,11 @@ protected function parseDate($value) /** * Returns the standard localised medium date - * - * @return ?string */ - public function Nice() + public function Nice(): string { if (!$this->value) { - return null; + return ''; } $formatter = $this->getFormatter(); return $formatter->format($this->getTimestamp()); @@ -103,40 +103,32 @@ public function Nice() /** * Returns the year from the given date - * - * @return string */ - public function Year() + public function Year(): string { return $this->Format('y'); } /** * Returns the day of the week - * - * @return string */ - public function DayOfWeek() + public function DayOfWeek(): string { return $this->Format('cccc'); } /** * Returns a full textual representation of a month, such as January. - * - * @return string */ - public function Month() + public function Month(): string { return $this->Format('LLLL'); } /** * Returns the short version of the month such as Jan - * - * @return string */ - public function ShortMonth() + public function ShortMonth(): string { return $this->Format('LLL'); } @@ -145,9 +137,8 @@ public function ShortMonth() * Returns the day of the month. * * @param bool $includeOrdinal Include ordinal suffix to day, e.g. "th" or "rd" - * @return string */ - public function DayOfMonth($includeOrdinal = false) + public function DayOfMonth(bool $includeOrdinal = false): string { $number = $this->Format('d'); if ($includeOrdinal && $number) { @@ -159,13 +150,11 @@ public function DayOfMonth($includeOrdinal = false) /** * Returns the date in the localised short format - * - * @return string */ - public function Short() + public function Short(): string { if (!$this->value) { - return null; + return ''; } $formatter = $this->getFormatter(IntlDateFormatter::SHORT); return $formatter->format($this->getTimestamp()); @@ -173,13 +162,11 @@ public function Short() /** * Returns the date in the localised long format - * - * @return string */ - public function Long() + public function Long(): string { if (!$this->value) { - return null; + return ''; } $formatter = $this->getFormatter(IntlDateFormatter::LONG); return $formatter->format($this->getTimestamp()); @@ -187,13 +174,11 @@ public function Long() /** * Returns the date in the localised full format - * - * @return string */ - public function Full() + public function Full(): string { if (!$this->value) { - return null; + return ''; } $formatter = $this->getFormatter(IntlDateFormatter::FULL); return $formatter->format($this->getTimestamp()); @@ -201,12 +186,8 @@ public function Full() /** * Get date formatter - * - * @param int $dateLength - * @param int $timeLength - * @return IntlDateFormatter */ - public function getFormatter($dateLength = IntlDateFormatter::MEDIUM, $timeLength = IntlDateFormatter::NONE) + public function getFormatter(int $dateLength = IntlDateFormatter::MEDIUM, int $timeLength = IntlDateFormatter::NONE): IntlDateFormatter { return $this->getCustomFormatter(null, null, $dateLength, $timeLength); } @@ -216,16 +197,13 @@ public function getFormatter($dateLength = IntlDateFormatter::MEDIUM, $timeLengt * * @param string|null $locale The current locale, or null to use default * @param string|null $pattern Custom pattern to use for this, if required - * @param int $dateLength - * @param int $timeLength - * @return IntlDateFormatter */ public function getCustomFormatter( - $locale = null, - $pattern = null, - $dateLength = IntlDateFormatter::MEDIUM, - $timeLength = IntlDateFormatter::NONE - ) { + ?string $locale = null, + ?string $pattern = null, + int $dateLength = IntlDateFormatter::MEDIUM, + int $timeLength = IntlDateFormatter::NONE + ): IntlDateFormatter { $locale = $locale ?: i18n::get_locale(); $formatter = IntlDateFormatter::create($locale, $dateLength, $timeLength); if ($pattern) { @@ -238,9 +216,8 @@ public function getCustomFormatter( * Formatter used internally * * @internal - * @return IntlDateFormatter */ - protected function getInternalFormatter() + protected function getInternalFormatter(): IntlDateFormatter { $formatter = $this->getCustomFormatter(DBDate::ISO_LOCALE, DBDate::ISO_DATE); $formatter->setLenient(false); @@ -249,10 +226,8 @@ protected function getInternalFormatter() /** * Get standard ISO date format string - * - * @return string */ - public function getISOFormat() + public function getISOFormat(): string { return DBDate::ISO_DATE; } @@ -262,16 +237,13 @@ public function getISOFormat() * for the day of the month ("1st", "2nd", "3rd" etc) * * @param string $format Format code string. See https://unicode-org.github.io/icu/userguide/format_parse/datetime - * @param string $locale Custom locale to use (add to signature in 5.0) - * @return ?string The date in the requested format + * @param string|null $locale Custom locale to use + * @return string The date in the requested format */ - public function Format($format) + public function Format(string $format, ?string $locale = null): string { - // Note: soft-arg uses func_get_args() to respect semver. Add to signature in 5.0 - $locale = func_num_args() > 1 ? func_get_arg(1) : null; - if (!$this->value) { - return null; + return ''; } // Replace {o} with ordinal representation of day of the month @@ -285,10 +257,8 @@ public function Format($format) /** * Get unix timestamp for this date - * - * @return int */ - public function getTimestamp() + public function getTimestamp(): int { if ($this->value) { return strtotime($this->value ?? ''); @@ -299,10 +269,9 @@ public function getTimestamp() /** * Return a date formatted as per a CMS user's settings. * - * @param Member $member - * @return boolean | string A date formatted as per user-defined settings. + * @return string A date formatted as per user-defined settings. */ - public function FormatFromSettings($member = null) + public function FormatFromSettings(?Member $member = null): string { if (!$member) { $member = Security::getCurrentUser(); @@ -512,7 +481,7 @@ public function TimeDiffIn($format) } } - public function requireField() + public function requireField(): void { $parts = ['datatype' => 'date', 'arrayValue' => $this->arrayValue]; $values = ['type' => 'date', 'parts' => $parts]; @@ -575,7 +544,7 @@ public function URLDate() return rawurlencode($this->Format(DBDate::ISO_DATE, DBDate::ISO_LOCALE) ?? ''); } - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { $field = DateField::create($this->name, $title); $field->setHTML5(true); diff --git a/src/ORM/FieldType/DBDatetime.php b/src/ORM/FieldType/DBDatetime.php index 877c707f260..3f1cbf81581 100644 --- a/src/ORM/FieldType/DBDatetime.php +++ b/src/ORM/FieldType/DBDatetime.php @@ -6,10 +6,12 @@ use IntlDateFormatter; use InvalidArgumentException; use SilverStripe\Forms\DatetimeField; +use SilverStripe\Forms\FormField; use SilverStripe\ORM\DB; use SilverStripe\Security\Member; use SilverStripe\Security\Security; use SilverStripe\View\TemplateGlobalProvider; +use SilverStripe\View\ViewableData; /** * Represents a date-time field. @@ -37,35 +39,36 @@ class DBDatetime extends DBDate implements TemplateGlobalProvider * Standard ISO format string for date and time in CLDR standard format, * with a whitespace separating date and time (common database representation, e.g. in MySQL). */ - const ISO_DATETIME = 'y-MM-dd HH:mm:ss'; + public const ISO_DATETIME = 'y-MM-dd HH:mm:ss'; /** * Standard ISO format string for date and time in CLDR standard format, * with a "T" separator between date and time (W3C standard, e.g. for HTML5 datetime-local fields). */ - const ISO_DATETIME_NORMALISED = 'y-MM-dd\'T\'HH:mm:ss'; + public const ISO_DATETIME_NORMALISED = 'y-MM-dd\'T\'HH:mm:ss'; /** * Flag idicating if this field is considered immutable * when this is enabled setting the value of this field will return a new field instance * instead updatin the old one - * - * @var bool */ - protected $immutable = false; + protected bool $immutable = false; /** - * @param bool $immutable - * @return $this + * Used to set a specific time for "now", useful for unit tests. */ - public function setImmutable(bool $immutable): DBDatetime + protected static ?DBDatetime $mock_now = null; + + /** + * Set whether this field is mutable (can be modified) or immutable (cannot be modified) + */ + public function setImmutable(bool $immutable): static { $this->immutable = $immutable; - return $this; } - public function setValue($value, $record = null, $markChanged = true) + public function setValue(mixed $value, null|array|ViewableData $record = null, bool $markChanged = true): static { if ($this->immutable) { // This field is set as immutable so we have to create a new field instance @@ -87,10 +90,8 @@ public function setValue($value, $record = null, $markChanged = true) /** * Returns the standard localised date - * - * @return string Formatted date. */ - public function Date() + public function Date(): string { $formatter = $this->getFormatter(IntlDateFormatter::MEDIUM, IntlDateFormatter::NONE); return $formatter->format($this->getTimestamp()); @@ -98,10 +99,8 @@ public function Date() /** * Returns the standard localised time - * - * @return string Formatted time. */ - public function Time() + public function Time(): string { $formatter = $this->getFormatter(IntlDateFormatter::NONE, IntlDateFormatter::MEDIUM); return $formatter->format($this->getTimestamp()); @@ -109,20 +108,16 @@ public function Time() /** * Returns the time in 12-hour format using the format string 'h:mm a' e.g. '1:32 pm'. - * - * @return string Formatted time. */ - public function Time12() + public function Time12(): string { return $this->Format('h:mm a'); } /** * Returns the time in 24-hour format using the format string 'H:mm' e.g. '13:32'. - * - * @return string Formatted time. */ - public function Time24() + public function Time24(): string { return $this->Format('H:mm'); } @@ -130,10 +125,9 @@ public function Time24() /** * Return a date and time formatted as per a CMS user's settings. * - * @param Member $member - * @return boolean|string A time and date pair formatted as per user-defined settings. + * @return string A time and date pair formatted as per user-defined settings. */ - public function FormatFromSettings($member = null) + public function FormatFromSettings(?Member $member = null): string { if (!$member) { $member = Security::getCurrentUser(); @@ -151,7 +145,7 @@ public function FormatFromSettings($member = null) return $this->Format($dateFormat . ' ' . $timeFormat, $member->getLocale()); } - public function requireField() + public function requireField(): void { $parts = [ 'datatype' => 'datetime', @@ -167,15 +161,13 @@ public function requireField() /** * Returns the url encoded date and time in ISO 6801 format using format * string 'y-MM-dd%20HH:mm:ss' e.g. '2014-02-28%2013:32:22'. - * - * @return string Formatted date and time. */ - public function URLDatetime() + public function URLDatetime(): string { return rawurlencode($this->Format(DBDatetime::ISO_DATETIME, DBDatetime::ISO_LOCALE) ?? ''); } - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { $field = DatetimeField::create($this->name, $title); $dateTimeFormat = $field->getDatetimeFormat(); @@ -195,18 +187,11 @@ public function scaffoldFormField($title = null, $params = null) return $field; } - /** - * - */ - protected static $mock_now = null; - /** * Returns either the current system date as determined * by date(), or a mocked date through {@link set_mock_now()}. - * - * @return static */ - public static function now() + public static function now(): static { $time = DBDatetime::$mock_now ? DBDatetime::$mock_now->Value : time(); @@ -224,7 +209,7 @@ public static function now() * @param DBDatetime|string $datetime Either in object format, or as a DBDatetime compatible string. * @throws Exception */ - public static function set_mock_now($datetime) + public static function set_mock_now(DBDatetime|string $datetime): void { if (!$datetime instanceof DBDatetime) { $value = $datetime; @@ -240,20 +225,15 @@ public static function set_mock_now($datetime) * Clear any mocked date, which causes * {@link Now()} to return the current system date. */ - public static function clear_mock_now() + public static function clear_mock_now(): void { DBDatetime::$mock_now = null; } /** * Run a callback with specific time, original mock value is retained after callback - * - * @param DBDatetime|string $time - * @param callable $callback - * @return mixed - * @throws Exception */ - public static function withFixedNow($time, $callback) + public static function withFixedNow(DBDatetime|string $time, callable $callback): mixed { $original = DBDatetime::$mock_now; @@ -266,7 +246,7 @@ public static function withFixedNow($time, $callback) } } - public static function get_template_global_variables() + public static function get_template_global_variables(): array { return [ 'Now' => ['method' => 'now', 'casting' => 'Datetime'], @@ -275,13 +255,11 @@ public static function get_template_global_variables() /** * Get date / time formatter for the current locale - * - * @param int $dateLength - * @param int $timeLength - * @return IntlDateFormatter */ - public function getFormatter($dateLength = IntlDateFormatter::MEDIUM, $timeLength = IntlDateFormatter::SHORT) - { + public function getFormatter( + int $dateLength = IntlDateFormatter::MEDIUM, + int $timeLength = IntlDateFormatter::SHORT + ): IntlDateFormatter { return parent::getFormatter($dateLength, $timeLength); } @@ -291,16 +269,13 @@ public function getFormatter($dateLength = IntlDateFormatter::MEDIUM, $timeLengt * * @param string|null $locale The current locale, or null to use default * @param string|null $pattern Custom pattern to use for this, if required - * @param int $dateLength - * @param int $timeLength - * @return IntlDateFormatter */ public function getCustomFormatter( - $locale = null, - $pattern = null, - $dateLength = IntlDateFormatter::MEDIUM, - $timeLength = IntlDateFormatter::MEDIUM - ) { + ?string $locale = null, + ?string $pattern = null, + int $dateLength = IntlDateFormatter::MEDIUM, + int $timeLength = IntlDateFormatter::MEDIUM + ): IntlDateFormatter { return parent::getCustomFormatter($locale, $pattern, $dateLength, $timeLength); } @@ -308,9 +283,8 @@ public function getCustomFormatter( * Formatter used internally * * @internal - * @return IntlDateFormatter */ - protected function getInternalFormatter() + protected function getInternalFormatter(): IntlDateFormatter { $formatter = $this->getCustomFormatter(DBDate::ISO_LOCALE, DBDatetime::ISO_DATETIME); $formatter->setLenient(false); @@ -319,10 +293,8 @@ protected function getInternalFormatter() /** * Get standard ISO date format string - * - * @return string */ - public function getISOFormat() + public function getISOFormat(): string { return DBDatetime::ISO_DATETIME; } diff --git a/src/ORM/FieldType/DBDecimal.php b/src/ORM/FieldType/DBDecimal.php index dcf2d7e8046..0b337786e34 100644 --- a/src/ORM/FieldType/DBDecimal.php +++ b/src/ORM/FieldType/DBDecimal.php @@ -2,71 +2,55 @@ namespace SilverStripe\ORM\FieldType; +use SilverStripe\Forms\FormField; use SilverStripe\Forms\NumericField; use SilverStripe\ORM\DB; +use SilverStripe\View\ViewableData; /** * Represents a Decimal field. */ class DBDecimal extends DBField { - /** * Whole number size - * - * @var int */ - protected $wholeSize = 9; + protected int $wholeSize = 9; /** * Decimal scale - * - * @var int */ - protected $decimalSize = 2; + protected int $decimalSize = 2; /** * Default value - * - * @var string */ - protected $defaultValue = 0; + protected float|int|string $defaultValue = 0; /** * Create a new Decimal field. - * - * @param string $name - * @param int $wholeSize - * @param int $decimalSize - * @param float|int $defaultValue */ - public function __construct($name = null, $wholeSize = 9, $decimalSize = 2, $defaultValue = 0) + public function __construct(?string $name = null, ?int $wholeSize = 9, ?int $decimalSize = 2, float|int $defaultValue = 0) { $this->wholeSize = is_int($wholeSize) ? $wholeSize : 9; $this->decimalSize = is_int($decimalSize) ? $decimalSize : 2; - $this->defaultValue = number_format((float) $defaultValue, $decimalSize ?? 0); + $this->defaultValue = number_format((float) $defaultValue, $this->decimalSize); parent::__construct($name); } - /** - * @return float - */ - public function Nice() + public function Nice(): string { return number_format($this->value ?? 0.0, $this->decimalSize ?? 0); } - /** - * @return int - */ - public function Int() + public function Int(): int { return floor($this->value ?? 0.0); } - public function requireField() + public function requireField(): void { $parts = [ 'datatype' => 'decimal', @@ -83,16 +67,16 @@ public function requireField() DB::require_field($this->tableName, $this->name, $values); } - public function saveInto($dataObject) + public function saveInto(ViewableData $model): void { $fieldName = $this->name; if ($fieldName) { if ($this->value instanceof DBField) { - $this->value->saveInto($dataObject); + $this->value->saveInto($model); } else { $value = (float) preg_replace('/[^0-9.\-\+]/', '', $this->value ?? ''); - $dataObject->__set($fieldName, $value); + $model->__set($fieldName, $value); } } else { throw new \UnexpectedValueException( @@ -101,27 +85,18 @@ public function saveInto($dataObject) } } - /** - * @param string $title - * @param array $params - * - * @return NumericField - */ - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { return NumericField::create($this->name, $title) ->setScale($this->decimalSize); } - /** - * @return float - */ - public function nullValue() + public function nullValue(): ?int { return 0; } - public function prepValueForDB($value) + public function prepValueForDB(mixed $value): float|int|null { if ($value === true) { return 1; diff --git a/src/ORM/FieldType/DBDouble.php b/src/ORM/FieldType/DBDouble.php index 04b91eab5ae..17d7f69eeb4 100644 --- a/src/ORM/FieldType/DBDouble.php +++ b/src/ORM/FieldType/DBDouble.php @@ -11,7 +11,7 @@ class DBDouble extends DBFloat { - public function requireField() + public function requireField(): void { // HACK: MSSQL does not support double so we're using float instead if (DB::get_conn() instanceof MySQLDatabase) { diff --git a/src/ORM/FieldType/DBEnum.php b/src/ORM/FieldType/DBEnum.php index b85dec01361..7f0eaba527a 100644 --- a/src/ORM/FieldType/DBEnum.php +++ b/src/ORM/FieldType/DBEnum.php @@ -4,6 +4,8 @@ use SilverStripe\Core\Config\Config; use SilverStripe\Forms\DropdownField; +use SilverStripe\Forms\FormField; +use SilverStripe\Forms\SelectField; use SilverStripe\ORM\ArrayLib; use SilverStripe\ORM\Connect\MySQLDatabase; use SilverStripe\ORM\DB; @@ -15,35 +17,28 @@ */ class DBEnum extends DBString { - /** * List of enum values - * - * @var array */ - protected $enum = []; + protected array $enum = []; /** * Default value - * - * @var string|null */ - protected $default = null; + protected ?string $default = null; - private static $default_search_filter_class = 'ExactMatchFilter'; + private static string $default_search_filter_class = 'ExactMatchFilter'; /** * Internal cache for obsolete enum values. The top level keys are the table, each of which contains * nested arrays with keys mapped to field names. The values of the lowest level array are the enum values - * - * @var array */ - protected static $enum_cache = []; + protected static array $enum_cache = []; /** * Clear all cached enum values. */ - public static function flushCache() + public static function flushCache(): void { DBEnum::$enum_cache = []; } @@ -60,15 +55,18 @@ public static function flushCache() * "MyField" => "Enum(['Val1', 'Val2', 'Val3'], 'Val1')" // Supports array notation as well * </code> * - * @param string $name * @param string|array $enum A string containing a comma separated list of options or an array of Vals. * @param string|int|null $default The default option, which is either NULL or one of the items in the enumeration. * If passing in an integer (non-string) it will default to the index of that item in the list. * Set to null or empty string to allow empty values - * @param array $options Optional parameters for this DB field + * @param array $options Optional parameters for this DB field */ - public function __construct($name = null, $enum = null, $default = 0, $options = []) - { + public function __construct( + ?string $name = null, + string|array|null $enum = null, + string|int|null $default = 0, + array $options = [] + ) { if ($enum) { $this->setEnum($enum); $enum = $this->getEnum(); @@ -94,10 +92,7 @@ public function __construct($name = null, $enum = null, $default = 0, $options = parent::__construct($name, $options); } - /** - * @return void - */ - public function requireField() + public function requireField(): void { $charset = Config::inst()->get(MySQLDatabase::class, 'charset'); $collation = Config::inst()->get(MySQLDatabase::class, 'collation'); @@ -121,18 +116,15 @@ public function requireField() } /** - * Return a dropdown field suitable for editing this field. - * - * @param string $title - * @param string $name - * @param bool $hasEmpty - * @param string $value - * @param string $emptyString - * @return DropdownField + * Return a form field suitable for editing this field. */ - public function formField($title = null, $name = null, $hasEmpty = false, $value = '', $emptyString = null) - { - + public function formField( + ?string $title = null, + ?string $name = null, + bool $hasEmpty = false, + ?string $value = '', + ?string $emptyString = null + ): SelectField { if (!$title) { $title = $this->getName(); } @@ -148,16 +140,12 @@ public function formField($title = null, $name = null, $hasEmpty = false, $value return $field; } - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { return $this->formField($title); } - /** - * @param string $title - * @return DropdownField - */ - public function scaffoldSearchField($title = null) + public function scaffoldSearchField(?string $title = null): ?FormField { $anyText = _t(__CLASS__ . '.ANY', 'Any'); return $this->formField($title, null, true, '', "($anyText)"); @@ -166,12 +154,8 @@ public function scaffoldSearchField($title = null) /** * Returns the values of this enum as an array, suitable for insertion into * a {@link DropdownField} - * - * @param bool $hasEmpty - * - * @return array */ - public function enumValues($hasEmpty = false) + public function enumValues(bool $hasEmpty = false): array { return ($hasEmpty) ? array_merge(['' => ''], ArrayLib::valuekey($this->getEnum())) @@ -180,15 +164,12 @@ public function enumValues($hasEmpty = false) /** * Get list of enum values - * - * @return array */ - public function getEnum() + public function getEnum(): array { return $this->enum; } - /** * Get the list of enum values, including obsolete values still present in the database * @@ -196,10 +177,8 @@ public function getEnum() * then only known enum values are returned. * * Values cached in this method can be cleared via `DBEnum::flushCache();` - * - * @return array */ - public function getEnumObsolete() + public function getEnumObsolete(): array { // Without a table or field specified, we can only retrieve known enum values $table = $this->getTable(); @@ -232,11 +211,8 @@ public function getEnumObsolete() /** * Set enum options - * - * @param string|array $enum - * @return $this */ - public function setEnum($enum) + public function setEnum(string|array $enum): static { if (!is_array($enum)) { $enum = preg_split( @@ -250,22 +226,17 @@ public function setEnum($enum) } /** - * Get default vwalue - * - * @return string|null + * Get default value */ - public function getDefault() + public function getDefault(): ?string { return $this->default; } /** * Set default value - * - * @param string $default - * @return $this */ - public function setDefault($default) + public function setDefault(?string $default): static { $this->default = $default; $this->setDefaultValue($default); diff --git a/src/ORM/FieldType/DBField.php b/src/ORM/FieldType/DBField.php index 94b5ee31f15..0053057e4ef 100644 --- a/src/ORM/FieldType/DBField.php +++ b/src/ORM/FieldType/DBField.php @@ -7,7 +7,6 @@ use SilverStripe\Core\Injector\Injector; use SilverStripe\Forms\FormField; use SilverStripe\Forms\TextField; -use SilverStripe\ORM\DataObject; use SilverStripe\ORM\Filters\SearchFilter; use SilverStripe\ORM\Queries\SQLSelect; use SilverStripe\View\ViewableData; @@ -36,7 +35,7 @@ * * <code> * class Blob extends DBField { - * function requireField() { + * function requireField(): void { * DB::require_field($this->tableName, $this->name, "blob"); * } * } @@ -47,65 +46,47 @@ abstract class DBField extends ViewableData implements DBIndexable /** * Raw value of this field - * - * @var mixed */ - protected $value; + protected mixed $value = null; /** * Table this field belongs to - * - * @var string */ - protected $tableName; + protected ?string $tableName = null; /** * Name of this field - * - * @var string */ - protected $name; + protected ?string $name = null; /** * Used for generating DB schema. {@see DBSchemaManager} - * - * @var array + * Despite its name, this seems to be a string */ protected $arrayValue; /** * Optional parameters for this field - * - * @var array */ - protected $options = []; + protected array $options = []; /** * The escape type for this field when inserted into a template - either "xml" or "raw". - * - * @var string - * @config */ - private static $escape_type = 'raw'; + private static string $escape_type = 'raw'; /** * Subclass of {@link SearchFilter} for usage in {@link defaultSearchFilter()}. - * - * @var string - * @config */ - private static $default_search_filter_class = 'PartialMatchFilter'; + private static string $default_search_filter_class = 'PartialMatchFilter'; /** * The type of index to use for this field. Can either be a string (one of the DBIndexable type options) or a * boolean. When a boolean is given, false will not index the field, and true will use the default index type. - * - * @var string|bool - * @config */ - private static $index = false; + private static string|bool $index = false; - private static $casting = [ + private static array $casting = [ 'ATT' => 'HTMLFragment', 'CDATA' => 'HTMLFragment', 'HTML' => 'HTMLFragment', @@ -119,20 +100,18 @@ abstract class DBField extends ViewableData implements DBIndexable ]; /** - * @var $default mixed Default-value in the database. + * Default value in the database. * Might be overridden on DataObject-level, but still useful for setting defaults on * already existing records after a db-build. */ - protected $defaultVal; + protected mixed $defaultVal = null; /** * Provide the DBField name and an array of options, e.g. ['index' => true], or ['nullifyEmpty' => false] * - * @param string $name - * @param array $options * @throws InvalidArgumentException If $options was passed by not an array */ - public function __construct($name = null, $options = []) + public function __construct(?string $name = null, array $options = []) { $this->name = $name; @@ -154,12 +133,11 @@ public function __construct($name = null, $options = []) * @param string $spec Class specification to construct. May include both service name and additional * constructor arguments in the same format as DataObject.db config. * @param mixed $value value of field - * @param string $name Name of field + * @param null|string $name Name of field * @param mixed $args Additional arguments to pass to constructor if not using args in service $spec * Note: Will raise a warning if using both - * @return static */ - public static function create_field($spec, $value, $name = null, ...$args) + public static function create_field(string $spec, mixed $value, ?string $name = null, mixed ...$args): static { // Raise warning if inconsistent with DataObject::dbObject() behaviour // This will cause spec args to be shifted down by the number of provided $args @@ -182,12 +160,8 @@ public static function create_field($spec, $value, $name = null, ...$args) * the first place you can set a name. * * If you try an alter the name a warning will be thrown. - * - * @param string $name - * - * @return $this */ - public function setName($name) + public function setName(?string $name): static { if ($this->name && $this->name !== $name) { user_error("DBField::setName() shouldn't be called once a DBField already has a name." @@ -201,20 +175,16 @@ public function setName($name) /** * Returns the name of this field. - * - * @return string */ - public function getName() + public function getName(): string { - return $this->name; + return $this->name ?? ''; } /** * Returns the value of this field. - * - * @return mixed */ - public function getValue() + public function getValue(): mixed { return $this->value; } @@ -228,14 +198,12 @@ public function getValue() * and actually changing its values, it needs a {@link $markChanged} * parameter. * - * @param mixed $value - * @param DataObject|array $record An array or object that this field is part of + * @param null|ViewableData|array $record An array or object that this field is part of * @param bool $markChanged Indicate whether this field should be marked changed. * Set to FALSE if you are initializing this field after construction, rather * than setting a new value. - * @return $this */ - public function setValue($value, $record = null, $markChanged = true) + public function setValue(mixed $value, null|array|ViewableData $record = null, bool $markChanged = true): static { $this->value = $value; return $this; @@ -243,21 +211,16 @@ public function setValue($value, $record = null, $markChanged = true) /** * Get default value assigned at the DB level - * - * @return mixed */ - public function getDefaultValue() + public function getDefaultValue(): mixed { return $this->defaultVal; } /** * Set default value to use at the DB level - * - * @param mixed $defaultValue - * @return $this */ - public function setDefaultValue($defaultValue) + public function setDefaultValue(mixed $defaultValue): static { $this->defaultVal = $defaultValue; return $this; @@ -265,11 +228,8 @@ public function setDefaultValue($defaultValue) /** * Update the optional parameters for this field - * - * @param array $options Array of options - * @return $this */ - public function setOptions(array $options = []) + public function setOptions(array $options = []): static { $this->options = $options; return $this; @@ -277,15 +237,13 @@ public function setOptions(array $options = []) /** * Get optional parameters for this field - * - * @return array */ - public function getOptions() + public function getOptions(): array { return $this->options; } - public function setIndexType($type) + public function setIndexType($type): string|bool { if (!is_bool($type) && !in_array($type, [DBIndexable::TYPE_INDEX, DBIndexable::TYPE_UNIQUE, DBIndexable::TYPE_FULLTEXT]) @@ -320,10 +278,8 @@ public function getIndexType() /** * Determines if the field has a value which is not considered to be 'null' * in a database context. - * - * @return boolean */ - public function exists() + public function exists(): bool { return (bool)$this->value; } @@ -336,7 +292,7 @@ public function exists() * @param mixed $value The value to check * @return mixed The raw value, or escaped parameterised details */ - public function prepValueForDB($value) + public function prepValueForDB(mixed $value): mixed { if ($value === null || $value === "" || @@ -358,10 +314,8 @@ public function prepValueForDB($value) * can also be used to apply special SQL-commands * to the raw value (e.g. for GIS functionality). * {@see prepValueForDB} - * - * @param array $manipulation */ - public function writeToManipulation(&$manipulation) + public function writeToManipulation(array &$manipulation): void { $manipulation['fields'][$this->name] = $this->exists() ? $this->prepValueForDB($this->value) : $this->nullValue(); @@ -375,20 +329,15 @@ public function writeToManipulation(&$manipulation) * SELECT <tablename>.* which * gets you the default representations * of all columns. - * - * @param SQLSelect $query */ - public function addToQuery(&$query) + public function addToQuery(SQLSelect &$query) { } /** * Assign this DBField to a table - * - * @param string $tableName - * @return $this */ - public function setTable($tableName) + public function setTable(string $tableName): static { $this->tableName = $tableName; return $this; @@ -396,20 +345,16 @@ public function setTable($tableName) /** * Get the table this field belongs to, if assigned - * - * @return string|null */ - public function getTable() + public function getTable(): ?string { return $this->tableName; } /** * Determine 'default' casting for this field. - * - * @return string */ - public function forTemplate() + public function forTemplate(): string { // Default to XML encoding return $this->XML(); @@ -417,40 +362,32 @@ public function forTemplate() /** * Gets the value appropriate for a HTML attribute string - * - * @return string */ - public function HTMLATT() + public function HTMLATT(): string { return Convert::raw2htmlatt($this->RAW()); } /** * urlencode this string - * - * @return string */ - public function URLATT() + public function URLATT(): string { return urlencode($this->RAW() ?? ''); } /** * rawurlencode this string - * - * @return string */ - public function RAWURLATT() + public function RAWURLATT(): string { return rawurlencode($this->RAW() ?? ''); } /** * Gets the value appropriate for a HTML attribute string - * - * @return string */ - public function ATT() + public function ATT(): string { return Convert::raw2att($this->RAW()); } @@ -458,60 +395,48 @@ public function ATT() /** * Gets the raw value for this field. * Note: Skips processors implemented via forTemplate() - * - * @return mixed */ - public function RAW() + public function RAW(): mixed { return $this->getValue(); } /** * Gets javascript string literal value - * - * @return string */ - public function JS() + public function JS(): string { return Convert::raw2js($this->RAW()); } /** * Return JSON encoded value - * - * @return string */ - public function JSON() + public function JSON(): string { return json_encode($this->RAW()); } /** * Alias for {@see XML()} - * - * @return string */ - public function HTML() + public function HTML(): string { return $this->XML(); } /** * XML encode this value - * - * @return string */ - public function XML() + public function XML(): string { return Convert::raw2xml($this->RAW()); } /** * Safely escape for XML string - * - * @return string */ - public function CDATA() + public function CDATA(): string { return $this->XML(); } @@ -519,20 +444,16 @@ public function CDATA() /** * Returns the value to be set in the database to blank this field. * Usually it's a choice between null, 0, and '' - * - * @return mixed */ - public function nullValue() + public function nullValue(): mixed { return null; } /** * Saves this field to the given data object. - * - * @param DataObject $dataObject */ - public function saveInto($dataObject) + public function saveInto(ViewableData $model): void { $fieldName = $this->name; if (empty($fieldName)) { @@ -541,9 +462,9 @@ public function saveInto($dataObject) ); } if ($this->value instanceof DBField) { - $this->value->saveInto($dataObject); + $this->value->saveInto($model); } else { - $dataObject->__set($fieldName, $this->value); + $model->__set($fieldName, $this->value); } } @@ -554,10 +475,8 @@ public function saveInto($dataObject) * Used by {@link SearchContext}, {@link ModelAdmin}, {@link DataObject::scaffoldFormFields()} * * @param string $title Optional. Localized title of the generated instance - * @param array $params - * @return FormField */ - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { return TextField::create($this->name, $title); } @@ -569,30 +488,28 @@ public function scaffoldFormField($title = null, $params = null) * Used by {@link SearchContext}, {@link ModelAdmin}, {@link DataObject::scaffoldFormFields()}. * * @param string $title Optional. Localized title of the generated instance - * @return FormField */ - public function scaffoldSearchField($title = null) + public function scaffoldSearchField(?string $title = null): ?FormField { return $this->scaffoldFormField($title); } /** * @param string $name Override name of this field - * @return SearchFilter */ - public function defaultSearchFilter($name = null) + public function defaultSearchFilter(?string $name = null): SearchFilter { $name = ($name) ? $name : $this->name; - $filterClass = $this->config()->get('default_search_filter_class'); + $filterClass = static::config()->get('default_search_filter_class'); return Injector::inst()->create($filterClass, $name); } /** * Add the field to the underlying database. */ - abstract public function requireField(); + abstract public function requireField(): void; - public function debug() + public function debug(): string { return <<<DBG <ul> @@ -603,40 +520,31 @@ public function debug() DBG; } - public function __toString() + public function __toString(): string { return (string)$this->forTemplate(); } - /** - * @return array - */ public function getArrayValue() { return $this->arrayValue; } - /** - * @param array $value - * @return $this - */ - public function setArrayValue($value) + public function setArrayValue($value): static { $this->arrayValue = $value; return $this; } /** - * Get formfield schema value - * - * @return string|array Encoded string for use in formschema response + * Get formfield schema value for use in formschema response */ - public function getSchemaValue() + public function getSchemaValue(): mixed { return $this->RAW(); } - public function getIndexSpecs() + public function getIndexSpecs(): ?array { $type = $this->getIndexType(); if ($type) { @@ -652,9 +560,8 @@ public function getIndexSpecs() * Whether or not this DBField only accepts scalar values. * * Composite DBFields can override this method and return `false` so they can accept arrays of values. - * @return boolean */ - public function scalarValueOnly() + public function scalarValueOnly(): bool { return true; } diff --git a/src/ORM/FieldType/DBFloat.php b/src/ORM/FieldType/DBFloat.php index 8fd0e129eef..0bec2866db7 100644 --- a/src/ORM/FieldType/DBFloat.php +++ b/src/ORM/FieldType/DBFloat.php @@ -2,6 +2,7 @@ namespace SilverStripe\ORM\FieldType; +use SilverStripe\Forms\FormField; use SilverStripe\Forms\NumericField; use SilverStripe\ORM\DB; @@ -10,15 +11,14 @@ */ class DBFloat extends DBField { - - public function __construct($name = null, $defaultVal = 0) + public function __construct(?string $name = null, float|int $defaultVal = 0) { $this->defaultVal = is_float($defaultVal) ? $defaultVal : (float) 0; parent::__construct($name); } - public function requireField() + public function requireField(): void { $parts = [ 'datatype' => 'float', @@ -35,34 +35,34 @@ public function requireField() * * @uses number_format() */ - public function Nice() + public function Nice(): string { return number_format($this->value ?? 0.0, 2); } - public function Round($precision = 3) + public function Round($precision = 3): float { return round($this->value ?? 0.0, $precision ?? 0); } - public function NiceRound($precision = 3) + public function NiceRound($precision = 3): string { return number_format(round($this->value ?? 0.0, $precision ?? 0), $precision ?? 0); } - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { $field = NumericField::create($this->name, $title); $field->setScale(null); // remove no-decimal restriction return $field; } - public function nullValue() + public function nullValue(): ?int { return 0; } - public function prepValueForDB($value) + public function prepValueForDB(mixed $value): float|int|null { if ($value === true) { return 1; diff --git a/src/ORM/FieldType/DBForeignKey.php b/src/ORM/FieldType/DBForeignKey.php index 4265491ae18..5f7d95f55da 100644 --- a/src/ORM/FieldType/DBForeignKey.php +++ b/src/ORM/FieldType/DBForeignKey.php @@ -6,9 +6,11 @@ use SilverStripe\Assets\Image; use SilverStripe\Core\Injector\Injector; use SilverStripe\Forms\FileHandleField; +use SilverStripe\Forms\FormField; use SilverStripe\Forms\SearchableDropdownField; use SilverStripe\ORM\DataList; use SilverStripe\ORM\DataObject; +use SilverStripe\View\ViewableData; /** * A special type Int field used for foreign keys in has_one relationships. @@ -22,40 +24,26 @@ */ class DBForeignKey extends DBInt { - /** - * @var DataObject - */ - protected $object; + protected ?DataObject $object; /** * Number of related objects to show in a scaffolded searchable dropdown field before it * switches to using lazyloading. * This will also be used as the lazy load limit - * - * @config - * @var int */ - private static $dropdown_field_threshold = 100; - - private static $index = true; + private static int $dropdown_field_threshold = 100; - private static $default_search_filter_class = 'ExactMatchFilter'; + private static string|bool $index = true; - /** - * Cache for multiple subsequent calls to scaffold form fields with the same foreign key object - * - * @var array - * @deprecated 5.2.0 Will be removed without equivalent functionality to replace it - */ - protected static $foreignListCache = []; + private static string $default_search_filter_class = 'ExactMatchFilter'; - public function __construct($name, $object = null) + public function __construct(?string $name, ?DataObject $object = null) { $this->object = $object; parent::__construct($name); } - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { if (empty($this->object)) { return null; @@ -70,11 +58,11 @@ public function scaffoldFormField($title = null, $params = null) return $field; } - public function setValue($value, $record = null, $markChanged = true) + public function setValue(mixed $value, null|array|ViewableData $record = null, bool $markChanged = true): static { if ($record instanceof DataObject) { $this->object = $record; } - parent::setValue($value, $record, $markChanged); + return parent::setValue($value, $record, $markChanged); } } diff --git a/src/ORM/FieldType/DBHTMLText.php b/src/ORM/FieldType/DBHTMLText.php index 4241c94d59c..6ee57d2c998 100644 --- a/src/ORM/FieldType/DBHTMLText.php +++ b/src/ORM/FieldType/DBHTMLText.php @@ -4,6 +4,7 @@ use SilverStripe\Control\HTTP; use SilverStripe\Core\Convert; +use SilverStripe\Forms\FormField; use SilverStripe\Forms\HTMLEditor\HTMLEditorField; use SilverStripe\Forms\TextField; use SilverStripe\View\Parsers\HTMLValue; @@ -25,9 +26,9 @@ */ class DBHTMLText extends DBText { - private static $escape_type = 'xml'; + private static string $escape_type = 'xml'; - private static $casting = [ + private static array $casting = [ "AbsoluteLinks" => "HTMLFragment", // DBString conversion / summary methods // Not overridden, but returns HTML instead of plain text. @@ -37,68 +38,52 @@ class DBHTMLText extends DBText /** * Enable shortcode parsing on this field - * - * @var bool */ - protected $processShortcodes = false; + protected bool $processShortcodes = false; + + /** + * List of html properties to whitelist + */ + protected array $whitelist = []; /** * Check if shortcodes are enabled - * - * @return bool */ - public function getProcessShortcodes() + public function getProcessShortcodes(): bool { return $this->processShortcodes; } /** * Set shortcodes on or off by default - * - * @param bool $process - * @return $this */ - public function setProcessShortcodes($process) + public function setProcessShortcodes(bool $process): static { - $this->processShortcodes = (bool)$process; + $this->processShortcodes = $process; return $this; } /** * List of html properties to whitelist - * - * @var array - */ - protected $whitelist = []; - - /** - * List of html properties to whitelist - * - * @return array */ - public function getWhitelist() + public function getWhitelist(): array { return $this->whitelist; } /** * Set list of html properties to whitelist - * - * @param array $whitelist - * @return $this */ - public function setWhitelist($whitelist) + public function setWhitelist(string|array $whitelist): static { if (!is_array($whitelist)) { - $whitelist = preg_split('/\s*,\s*/', $whitelist ?? ''); + $whitelist = preg_split('/\s*,\s*/', $whitelist); } $this->whitelist = $whitelist; return $this; } /** - * @param array $options - * * Options accepted in addition to those provided by Text: * * - shortcodes: If true, shortcodes will be turned into the appropriate HTML. @@ -110,10 +95,8 @@ public function setWhitelist($whitelist) * Text nodes outside of HTML tags are filtered out by default, but may be included by adding * the text() directive. E.g. 'link,meta,text()' will allow only <link /> <meta /> and text at * the root level. - * - * @return $this */ - public function setOptions(array $options = []) + public function setOptions(array $options = []): static { if (array_key_exists("shortcodes", $options ?? [])) { $this->setProcessShortcodes(!!$options["shortcodes"]); @@ -126,7 +109,7 @@ public function setOptions(array $options = []) return parent::setOptions($options); } - public function RAW() + public function RAW(): ?string { if ($this->processShortcodes) { return ShortcodeParser::get_active()->parse($this->value); @@ -136,25 +119,22 @@ public function RAW() /** * Return the value of the field with relative links converted to absolute urls (with placeholders parsed). - * @return string */ - public function AbsoluteLinks() + public function AbsoluteLinks(): string { return HTTP::absoluteURLs($this->forTemplate()); } - public function forTemplate() + public function forTemplate(): string { // Suppress XML encoding for DBHtmlText - return $this->RAW(); + return $this->RAW() ?? ''; } /** * Safely escape for XML string - * - * @return string */ - public function CDATA() + public function CDATA(): string { return sprintf( '<![CDATA[%s]]>', @@ -162,7 +142,7 @@ public function CDATA() ); } - public function prepValueForDB($value) + public function prepValueForDB(mixed $value): ?string { return parent::prepValueForDB($this->whitelistContent($value)); } @@ -173,7 +153,7 @@ public function prepValueForDB($value) * @param string $value Input html content * @return string Value with all non-whitelisted content stripped (if applicable) */ - public function whitelistContent($value) + public function whitelistContent(string $value): string { if ($this->whitelist) { $dom = HTMLValue::create($value); @@ -199,22 +179,20 @@ public function whitelistContent($value) return $value; } - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { return HTMLEditorField::create($this->name, $title); } - public function scaffoldSearchField($title = null) + public function scaffoldSearchField(?string $title = null): ?FormField { return new TextField($this->name, $title); } /** * Get plain-text version - * - * @return string */ - public function Plain() + public function Plain(): string { // Preserve line breaks $text = preg_replace('/\<br(\s*)?\/?\>/i', "\n", $this->RAW() ?? ''); @@ -232,7 +210,7 @@ public function Plain() return trim(Convert::xml2raw($text) ?? ''); } - public function getSchemaValue() + public function getSchemaValue(): ?array { // Form schema format as HTML $value = $this->RAW(); @@ -242,7 +220,7 @@ public function getSchemaValue() return null; } - public function exists() + public function exists(): bool { // Optimisation: don't process shortcode just for ->exists() $value = $this->getValue(); diff --git a/src/ORM/FieldType/DBHTMLVarchar.php b/src/ORM/FieldType/DBHTMLVarchar.php index 3cc0f083743..f11762eaa8d 100644 --- a/src/ORM/FieldType/DBHTMLVarchar.php +++ b/src/ORM/FieldType/DBHTMLVarchar.php @@ -3,6 +3,7 @@ namespace SilverStripe\ORM\FieldType; use SilverStripe\Core\Convert; +use SilverStripe\Forms\FormField; use SilverStripe\Forms\HTMLEditor\HTMLEditorField; use SilverStripe\Forms\TextField; use SilverStripe\View\Parsers\ShortcodeParser; @@ -14,10 +15,9 @@ */ class DBHTMLVarchar extends DBVarchar { + private static string $escape_type = 'xml'; - private static $escape_type = 'xml'; - - private static $casting = [ + private static array $casting = [ // DBString conversion / summary methods // Not overridden, but returns HTML instead of plain text. "LowerCase" => "HTMLFragment", @@ -26,35 +26,27 @@ class DBHTMLVarchar extends DBVarchar /** * Enable shortcode parsing on this field - * - * @var bool */ - protected $processShortcodes = false; + protected bool $processShortcodes = false; /** * Check if shortcodes are enabled - * - * @return bool */ - public function getProcessShortcodes() + public function getProcessShortcodes(): bool { return $this->processShortcodes; } /** * Set shortcodes on or off by default - * - * @param bool $process - * @return $this */ - public function setProcessShortcodes($process) + public function setProcessShortcodes(bool $process): static { - $this->processShortcodes = (bool)$process; + $this->processShortcodes = $process; return $this; } + /** - * @param array $options - * * Options accepted in addition to those provided by Text: * * - shortcodes: If true, shortcodes will be turned into the appropriate HTML. @@ -66,10 +58,8 @@ public function setProcessShortcodes($process) * Text nodes outside of HTML tags are filtered out by default, but may be included by adding * the text() directive. E.g. 'link,meta,text()' will allow only <link /> <meta /> and text at * the root level. - * - * @return $this */ - public function setOptions(array $options = []) + public function setOptions(array $options = []): static { if (array_key_exists("shortcodes", $options ?? [])) { $this->setProcessShortcodes(!!$options["shortcodes"]); @@ -78,13 +68,13 @@ public function setOptions(array $options = []) return parent::setOptions($options); } - public function forTemplate() + public function forTemplate(): string { // Suppress XML encoding for DBHtmlText - return $this->RAW(); + return $this->RAW() ?? ''; } - public function RAW() + public function RAW(): ?string { if ($this->processShortcodes) { return ShortcodeParser::get_active()->parse($this->value); @@ -94,10 +84,8 @@ public function RAW() /** * Safely escape for XML string - * - * @return string */ - public function CDATA() + public function CDATA(): string { return sprintf( '<![CDATA[%s]]>', @@ -109,10 +97,8 @@ public function CDATA() * Get plain-text version. * * Note: unlike DBHTMLText, this doesn't respect line breaks / paragraphs - * - * @return string */ - public function Plain() + public function Plain(): string { // Strip out HTML $text = strip_tags($this->RAW() ?? ''); @@ -121,17 +107,17 @@ public function Plain() return trim(Convert::xml2raw($text) ?? ''); } - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { return HTMLEditorField::create($this->name, $title); } - public function scaffoldSearchField($title = null) + public function scaffoldSearchField(?string $title = null): ?FormField { return TextField::create($this->name, $title); } - public function getSchemaValue() + public function getSchemaValue(): ?array { // Form schema format as HTML $value = $this->RAW(); @@ -141,7 +127,7 @@ public function getSchemaValue() return null; } - public function exists() + public function exists(): bool { // Optimisation: don't process shortcode just for ->exists() $value = $this->getValue(); diff --git a/src/ORM/FieldType/DBInt.php b/src/ORM/FieldType/DBInt.php index 656a98ac393..4a90118757e 100644 --- a/src/ORM/FieldType/DBInt.php +++ b/src/ORM/FieldType/DBInt.php @@ -2,9 +2,11 @@ namespace SilverStripe\ORM\FieldType; +use SilverStripe\Forms\FormField; use SilverStripe\Forms\NumericField; use SilverStripe\ORM\ArrayList; use SilverStripe\ORM\DB; +use SilverStripe\ORM\SS_List; use SilverStripe\View\ArrayData; /** @@ -12,8 +14,7 @@ */ class DBInt extends DBField { - - public function __construct($name = null, $defaultVal = 0) + public function __construct(?string $name = null, int $defaultVal = 0) { $this->defaultVal = is_int($defaultVal) ? $defaultVal : 0; @@ -24,7 +25,7 @@ public function __construct($name = null, $defaultVal = 0) * Ensure int values are always returned. * This is for mis-configured databases that return strings. */ - public function getValue() + public function getValue(): ?int { return (int) $this->value; } @@ -32,12 +33,12 @@ public function getValue() /** * Returns the number, with commas added as appropriate, eg “1,000”. */ - public function Formatted() + public function Formatted(): string { return number_format($this->value ?? 0.0); } - public function requireField() + public function requireField(): void { $parts = [ 'datatype' => 'int', @@ -50,7 +51,7 @@ public function requireField() DB::require_field($this->tableName, $this->name, $values); } - public function Times() + public function Times(): SS_List { $output = new ArrayList(); for ($i = 0; $i < $this->value; $i++) { @@ -60,22 +61,22 @@ public function Times() return $output; } - public function Nice() + public function Nice(): string { return sprintf('%d', $this->value); } - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { return NumericField::create($this->name, $title); } - public function nullValue() + public function nullValue(): ?int { return 0; } - public function prepValueForDB($value) + public function prepValueForDB(mixed $value): ?int { if ($value === true) { return 1; diff --git a/src/ORM/FieldType/DBLocale.php b/src/ORM/FieldType/DBLocale.php index 9b5e7c61458..aae33bbeefe 100644 --- a/src/ORM/FieldType/DBLocale.php +++ b/src/ORM/FieldType/DBLocale.php @@ -9,8 +9,7 @@ */ class DBLocale extends DBVarchar { - - public function __construct($name = null, $size = 16) + public function __construct(?string $name = null, int $size = 16) { parent::__construct($name, $size); } @@ -18,11 +17,10 @@ public function __construct($name = null, $size = 16) /** * See {@link getShortName()} and {@link getNativeName()}. * - * @param Boolean $showNative Show a localized version of the name instead, based on the + * @param bool $showNative Show a localized version of the name instead, based on the * field's locale value. - * @return String */ - public function Nice($showNative = false) + public function Nice(bool $showNative = false): string { if ($showNative) { return $this->getNativeName(); @@ -30,7 +28,7 @@ public function Nice($showNative = false) return $this->getShortName(); } - public function RFC1766() + public function RFC1766(): string { return i18n::convert_rfc1766($this->value); } @@ -38,18 +36,13 @@ public function RFC1766() /** * Resolves the locale to a common english-language * name through {@link i18n::get_common_locales()}. - * - * @return string */ - public function getShortName() + public function getShortName(): string { return i18n::getData()->languageName($this->value); } - /** - * @return string - */ - public function getLongName() + public function getLongName(): string { return i18n::getData()->localeName($this->value); } @@ -57,10 +50,8 @@ public function getLongName() /** * Returns the localized name based on the field's value. * Example: "de_DE" returns "Deutsch". - * - * @return string */ - public function getNativeName() + public function getNativeName(): string { $locale = $this->value; return i18n::with_locale($locale, function () { diff --git a/src/ORM/FieldType/DBMoney.php b/src/ORM/FieldType/DBMoney.php index a9573bc5f1b..04aaa9dd0df 100644 --- a/src/ORM/FieldType/DBMoney.php +++ b/src/ORM/FieldType/DBMoney.php @@ -13,25 +13,17 @@ */ class DBMoney extends DBComposite { - /** - * @var string $locale - */ - protected $locale = null; + protected ?string $locale = null; - /** - * @var array<string,string> - */ - private static $composite_db = [ + private static array $composite_db = [ 'Currency' => 'Varchar(3)', 'Amount' => 'Decimal(19,4)' ]; /** * Get currency formatter - * - * @return NumberFormatter */ - public function getFormatter() + public function getFormatter(): NumberFormatter { $locale = $this->getLocale(); $currency = $this->getCurrency(); @@ -43,10 +35,8 @@ public function getFormatter() /** * Get nicely formatted currency (based on current locale) - * - * @return string */ - public function Nice() + public function Nice(): string { if (!$this->exists()) { return null; @@ -66,10 +56,8 @@ public function Nice() /** * Standard '0.00 CUR' format (non-localised) - * - * @return string */ - public function getValue() + public function getValue(): ?string { if (!$this->exists()) { return null; @@ -82,39 +70,23 @@ public function getValue() return $amount . ' ' . $currency; } - /** - * @return string - */ - public function getCurrency() + public function getCurrency(): ?string { return $this->getField('Currency'); } - /** - * @param string $currency - * @param bool $markChanged - * @return $this - */ - public function setCurrency($currency, $markChanged = true) + public function setCurrency(?string $currency, bool $markChanged = true): static { $this->setField('Currency', $currency, $markChanged); return $this; } - /** - * @return float - */ - public function getAmount() + public function getAmount(): ?float { return $this->getField('Amount'); } - /** - * @param mixed $amount - * @param bool $markChanged - * @return $this - */ - public function setAmount($amount, $markChanged = true) + public function setAmount(mixed $amount, bool $markChanged = true): static { // Retain nullability to mark this field as empty if (isset($amount)) { @@ -124,49 +96,35 @@ public function setAmount($amount, $markChanged = true) return $this; } - /** - * @return boolean - */ - public function exists() + public function exists(): bool { return is_numeric($this->getAmount()); } /** * Determine if this has a non-zero amount - * - * @return bool */ - public function hasAmount() + public function hasAmount(): bool { $a = $this->getAmount(); return (!empty($a) && is_numeric($a)); } - /** - * @param string $locale - * @return $this - */ - public function setLocale($locale) + public function setLocale(string $locale): static { $this->locale = $locale; return $this; } - /** - * @return string - */ - public function getLocale() + public function getLocale(): string { return $this->locale ?: i18n::get_locale(); } /** * Get currency symbol - * - * @return string */ - public function getSymbol() + public function getSymbol(): string { return $this->getFormatter()->getSymbol(NumberFormatter::CURRENCY_SYMBOL); } @@ -178,10 +136,8 @@ public function getSymbol() * Used by {@link SearchContext}, {@link ModelAdmin}, {@link DataObject::scaffoldFormFields()} * * @param string $title Optional. Localized title of the generated instance - * @param array $params - * @return FormField */ - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { return MoneyField::create($this->getName(), $title) ->setLocale($this->getLocale()); diff --git a/src/ORM/FieldType/DBMultiEnum.php b/src/ORM/FieldType/DBMultiEnum.php index 93cccab8e4e..1e55b5b51c7 100644 --- a/src/ORM/FieldType/DBMultiEnum.php +++ b/src/ORM/FieldType/DBMultiEnum.php @@ -4,6 +4,7 @@ use SilverStripe\Core\Config\Config; use SilverStripe\Forms\CheckboxSetField; +use SilverStripe\Forms\MultiSelectField; use SilverStripe\ORM\Connect\MySQLDatabase; use SilverStripe\ORM\DB; @@ -33,7 +34,7 @@ public function __construct($name = null, $enum = null, $default = null) } } - public function requireField() + public function requireField(): void { $charset = Config::inst()->get(MySQLDatabase::class, 'charset'); $collation = Config::inst()->get(MySQLDatabase::class, 'collation'); @@ -54,18 +55,15 @@ public function requireField() /** - * Return a {@link CheckboxSetField} suitable for editing this field - * - * @param string $title - * @param string $name - * @param bool $hasEmpty - * @param string $value - * @param string $emptyString - * @return CheckboxSetField + * Return a form field suitable for editing this field */ - public function formField($title = null, $name = null, $hasEmpty = false, $value = '', $emptyString = null) - { - + public function formField( + ?string $title = null, + ?string $name = null, + bool $hasEmpty = false, + ?string $value = '', + ?string $emptyString = null + ): MultiSelectField { if (!$title) { $title = $this->name; } @@ -73,6 +71,6 @@ public function formField($title = null, $name = null, $hasEmpty = false, $value $name = $this->name; } - return new CheckboxSetField($name, $title, $this->enumValues($hasEmpty), $value); + return CheckboxSetField::create($name, $title, $this->enumValues($hasEmpty), $value); } } diff --git a/src/ORM/FieldType/DBPercentage.php b/src/ORM/FieldType/DBPercentage.php index 1abf613a26a..cf703e8c140 100644 --- a/src/ORM/FieldType/DBPercentage.php +++ b/src/ORM/FieldType/DBPercentage.php @@ -2,6 +2,8 @@ namespace SilverStripe\ORM\FieldType; +use SilverStripe\View\ViewableData; + /** * Represents a decimal field from 0-1 containing a percentage value. * @@ -15,14 +17,10 @@ */ class DBPercentage extends DBDecimal { - /** * Create a new Decimal field. - * - * @param string $name - * @param int $precision */ - public function __construct($name = null, $precision = 4) + public function __construct(?string $name = null, int $precision = 4) { if (!$precision) { $precision = 4; @@ -34,18 +32,18 @@ public function __construct($name = null, $precision = 4) /** * Returns the number, expressed as a percentage. For example, “36.30%” */ - public function Nice() + public function Nice(): string { return number_format($this->value * 100, $this->decimalSize - 2) . '%'; } - public function saveInto($dataObject) + public function saveInto(ViewableData $model): void { - parent::saveInto($dataObject); + parent::saveInto($model); $fieldName = $this->name; - if ($fieldName && $dataObject->$fieldName > 1.0) { - $dataObject->__set($fieldName, 1.0); + if ($fieldName && $model->$fieldName > 1.0) { + $model->__set($fieldName, 1.0); } } } diff --git a/src/ORM/FieldType/DBPolymorphicForeignKey.php b/src/ORM/FieldType/DBPolymorphicForeignKey.php index a70be359cc7..8e5616635f3 100644 --- a/src/ORM/FieldType/DBPolymorphicForeignKey.php +++ b/src/ORM/FieldType/DBPolymorphicForeignKey.php @@ -2,21 +2,23 @@ namespace SilverStripe\ORM\FieldType; +use SilverStripe\Forms\FormField; use SilverStripe\ORM\DataObject; +use SilverStripe\View\ViewableData; /** * A special ForeignKey class that handles relations with arbitrary class types */ class DBPolymorphicForeignKey extends DBComposite { - private static $index = true; + private static bool $index = true; - private static $composite_db = [ + private static array $composite_db = [ 'ID' => 'Int', 'Class' => "DBClassName('" . DataObject::class . "', ['index' => false])" ]; - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { // Don't provide scaffolded form field generation - Scaffolding should be performed on // the has_many end, or set programmatically. @@ -28,7 +30,7 @@ public function scaffoldFormField($title = null, $params = null) * * @return string Name of a subclass of DataObject */ - public function getClassValue() + public function getClassValue(): ?string { return $this->getField('Class'); } @@ -37,35 +39,29 @@ public function getClassValue() * Set the value of the "Class" this key points to * * @param string $value Name of a subclass of DataObject - * @param boolean $markChanged Mark this field as changed? */ - public function setClassValue($value, $markChanged = true) + public function setClassValue(string $value, bool $markChanged = true) { $this->setField('Class', $value, $markChanged); } /** * Gets the value of the "ID" this key points to - * - * @return integer */ - public function getIDValue() + public function getIDValue(): ?int { return $this->getField('ID'); } /** * Sets the value of the "ID" this key points to - * - * @param integer $value - * @param boolean $markChanged Mark this field as changed? */ - public function setIDValue($value, $markChanged = true) + public function setIDValue(int $value, bool $markChanged = true) { $this->setField('ID', $value, $markChanged); } - public function setValue($value, $record = null, $markChanged = true) + public function setValue(mixed $value, null|array|ViewableData $record = null, bool $markChanged = true): static { // Map dataobject value to array if ($value instanceof DataObject) { @@ -75,10 +71,10 @@ public function setValue($value, $record = null, $markChanged = true) ]; } - parent::setValue($value, $record, $markChanged); + return parent::setValue($value, $record, $markChanged); } - public function getValue() + public function getValue(): ?DataObject { $id = $this->getIDValue(); $class = $this->getClassValue(); diff --git a/src/ORM/FieldType/DBPrimaryKey.php b/src/ORM/FieldType/DBPrimaryKey.php index 7e9a207e240..9a6f7023811 100644 --- a/src/ORM/FieldType/DBPrimaryKey.php +++ b/src/ORM/FieldType/DBPrimaryKey.php @@ -2,64 +2,59 @@ namespace SilverStripe\ORM\FieldType; +use SilverStripe\Forms\FormField; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DB; +use SilverStripe\View\ViewableData; /** * A special type Int field used for primary keys. */ class DBPrimaryKey extends DBInt { - /** - * @var DataObject - */ - protected $object; + protected ?DataObject $object; private static $default_search_filter_class = 'ExactMatchFilter'; + protected bool $autoIncrement = true; + /** - * @var bool + * @param DataObject $object The object that this is primary key for (should have a relation with $name) */ - protected $autoIncrement = true; + public function __construct(?string $name, ?DataObject $object = null) + { + $this->object = $object; + parent::__construct($name); + } - public function setAutoIncrement($autoIncrement) + public function setAutoIncrement(bool $autoIncrement): static { $this->autoIncrement = $autoIncrement; return $this; } - public function getAutoIncrement() + public function getAutoIncrement(): bool { return $this->autoIncrement; } - public function requireField() + public function requireField(): void { $spec = DB::get_schema()->IdColumn(false, $this->getAutoIncrement()); DB::require_field($this->getTable(), $this->getName(), $spec); } - /** - * @param string $name - * @param DataObject $object The object that this is primary key for (should have a relation with $name) - */ - public function __construct($name, $object = null) - { - $this->object = $object; - parent::__construct($name); - } - - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { return null; } - public function scaffoldSearchField($title = null) + public function scaffoldSearchField(?string $title = null): ?FormField { - parent::scaffoldFormField($title); + return parent::scaffoldFormField($title); } - public function setValue($value, $record = null, $markChanged = true) + public function setValue(mixed $value, null|array|ViewableData $record = null, bool $markChanged = true): static { parent::setValue($value, $record, $markChanged); diff --git a/src/ORM/FieldType/DBString.php b/src/ORM/FieldType/DBString.php index 875936d5650..aed498b6ec3 100644 --- a/src/ORM/FieldType/DBString.php +++ b/src/ORM/FieldType/DBString.php @@ -7,10 +7,7 @@ */ abstract class DBString extends DBField { - /** - * @var array - */ - private static $casting = [ + private static array $casting = [ 'LimitCharacters' => 'Text', 'LimitCharactersToClosestWord' => 'Text', 'LimitWordCount' => 'Text', @@ -33,16 +30,14 @@ public function __construct($name = null, $options = []) /** * Update the optional parameters for this field. * - * @param array $options Array of options * The options allowed are: * <ul><li>"nullifyEmpty" * This is a boolean flag. * True (the default) means that empty strings are automatically converted to nulls to be stored in * the database. Set it to false to ensure that nulls and empty strings are kept intact in the database. * </li></ul> - * @return $this */ - public function setOptions(array $options = []) + public function setOptions(array $options = []): static { parent::setOptions($options); @@ -63,9 +58,9 @@ public function setOptions(array $options = []) * @param $value boolean True if empty strings are to be converted to null * @return $this */ - public function setNullifyEmpty($value) + public function setNullifyEmpty(bool $value): static { - $this->options['nullifyEmpty'] = (bool) $value; + $this->options['nullifyEmpty'] = $value; return $this; } @@ -75,23 +70,19 @@ public function setNullifyEmpty($value) * * @return boolean True if empty strings are to be converted to null */ - public function getNullifyEmpty() + public function getNullifyEmpty(): bool { return !empty($this->options['nullifyEmpty']); } - /** - * (non-PHPdoc) - * @see DBField::exists() - */ - public function exists() + public function exists(): bool { $value = $this->RAW(); // All truthy values and non-empty strings exist ('0' but not (int)0) return $value || (is_string($value) && strlen($value ?? '')); } - public function prepValueForDB($value) + public function prepValueForDB(mixed $value): ?string { // Cast non-empty value if (is_scalar($value) && strlen($value ?? '')) { @@ -105,10 +96,7 @@ public function prepValueForDB($value) return ''; } - /** - * @return string - */ - public function forTemplate() + public function forTemplate(): string { return nl2br(parent::forTemplate() ?? ''); } @@ -120,9 +108,8 @@ public function forTemplate() * * @param int $limit Number of characters to limit by * @param string|false $add Ellipsis to add to the end of truncated string - * @return string */ - public function LimitCharacters($limit = 20, $add = false) + public function LimitCharacters(int $limit = 20, string|false $add = false): string { $value = $this->Plain(); if (mb_strlen($value ?? '') <= $limit) { @@ -140,7 +127,7 @@ public function LimitCharacters($limit = 20, $add = false) * @param string|false $add Ellipsis to add to the end of truncated string * @return string Plain text value with limited characters */ - public function LimitCharactersToClosestWord($limit = 20, $add = false) + public function LimitCharactersToClosestWord(int $limit = 20, string|false $add = false): string { // Safely convert to plain text $value = $this->Plain(); @@ -169,11 +156,9 @@ public function LimitCharactersToClosestWord($limit = 20, $add = false) * Limit this field's content by a number of words. * * @param int $numWords Number of words to limit by. - * @param false $add Ellipsis to add to the end of truncated string. - * - * @return string + * @param string|false $add Ellipsis to add to the end of truncated string. */ - public function LimitWordCount($numWords = 26, $add = false) + public function LimitWordCount(int $numWords = 26, string|false $add = false): string { $value = $this->Plain(); $words = explode(' ', $value ?? ''); @@ -191,7 +176,7 @@ public function LimitWordCount($numWords = 26, $add = false) * * @return string Text with lowercase (HTML for some subclasses) */ - public function LowerCase() + public function LowerCase(): string { return mb_strtolower($this->RAW() ?? ''); } @@ -201,28 +186,23 @@ public function LowerCase() * * @return string Text with uppercase (HTML for some subclasses) */ - public function UpperCase() + public function UpperCase(): string { return mb_strtoupper($this->RAW() ?? ''); } /** * Plain text version of this string - * - * @return string Plain text */ - public function Plain() + public function Plain(): string { return trim($this->RAW() ?? ''); } /** * Swap add for defaultEllipsis if need be - * @param string $string - * @param false|string $add - * @return string */ - private function addEllipsis(string $string, $add): string + private function addEllipsis(string $string, string|false $add): string { if ($add === false) { $add = $this->defaultEllipsis(); @@ -233,7 +213,6 @@ private function addEllipsis(string $string, $add): string /** * Get the default string to indicate that a string was cut off. - * @return string */ public function defaultEllipsis(): string { diff --git a/src/ORM/FieldType/DBText.php b/src/ORM/FieldType/DBText.php index f0025258525..97169cf1a32 100644 --- a/src/ORM/FieldType/DBText.php +++ b/src/ORM/FieldType/DBText.php @@ -5,6 +5,7 @@ use InvalidArgumentException; use SilverStripe\Core\Config\Config; use SilverStripe\Core\Convert; +use SilverStripe\Forms\FormField; use SilverStripe\Forms\NullableField; use SilverStripe\Forms\TextareaField; use SilverStripe\Forms\TextField; @@ -27,8 +28,7 @@ */ class DBText extends DBString { - - private static $casting = [ + private static array $casting = [ 'BigSummary' => 'Text', 'ContextSummary' => 'HTMLFragment', // Always returns HTML as it contains formatting and highlighting 'FirstParagraph' => 'Text', @@ -42,11 +42,7 @@ class DBText extends DBString */ private static array $summary_sentence_separators = ['.', '?', '!']; - /** - * (non-PHPdoc) - * @see DBField::requireField() - */ - public function requireField() + public function requireField(): void { $charset = Config::inst()->get(MySQLDatabase::class, 'charset'); $collation = Config::inst()->get(MySQLDatabase::class, 'collation'); @@ -71,9 +67,8 @@ public function requireField() * Limit sentences, can be controlled by passing an integer. * * @param int $maxSentences The amount of sentences you want. - * @return string */ - public function LimitSentences($maxSentences = 2) + public function LimitSentences(int $maxSentences = 2): string { if (!is_numeric($maxSentences)) { throw new InvalidArgumentException("Text::LimitSentence() expects one numeric argument"); @@ -107,22 +102,16 @@ public function LimitSentences($maxSentences = 2) /** * Return the first string that finishes with a period (.) in this text. - * - * @return string */ - public function FirstSentence() + public function FirstSentence(): string { return $this->LimitSentences(1); } /** * Builds a basic summary, up to a maximum number of words - * - * @param int $maxWords - * @param string|false $add - * @return string */ - public function Summary($maxWords = 50, $add = false) + public function Summary(int $maxWords = 50, string|false $add = false): string { // Get plain-text version $value = $this->Plain(); @@ -171,10 +160,8 @@ public function Summary($maxWords = 50, $add = false) /** * Get first paragraph - * - * @return string */ - public function FirstParagraph() + public function FirstParagraph(): string { $value = $this->Plain(); if (empty($value)) { @@ -193,17 +180,15 @@ public function FirstParagraph() * @param int $characters Number of characters in the summary * @param string $keywords Supplied string ("keywords"). Will fall back to 'Search' querystring arg. * @param bool $highlight Add a highlight <mark> element around search query? - * @param string|false $prefix Prefix text - * @param string|false $suffix Suffix text * @return string HTML string with context */ public function ContextSummary( - $characters = 500, - $keywords = null, - $highlight = true, - $prefix = false, - $suffix = false - ) { + int $characters = 500, + ?string $keywords = null, + bool $highlight = true, + string|false $prefix = false, + string|false $suffix = false + ): string { if (!$keywords) { // Use the default "Search" request variable (from SearchForm) @@ -267,7 +252,7 @@ public function ContextSummary( return nl2br($summary ?? ''); } - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { if (!$this->nullifyEmpty) { // Allow the user to select if it's null instead of automatically assuming empty string is @@ -277,7 +262,7 @@ public function scaffoldFormField($title = null, $params = null) return TextareaField::create($this->name, $title); } - public function scaffoldSearchField($title = null) + public function scaffoldSearchField(?string $title = null): ?FormField { return new TextField($this->name, $title); } diff --git a/src/ORM/FieldType/DBTime.php b/src/ORM/FieldType/DBTime.php index 6b1813eea5a..9c64359c349 100644 --- a/src/ORM/FieldType/DBTime.php +++ b/src/ORM/FieldType/DBTime.php @@ -4,11 +4,13 @@ use IntlDateFormatter; use InvalidArgumentException; +use SilverStripe\Forms\FormField; use SilverStripe\Forms\TimeField; use SilverStripe\i18n\i18n; use SilverStripe\ORM\DB; use SilverStripe\Security\Member; use SilverStripe\Security\Security; +use SilverStripe\View\ViewableData; /** * Represents a column in the database with the type 'Time'. @@ -25,9 +27,9 @@ class DBTime extends DBField /** * Standard ISO format string for time in CLDR standard format */ - const ISO_TIME = 'HH:mm:ss'; + public const ISO_TIME = 'HH:mm:ss'; - public function setValue($value, $record = null, $markChanged = true) + public function setValue(mixed $value, null|array|ViewableData $record = null, bool $markChanged = true): static { $value = $this->parseTime($value); if ($value === false) { @@ -42,10 +44,9 @@ public function setValue($value, $record = null, $markChanged = true) /** * Parse timestamp or iso8601-ish date into standard iso8601 format * - * @param mixed $value * @return string|null|false Formatted time, null if empty but valid, or false if invalid */ - protected function parseTime($value) + protected function parseTime(mixed $value): string|null|false { // Skip empty values if (empty($value) && !is_numeric($value)) { @@ -73,24 +74,19 @@ protected function parseTime($value) /** * Get date / time formatter for the current locale - * - * @param int $timeLength - * @return IntlDateFormatter */ - public function getFormatter($timeLength = IntlDateFormatter::MEDIUM) + public function getFormatter(int $timeLength = IntlDateFormatter::MEDIUM): IntlDateFormatter { return IntlDateFormatter::create(i18n::get_locale(), IntlDateFormatter::NONE, $timeLength); } /** * Returns the date in the localised short format - * - * @return string */ - public function Short() + public function Short(): string { if (!$this->value) { - return null; + return ''; } $formatter = $this->getFormatter(IntlDateFormatter::SHORT); return $formatter->format($this->getTimestamp()); @@ -99,13 +95,11 @@ public function Short() /** * Returns the standard localised medium time * e.g. "3:15pm" - * - * @return string */ - public function Nice() + public function Nice(): string { if (!$this->value) { - return null; + return ''; } $formatter = $this->getFormatter(); return $formatter->format($this->getTimestamp()); @@ -114,20 +108,19 @@ public function Nice() /** * Return the time using a particular formatting string. * - * @param string $format Format code string. See https://unicode-org.github.io/icu/userguide/format_parse/datetime - * @return string The time in the requested format + * See https://unicode-org.github.io/icu/userguide/format_parse/datetime for valid formats */ - public function Format($format) + public function Format(string $format): string { if (!$this->value) { - return null; + return ''; } $formatter = $this->getFormatter(); $formatter->setPattern($format); return $formatter->format($this->getTimestamp()); } - public function requireField() + public function requireField(): void { $parts = [ 'datatype' => 'time', @@ -140,18 +133,15 @@ public function requireField() DB::require_field($this->tableName, $this->name, $values); } - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { return TimeField::create($this->name, $title); } /** * Return a time formatted as per a CMS user's settings. - * - * @param Member $member - * @return string A time formatted as per user-defined settings. */ - public function FormatFromSettings($member = null) + public function FormatFromSettings(?Member $member = null): string { if (!$member) { $member = Security::getCurrentUser(); @@ -169,20 +159,16 @@ public function FormatFromSettings($member = null) /** * Get standard ISO time format string - * - * @return string */ - public function getISOFormat() + public function getISOFormat(): string { return DBTime::ISO_TIME; } /** * Get unix timestamp for this time - * - * @return int */ - public function getTimestamp() + public function getTimestamp(): int { if ($this->value) { return strtotime($this->value ?? ''); diff --git a/src/ORM/FieldType/DBVarchar.php b/src/ORM/FieldType/DBVarchar.php index d38f4d462c2..3081ad34be0 100644 --- a/src/ORM/FieldType/DBVarchar.php +++ b/src/ORM/FieldType/DBVarchar.php @@ -3,6 +3,7 @@ namespace SilverStripe\ORM\FieldType; use SilverStripe\Core\Config\Config; +use SilverStripe\Forms\FormField; use SilverStripe\Forms\NullableField; use SilverStripe\Forms\TextField; use SilverStripe\ORM\Connect\MySQLDatabase; @@ -17,18 +18,15 @@ */ class DBVarchar extends DBString { - - private static $casting = [ + private static array $casting = [ 'Initial' => 'Text', 'URL' => 'Text', ]; /** * Max size of this field - * - * @var int */ - protected $size; + protected int $size; /** * Construct a new short text field @@ -38,7 +36,7 @@ class DBVarchar extends DBString * @param array $options Optional parameters, e.g. array("nullifyEmpty"=>false). * See {@link StringField::setOptions()} for information on the available options */ - public function __construct($name = null, $size = 255, $options = []) + public function __construct(?string $name = null, int $size = 255, array $options = []) { $this->size = $size ? $size : 255; parent::__construct($name, $options); @@ -53,16 +51,12 @@ public function __construct($name = null, $size = 255, $options = []) * * @return int The size of the field */ - public function getSize() + public function getSize(): int { return $this->size; } - /** - * (non-PHPdoc) - * @see DBField::requireField() - */ - public function requireField() + public function requireField(): void { $charset = Config::inst()->get(MySQLDatabase::class, 'charset'); $collation = Config::inst()->get(MySQLDatabase::class, 'collation'); @@ -85,24 +79,20 @@ public function requireField() /** * Return the first letter of the string followed by a . - * - * @return string */ - public function Initial() + public function Initial(): string { if ($this->exists()) { $value = $this->RAW(); return $value[0] . '.'; } - return null; + return ''; } /** * Ensure that the given value is an absolute URL. - * - * @return string */ - public function URL() + public function URL(): string { $value = $this->RAW(); if (preg_match('#^[a-zA-Z]+://#', $value ?? '')) { @@ -113,14 +103,13 @@ public function URL() /** * Return the value of the field in rich text format - * @return string */ - public function RTF() + public function RTF(): string { return str_replace("\n", '\par ', $this->RAW() ?? ''); } - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { // Set field with appropriate size $field = TextField::create($this->name, $title); diff --git a/src/ORM/FieldType/DBYear.php b/src/ORM/FieldType/DBYear.php index 55a974d1990..04618cae339 100644 --- a/src/ORM/FieldType/DBYear.php +++ b/src/ORM/FieldType/DBYear.php @@ -3,6 +3,7 @@ namespace SilverStripe\ORM\FieldType; use SilverStripe\Forms\DropdownField; +use SilverStripe\Forms\FormField; use SilverStripe\ORM\DB; /** @@ -10,15 +11,14 @@ */ class DBYear extends DBField { - - public function requireField() + public function requireField(): void { $parts = ['datatype' => 'year', 'precision' => 4, 'arrayValue' => $this->arrayValue]; $values = ['type' => 'year', 'parts' => $parts]; DB::require_field($this->tableName, $this->name, $values); } - public function scaffoldFormField($title = null, $params = null) + public function scaffoldFormField(?string $title = null, array $params = []): ?FormField { $selectBox = DropdownField::create($this->name, $title); $selectBox->setSource($this->getDefaultOptions()); @@ -31,11 +31,10 @@ public function scaffoldFormField($title = null, $params = null) * input values. Starts by default at the current year, * and counts back to 1900. * - * @param int|bool $start starting date to count down from - * @param int|bool $end end date to count down to - * @return array + * @param int|null $start starting date to count down from + * @param int|null $end end date to count down to */ - private function getDefaultOptions($start = null, $end = null) + private function getDefaultOptions(?int $start = null, ?int $end = null): array { if (!$start) { $start = (int)date('Y'); diff --git a/src/ORM/ListDecorator.php b/src/ORM/ListDecorator.php index b063acb7481..956cfd0d1d1 100644 --- a/src/ORM/ListDecorator.php +++ b/src/ORM/ListDecorator.php @@ -112,7 +112,7 @@ public function getIterator(): Traversable return $this->list->getIterator(); } - public function exists() + public function exists(): bool { return $this->list->exists(); } @@ -140,7 +140,7 @@ public function Count(): int return $this->list->count(); } - public function forTemplate() + public function forTemplate(): string { return $this->list->forTemplate(); } @@ -313,7 +313,7 @@ public function exclude() return $this->list->exclude(...func_get_args()); } - public function debug() + public function debug(): string { return $this->list->debug(); } diff --git a/src/View/ArrayData.php b/src/View/ArrayData.php index 819ad8f75ac..c107fd060c8 100644 --- a/src/View/ArrayData.php +++ b/src/View/ArrayData.php @@ -70,13 +70,10 @@ public function toMap() * * If the value is an associative array, it will likewise be * converted recursively to an ArrayData. - * - * @param string $field - * @return mixed */ - public function getField($field) + public function getField(string $fieldName): mixed { - $value = $this->array[$field]; + $value = $this->array[$fieldName]; if (is_object($value) && !($value instanceof ViewableData) && !is_iterable($value)) { return new ArrayData($value); } elseif (ArrayLib::is_associative($value)) { @@ -87,14 +84,10 @@ public function getField($field) } /** * Add or set a field on this object. - * - * @param string $field - * @param mixed $value - * @return $this */ - public function setField($field, $value) + public function setField(string $fieldName, mixed $value): static { - $this->array[$field] = $value; + $this->array[$fieldName] = $value; return $this; } @@ -104,9 +97,9 @@ public function setField($field, $value) * @param string $field Field Key * @return bool */ - public function hasField($field) + public function hasField(string $fieldName): bool { - return isset($this->array[$field]); + return isset($this->array[$fieldName]); } /** diff --git a/src/View/Parsers/HTMLValue.php b/src/View/Parsers/HTMLValue.php index 76b5ebc17af..c8c0779b34e 100644 --- a/src/View/Parsers/HTMLValue.php +++ b/src/View/Parsers/HTMLValue.php @@ -46,10 +46,7 @@ public function setContent($content) return false; } - /** - * @return string - */ - public function getContent() + public function getContent(): string { $document = $this->getDocument(); if (!$document) { @@ -98,7 +95,7 @@ public function getContent() } /** @see HTMLValue::getContent() */ - public function forTemplate() + public function forTemplate(): string { return $this->getContent(); } diff --git a/src/View/Parsers/ShortcodeParser.php b/src/View/Parsers/ShortcodeParser.php index 2031a1645f8..bfade4d124d 100644 --- a/src/View/Parsers/ShortcodeParser.php +++ b/src/View/Parsers/ShortcodeParser.php @@ -733,6 +733,6 @@ function ($matches) use ($tags, $parser) { $this->extend('onAfterParse', $content); - return $content; + return $content ?? ''; } } diff --git a/src/View/SSTemplateParser.peg b/src/View/SSTemplateParser.peg index 0f15460f97b..a7068859f95 100644 --- a/src/View/SSTemplateParser.peg +++ b/src/View/SSTemplateParser.peg @@ -288,7 +288,7 @@ class SSTemplateParser extends Parser implements TemplateParser $arguments = $sub['Call']['CallArguments']['php']; $res['php'] .= "->$method('$property', [$arguments], true)"; } else { - $res['php'] .= "->$method('$property', null, true)"; + $res['php'] .= "->$method('$property', [], true)"; } } diff --git a/src/View/SSTemplateParser.php b/src/View/SSTemplateParser.php index bafe80e4be4..a19c91bbf78 100644 --- a/src/View/SSTemplateParser.php +++ b/src/View/SSTemplateParser.php @@ -779,7 +779,7 @@ function Lookup_AddLookupStep(&$res, $sub, $method) $arguments = $sub['Call']['CallArguments']['php']; $res['php'] .= "->$method('$property', [$arguments], true)"; } else { - $res['php'] .= "->$method('$property', null, true)"; + $res['php'] .= "->$method('$property', [], true)"; } } @@ -1886,6 +1886,8 @@ function PresenceCheck_Argument(&$res, $sub) $res['php'] .= '((bool)'.$sub['php'].')'; } else { $php = ($sub['ArgumentMode'] == 'default' ? $sub['lookup_php'] : $sub['php']); + // TODO: kinda hacky - maybe we need a way to pass state down the parse chain so + // Lookup_LastLookupStep and Argument_BareWord can produce hasValue instead of XML_val $res['php'] .= str_replace('$$FINAL', 'hasValue', $php ?? ''); } } @@ -5290,6 +5292,8 @@ function Text__finalise(&$res) $text = stripslashes($text ?? ''); $text = addcslashes($text ?? '', '\'\\'); + // TODO: This is pretty ugly & gets applied on all files not just html. I wonder if we can make this + // non-dynamically calculated $code = <<<'EOC' (\SilverStripe\View\SSViewer::getRewriteHashLinksDefault() ? \SilverStripe\Core\Convert::raw2att( preg_replace("/^(\\/)+/", "/", $_SERVER['REQUEST_URI'] ) ) @@ -5328,7 +5332,8 @@ public function compileString($string, $templateName = "", $includeDebuggingComm $this->includeDebuggingComments = $includeDebuggingComments; - // Ignore UTF8 BOM at beginning of string. + // Ignore UTF8 BOM at beginning of string. TODO: Confirm this is needed, make sure SSViewer handles UTF + // (and other encodings) properly if (substr($string ?? '', 0, 3) == pack("CCC", 0xef, 0xbb, 0xbf)) { $this->pos = 3; } diff --git a/src/View/ViewableData.php b/src/View/ViewableData.php index d2e7dbccd02..29831960738 100644 --- a/src/View/ViewableData.php +++ b/src/View/ViewableData.php @@ -2,29 +2,23 @@ namespace SilverStripe\View; -use ArrayIterator; use Exception; use InvalidArgumentException; -use IteratorAggregate; use LogicException; use ReflectionMethod; -use ReflectionObject; use ReflectionProperty; use SilverStripe\Core\ClassInfo; -use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Configurable; use SilverStripe\Core\Convert; 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\ArrayList; use SilverStripe\ORM\FieldType\DBField; use SilverStripe\ORM\FieldType\DBHTMLText; use SilverStripe\View\SSViewer; -use Traversable; use UnexpectedValueException; /** @@ -34,7 +28,7 @@ * is provided and automatically escaped by ViewableData. Any class that needs to be available to a view (controllers, * {@link DataObject}s, page controls) should inherit from this class. */ -class ViewableData implements IteratorAggregate +class ViewableData { use Extensible { defineMethods as extensibleDefineMethods; @@ -50,27 +44,18 @@ class ViewableData implements IteratorAggregate * 'FieldName' => 'ClassToCastTo(Arguments)' * ); * </code> - * - * @var array - * @config */ - private static $casting = [ + private static array $casting = [ 'CSSClasses' => 'Varchar' ]; /** * The default object to cast scalar fields to if casting information is not specified, and casting to an object * is required. - * - * @var string - * @config */ - private static $default_cast = 'Text'; + private static string $default_cast = 'Text'; - /** - * @var array - */ - private static $casting_cache = []; + private static array $casting_cache = []; /** * Acts as a PHP 8.2+ compliant replacement for dynamic properties @@ -81,20 +66,12 @@ class ViewableData implements IteratorAggregate /** * A failover object to attempt to get data from if it is not present on this object. - * - * @var ViewableData */ - protected $failover; + protected ?ViewableData $failover = null; - /** - * @var ViewableData - */ - protected $customisedObject; + protected ?ViewableData $customisedObject = null; - /** - * @var array - */ - private $objCache = []; + private array $objCache = []; public function __construct() { @@ -108,11 +85,8 @@ public function __construct() * Check if a field exists on this object or its failover. * Note that, unlike the core isset() implementation, this will return true if the property is defined * and set to null. - * - * @param string $property - * @return bool */ - public function __isset($property) + public function __isset(string $property): bool { // getField() isn't a field-specific getter and shouldn't be treated as such if (strtolower($property ?? '') !== 'field' && $this->hasMethod("get$property")) { @@ -131,11 +105,8 @@ public function __isset($property) /** * Get the value of a property/field on this object. This will check if a method called get{$property} exists, then * check if a field is available using {@link ViewableData::getField()}, then fall back on a failover object. - * - * @param string $property - * @return mixed */ - public function __get($property) + public function __get(string $property): mixed { // getField() isn't a field-specific getter and shouldn't be treated as such $method = "get$property"; @@ -155,11 +126,8 @@ public function __get($property) /** * Set a property/field on this object. This will check for the existence of a method called set{$property}, then * use the {@link ViewableData::setField()} method. - * - * @param string $property - * @param mixed $value */ - public function __set($property, $value) + public function __set(string $property, mixed $value): void { $this->objCacheClear(); $method = "set$property"; @@ -173,10 +141,8 @@ public function __set($property, $value) /** * Set a failover object to attempt to get data from if it is not present on this object. - * - * @param ViewableData $failover */ - public function setFailover(ViewableData $failover) + public function setFailover(ViewableData $failover): void { // Ensure cached methods from previous failover are removed if ($this->failover) { @@ -189,56 +155,44 @@ public function setFailover(ViewableData $failover) /** * Get the current failover object if set - * - * @return ViewableData|null */ - public function getFailover() + public function getFailover(): ?ViewableData { return $this->failover; } /** * Check if a field exists on this object. This should be overloaded in child classes. - * - * @param string $field - * @return bool */ - public function hasField($field) + public function hasField(string $fieldName): bool { - return property_exists($this, $field) || $this->hasDynamicData($field); + return property_exists($this, $fieldName) || $this->hasDynamicData($fieldName); } /** * Get the value of a field on this object. This should be overloaded in child classes. - * - * @param string $field - * @return mixed */ - public function getField($field) + public function getField(string $fieldName): mixed { - if ($this->isAccessibleProperty($field)) { - return $this->$field; + if ($this->isAccessibleProperty($fieldName)) { + return $this->$fieldName; } - return $this->getDynamicData($field); + return $this->getDynamicData($fieldName); } /** * Set a field on this object. This should be overloaded in child classes. - * - * @param string $field - * @param mixed $value - * @return $this */ - public function setField($field, $value) + public function setField(string $fieldName, mixed $value): static { $this->objCacheClear(); // prior to PHP 8.2 support ViewableData::setField() simply used `$this->field = $value;` // so the following logic essentially mimics this behaviour, though without the use // of now deprecated dynamic properties - if ($this->isAccessibleProperty($field)) { - $this->$field = $value; + if ($this->isAccessibleProperty($fieldName)) { + $this->$fieldName = $value; } - return $this->setDynamicData($field, $value); + return $this->setDynamicData($fieldName, $value); } public function getDynamicData(string $field): mixed @@ -322,11 +276,8 @@ public function defineMethods() * with references to both this and the new custom data. * * Note that any fields you specify will take precedence over the fields on this object. - * - * @param array|ViewableData $data - * @return ViewableData_Customised */ - public function customise($data) + public function customise(array|ViewableData $data): ViewableData { if (is_array($data) && (empty($data) || ArrayLib::is_associative($data))) { $data = new ArrayData($data); @@ -346,33 +297,25 @@ public function customise($data) * * This method should be overridden in subclasses to provide more context about the classes state. For example, a * {@link DataObject} class could return false when it is deleted from the database - * - * @return bool */ - public function exists() + public function exists(): bool { return true; } /** - * @return string the class name + * Return the class name (though subclasses may return something else) */ - public function __toString() + public function __toString(): string { return static::class; } - /** - * @return ViewableData - */ - public function getCustomisedObj() + public function getCustomisedObj(): ?ViewableData { return $this->customisedObject; } - /** - * @param ViewableData $object - */ public function setCustomisedObj(ViewableData $object) { $this->customisedObject = $object; @@ -384,12 +327,11 @@ public function setCustomisedObj(ViewableData $object) * Return the "casting helper" (a piece of PHP code that when evaluated creates a casted value object) * for a field on this object. This helper will be a subclass of DBField. * - * @param string $field * @param bool $useFallback If true, fall back on the default casting helper if there isn't an explicit one. * @return string|null Casting helper As a constructor pattern, and may include arguments. * @throws Exception */ - public function castingHelper($field, bool $useFallback = true) + public function castingHelper(string $field, bool $useFallback = true): ?string { // Get casting if it has been configured. // DB fields and PHP methods are all case insensitive so we normalise casing before checking. @@ -441,11 +383,8 @@ protected function defaultCastingHelper(string $field): string /** * Get the class name a field on this object will be casted to. - * - * @param string $field - * @return string */ - public function castingClass($field) + public function castingClass(string $field): string { // Strip arguments $spec = $this->castingHelper($field); @@ -455,10 +394,9 @@ public function castingClass($field) /** * Return the string-format type for the given field. * - * @param string $field * @return string 'xml'|'raw' */ - public function escapeTypeForField($field) + public function escapeTypeForField(string $field): string { $class = $this->castingClass($field) ?: $this->config()->get('default_cast'); @@ -477,10 +415,9 @@ public function escapeTypeForField($field) * - an SSViewer instance * * @param string|array|SSViewer $template the template to render into - * @param array $customFields fields to customise() the object with before rendering - * @return DBHTMLText + * @param ViewableData|array|null $customFields fields to customise() the object with before rendering */ - public function renderWith($template, $customFields = null) + public function renderWith($template, ViewableData|array|null $customFields = null): DBHTMLText { if (!is_object($template)) { $template = SSViewer::create($template); @@ -556,14 +493,14 @@ protected function objCacheClear() * Get the value of a field on this object, automatically inserting the value into any available casting objects * that have been specified. * - * @param string $fieldName - * @param array $arguments - * @param bool $cache Cache this object - * @param string $cacheName a custom cache name * @return object|DBField */ - public function obj($fieldName, $arguments = [], $cache = false, $cacheName = null) - { + public function obj( + string $fieldName, + array $arguments = [], + bool $cache = false, + ?string $cacheName = null + ): object { if (!$cacheName && $cache) { $cacheName = $this->objCacheName($fieldName, $arguments); } @@ -617,26 +554,18 @@ public function obj($fieldName, $arguments = [], $cache = false, $cacheName = nu * A simple wrapper around {@link ViewableData::obj()} that automatically caches the result so it can be used again * without re-running the method. * - * @param string $fieldName - * @param array $arguments - * @param string $identifier an optional custom cache identifier * @return Object|DBField */ - public function cachedCall($fieldName, $arguments = [], $identifier = null) + public function cachedCall(string $fieldName, array $arguments = [], ?string $cacheName = null): object { - return $this->obj($fieldName, $arguments, true, $identifier); + return $this->obj($fieldName, $arguments, true, $cacheName); } /** * Checks if a given method/field has a valid value. If the result is an object, this will return the result of the * exists method, otherwise will check if the result is not just an empty paragraph tag. - * - * @param string $field - * @param array $arguments - * @param bool $cache - * @return bool */ - public function hasValue($field, $arguments = [], $cache = true) + public function hasValue(string $field, array $arguments = [], bool $cache = true): bool { $result = $this->obj($field, $arguments, $cache); if ($result instanceof ViewableData) { @@ -648,13 +577,8 @@ public function hasValue($field, $arguments = [], $cache = true) /** * Get the string value of a field on this object that has been suitable escaped to be inserted directly into a * template. - * - * @param string $field - * @param array $arguments - * @param bool $cache - * @return string */ - public function XML_val($field, $arguments = [], $cache = false) + public function XML_val(string $field, array $arguments = [], bool $cache = false): string { $result = $this->obj($field, $arguments, $cache); // Might contain additional formatting over ->XML(). E.g. parse shortcodes, nl2br() @@ -665,9 +589,8 @@ public function XML_val($field, $arguments = [], $cache = false) * Get an array of XML-escaped values by field name * * @param array $fields an array of field names - * @return array */ - public function getXMLValues($fields) + public function getXMLValues(array $fields): array { $result = []; @@ -678,33 +601,12 @@ public function getXMLValues($fields) return $result; } - // ITERATOR SUPPORT ------------------------------------------------------------------------------------------------ - - /** - * Return a single-item iterator so you can iterate over the fields of a single record. - * - * 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]); - } - // UTILITY METHODS ------------------------------------------------------------------------------------------------- /** * Find appropriate templates for SSViewer to use to render this object - * - * @param string $suffix - * @return array */ - public function getViewerTemplates($suffix = '') + public function getViewerTemplates(string $suffix = ''): array { return SSViewer::get_templates_by_class(static::class, $suffix, ViewableData::class); } @@ -725,10 +627,9 @@ public function Me(): static * stop point - e.g. "Page DataObject ViewableData". * * @param string $stopAtClass the class to stop at (default: ViewableData) - * @return string * @uses ClassInfo */ - public function CSSClasses($stopAtClass = ViewableData::class) + public function CSSClasses(string $stopAtClass = ViewableData::class): string { $classes = []; $classAncestry = array_reverse(ClassInfo::ancestry(static::class) ?? []); @@ -754,11 +655,9 @@ public function CSSClasses($stopAtClass = ViewableData::class) /** * Return debug information about this object that can be rendered into a template - * - * @return ViewableData_Debugger */ - public function Debug() + public function Debug(): ViewableData|string { - return new ViewableData_Debugger($this); + return ViewableData_Debugger::create($this); } } diff --git a/src/View/ViewableData_Customised.php b/src/View/ViewableData_Customised.php index a8589bb51a8..90768c6e4a6 100644 --- a/src/View/ViewableData_Customised.php +++ b/src/View/ViewableData_Customised.php @@ -4,17 +4,12 @@ class ViewableData_Customised extends ViewableData { + protected ViewableData $original; - /** - * @var ViewableData - */ - protected $original, $customised; + protected ViewableData $customised; /** * Instantiate a new customised ViewableData object - * - * @param ViewableData $originalObject - * @param ViewableData $customisedObject */ public function __construct(ViewableData $originalObject, ViewableData $customisedObject) { @@ -35,7 +30,7 @@ public function __call($method, $arguments) return call_user_func_array([$this->original, $method], $arguments ?? []); } - public function __get($property) + public function __get(string $property): mixed { if (isset($this->customised->$property)) { return $this->customised->$property; @@ -44,12 +39,12 @@ public function __get($property) return $this->original->$property; } - public function __set($property, $value) + public function __set(string $property, mixed $value): void { $this->customised->$property = $this->original->$property = $value; } - public function __isset($property) + public function __isset(string $property): bool { return isset($this->customised->$property) || isset($this->original->$property) || parent::__isset($property); } @@ -59,16 +54,20 @@ public function hasMethod($method) return $this->customised->hasMethod($method) || $this->original->hasMethod($method); } - public function cachedCall($fieldName, $arguments = null, $identifier = null) + public function cachedCall(string $fieldName, array $arguments = [], ?string $cacheName = null): object { if ($this->customisedHas($fieldName)) { - return $this->customised->cachedCall($fieldName, $arguments, $identifier); + return $this->customised->cachedCall($fieldName, $arguments, $cacheName); } - return $this->original->cachedCall($fieldName, $arguments, $identifier); + return $this->original->cachedCall($fieldName, $arguments, $cacheName); } - public function obj($fieldName, $arguments = null, $cache = false, $cacheName = null) - { + public function obj( + string $fieldName, + array $arguments = [], + bool $cache = false, + ?string $cacheName = null + ): object { if ($this->customisedHas($fieldName)) { return $this->customised->obj($fieldName, $arguments, $cache, $cacheName); } diff --git a/src/View/ViewableData_Debugger.php b/src/View/ViewableData_Debugger.php index 9c496c99a4f..2c496610435 100644 --- a/src/View/ViewableData_Debugger.php +++ b/src/View/ViewableData_Debugger.php @@ -9,15 +9,8 @@ */ class ViewableData_Debugger extends ViewableData { + protected ViewableData $object; - /** - * @var ViewableData - */ - protected $object; - - /** - * @param ViewableData $object - */ public function __construct(ViewableData $object) { $this->object = $object; @@ -25,9 +18,9 @@ public function __construct(ViewableData $object) } /** - * @return string The rendered debugger + * Returns the rendered debugger */ - public function __toString() + public function __toString(): string { return (string)$this->forTemplate(); } @@ -35,11 +28,8 @@ public function __toString() /** * Return debugging information, as XHTML. If a field name is passed, it will show debugging information on that * field, otherwise it will show information on all methods and fields. - * - * @param string $field the field name - * @return string */ - public function forTemplate($field = null) + public function forTemplate(?string $field = null): string { // debugging info for a specific field $class = get_class($this->object); diff --git a/tests/php/Core/ClassInfoTest.php b/tests/php/Core/ClassInfoTest.php index 7528c3f3661..140aa78aa80 100644 --- a/tests/php/Core/ClassInfoTest.php +++ b/tests/php/Core/ClassInfoTest.php @@ -152,7 +152,7 @@ public function testAncestry() { $ancestry = ClassInfo::ancestry(ChildClass::class); $expect = [ - 'silverstripe\\view\\viewabledata' => ViewableData::class, + 'silverstripe\\view\\viewableData' => ViewableData::class, 'silverstripe\\orm\\dataobject' => DataObject::class, 'silverstripe\\core\tests\classinfotest\\baseclass' => BaseClass::class, 'silverstripe\\core\tests\classinfotest\\childclass' => ChildClass::class, diff --git a/tests/php/Dev/ViewableDataContainsTest/TestObject.php b/tests/php/Dev/ViewableDataContainsTest/TestObject.php index 0db24f3e400..658cae21040 100644 --- a/tests/php/Dev/ViewableDataContainsTest/TestObject.php +++ b/tests/php/Dev/ViewableDataContainsTest/TestObject.php @@ -14,14 +14,14 @@ public function __construct($data) $this->data = $data; } - public function hasField($name) + public function hasField(string $fieldName): bool { - return isset($this->data[$name]); + return isset($this->data[$fieldName]); } - public function getField($name) + public function getField(string $fieldName): mixed { - return isset($this->data[$name]) ?: null; + return isset($this->data[$fieldName]) ?: null; } public function getSomething() diff --git a/tests/php/ORM/DBFieldTest/TestDataObject.php b/tests/php/ORM/DBFieldTest/TestDataObject.php index 4d2efd37454..040571abf5d 100644 --- a/tests/php/ORM/DBFieldTest/TestDataObject.php +++ b/tests/php/ORM/DBFieldTest/TestDataObject.php @@ -16,10 +16,10 @@ class TestDataObject extends DataObject implements TestOnly public $setFieldCalledCount = 0; - public function setField($fieldName, $val) + public function setField(string $fieldName, mixed $value): static { $this->setFieldCalledCount++; - return parent::setField($fieldName, $val); + return parent::setField($fieldName, $value); } public function setMyTestField($val) diff --git a/tests/php/ORM/DBFieldTest/TestDbField.php b/tests/php/ORM/DBFieldTest/TestDbField.php index deb9773c419..264b3b6c8c4 100644 --- a/tests/php/ORM/DBFieldTest/TestDbField.php +++ b/tests/php/ORM/DBFieldTest/TestDbField.php @@ -9,7 +9,7 @@ class TestDbField extends DBField implements TestOnly { - public function requireField() + public function requireField(): void { // Basically the same as DBVarchar but we don't want to test with DBVarchar in case something // changes in that class eventually. diff --git a/tests/php/ORM/DBStringTest/MyStringField.php b/tests/php/ORM/DBStringTest/MyStringField.php index 9c9afe2376a..c4c8ade800f 100644 --- a/tests/php/ORM/DBStringTest/MyStringField.php +++ b/tests/php/ORM/DBStringTest/MyStringField.php @@ -7,7 +7,7 @@ class MyStringField extends DBString implements TestOnly { - public function requireField() + public function requireField(): void { } } diff --git a/tests/php/ORM/DataObjectTest/MockDynamicAssignmentDBField.php b/tests/php/ORM/DataObjectTest/MockDynamicAssignmentDBField.php index c2a297fb0c3..6b9d04bfa3b 100644 --- a/tests/php/ORM/DataObjectTest/MockDynamicAssignmentDBField.php +++ b/tests/php/ORM/DataObjectTest/MockDynamicAssignmentDBField.php @@ -34,10 +34,8 @@ public function __construct($name = '', $scalarOnly = false, $dynamicAssignment /** * If the field value and $dynamicAssignment are true, we'll try to do a dynamic assignment. - * @param $value - * @return array|int */ - public function prepValueForDB($value) + public function prepValueForDB(mixed $value): array|int|null { if ($value) { return $this->dynamicAssignment @@ -48,7 +46,7 @@ public function prepValueForDB($value) return 0; } - public function scalarValueOnly() + public function scalarValueOnly(): bool { return $this->scalarOnly; } diff --git a/tests/php/View/ArrayDataTest.php b/tests/php/View/ArrayDataTest.php index 49d93309e43..4ac60b1306a 100644 --- a/tests/php/View/ArrayDataTest.php +++ b/tests/php/View/ArrayDataTest.php @@ -11,7 +11,7 @@ class ArrayDataTest extends SapphireTest { - public function testViewabledataItemsInsideArraydataArePreserved() + public function testViewableDataItemsInsideArraydataArePreserved() { /* ViewableData objects will be preserved, but other objects will be converted */ $arrayData = new ArrayData( diff --git a/tests/php/View/SSViewerTest/TestFixture.php b/tests/php/View/SSViewerTest/TestFixture.php index 7f1da4b265c..c223c5cdd6d 100644 --- a/tests/php/View/SSViewerTest/TestFixture.php +++ b/tests/php/View/SSViewerTest/TestFixture.php @@ -18,7 +18,6 @@ public function __construct($name = null) parent::__construct(); } - private function argedName($fieldName, $arguments) { $childName = $this->name ? "$this->name.$fieldName" : $fieldName; @@ -29,8 +28,12 @@ private function argedName($fieldName, $arguments) } } - public function obj($fieldName, $arguments = null, $cache = false, $cacheName = null) - { + public function obj( + string $fieldName, + array $arguments = [], + bool $cache = false, + ?string $cacheName = null + ): object { $childName = $this->argedName($fieldName, $arguments); // Special field name Loop### to create a list @@ -49,8 +52,7 @@ public function obj($fieldName, $arguments = null, $cache = false, $cacheName = } } - - public function XML_val($fieldName, $arguments = null, $cache = false) + public function XML_val(string $fieldName, array $arguments = [], bool $cache = false): string { if (preg_match('/NotSet/i', $fieldName ?? '')) { return ''; @@ -63,7 +65,7 @@ public function XML_val($fieldName, $arguments = null, $cache = false) } } - public function hasValue($fieldName, $arguments = null, $cache = true) + public function hasValue(string $fieldName, array $arguments = [], bool $cache = true): bool { return (bool)$this->XML_val($fieldName, $arguments); } diff --git a/tests/php/View/ViewableDataTest/Castable.php b/tests/php/View/ViewableDataTest/Castable.php index e76966f18aa..97c23682372 100644 --- a/tests/php/View/ViewableDataTest/Castable.php +++ b/tests/php/View/ViewableDataTest/Castable.php @@ -50,7 +50,7 @@ public function castedUnsafeXML() return $this->unsafeXML(); } - public function forTemplate() + public function forTemplate(): string { return 'castable'; } diff --git a/tests/php/View/ViewableDataTest/Caster.php b/tests/php/View/ViewableDataTest/Caster.php index 6c68465a516..8b9042e7f65 100644 --- a/tests/php/View/ViewableDataTest/Caster.php +++ b/tests/php/View/ViewableDataTest/Caster.php @@ -8,7 +8,7 @@ class Caster extends ViewableData implements TestOnly { - public function forTemplate() + public function forTemplate(): string { return 'casted'; } diff --git a/tests/php/View/ViewableDataTest/RequiresCasting.php b/tests/php/View/ViewableDataTest/RequiresCasting.php index f66d832a9f9..155acf61cff 100644 --- a/tests/php/View/ViewableDataTest/RequiresCasting.php +++ b/tests/php/View/ViewableDataTest/RequiresCasting.php @@ -10,7 +10,7 @@ class RequiresCasting extends ViewableData implements TestOnly public $test = 'overwritten'; - public function forTemplate() + public function forTemplate(): string { return 'casted'; } diff --git a/tests/php/View/ViewableDataTest/UnescapedCaster.php b/tests/php/View/ViewableDataTest/UnescapedCaster.php index 688cb3077ff..d1eb0735e08 100644 --- a/tests/php/View/ViewableDataTest/UnescapedCaster.php +++ b/tests/php/View/ViewableDataTest/UnescapedCaster.php @@ -15,7 +15,7 @@ public function setValue($value) $this->value = $value; } - public function forTemplate() + public function forTemplate(): string { return Convert::raw2xml($this->value); }