From ea93316d9c85c4dc52dd121fa6d7793e10cabe4e Mon Sep 17 00:00:00 2001 From: Guy Sartorelli <36352093+GuySartorelli@users.noreply.github.com> Date: Wed, 28 Aug 2024 10:54:31 +1200 Subject: [PATCH] API Strong typing for the view layer (#11351) --- 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 +- .../GridFieldDetailForm_ItemRequest.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 | 4 +- src/View/SSTemplateParser.php | 11 +- src/View/SSViewer_FromString.php | 5 +- src/View/ViewableData.php | 214 ++++++------------ src/View/ViewableData_Customised.php | 29 ++- src/View/ViewableData_Debugger.php | 18 +- .../ViewableDataContainsTest/TestObject.php | 8 +- tests/php/ORM/DBClassNameTest.php | 4 +- .../php/ORM/DBCompositeTest/DBDoubleMoney.php | 2 +- tests/php/ORM/DBFieldTest/TestDataObject.php | 4 +- tests/php/ORM/DBFieldTest/TestDbField.php | 7 +- tests/php/ORM/DBStringTest/MyStringField.php | 2 +- tests/php/ORM/DataObjectTest.php | 22 +- .../MockDynamicAssignmentDBField.php | 6 +- tests/php/ORM/DecimalTest.php | 8 +- tests/php/ORM/DecimalTest/TestObject.php | 1 - tests/php/View/ArrayDataTest.php | 2 +- tests/php/View/SSViewerTest.php | 17 +- tests/php/View/SSViewerTest/TestFixture.php | 14 +- tests/php/View/ViewableDataTest.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 +- 70 files changed, 685 insertions(+), 1248 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)
* 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 .* 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 <<
@@ -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..824ff9f0b84 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): array|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..fb82d2f95e8 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 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(
'',
@@ -162,7 +142,7 @@ public function CDATA()
);
}
- public function prepValueForDB($value)
+ public function prepValueForDB(mixed $value): array|string|null
{
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(mixed $value): mixed
{
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('/\
/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 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(
'',
@@ -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..826e0724afb 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): array|int|null
{
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
- */
- 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..99d597aa9c5 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:
* - "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.
*
- * @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): array|string|null
{
// 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 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..628c4150bde 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)";
}
}
@@ -1037,7 +1037,7 @@ class SSTemplateParser extends Parser implements TemplateParser
//loop without arguments loops on the current scope
if ($res['ArgumentCount'] == 0) {
- $on = '$scope->locally()->obj(\'Me\', null, true)';
+ $on = '$scope->locally()->obj(\'Me\', [], true)';
} else { //loop in the normal way
$arg = $res['Arguments'][0];
if ($arg['ArgumentMode'] == 'string') {
diff --git a/src/View/SSTemplateParser.php b/src/View/SSTemplateParser.php
index bafe80e4be4..d9e1e15323b 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 ?? '');
}
}
@@ -4263,7 +4265,7 @@ function ClosedBlock_Handle_Loop(&$res)
//loop without arguments loops on the current scope
if ($res['ArgumentCount'] == 0) {
- $on = '$scope->locally()->obj(\'Me\', null, true)';
+ $on = '$scope->locally()->obj(\'Me\', [], true)';
} else { //loop in the normal way
$arg = $res['Arguments'][0];
if ($arg['ArgumentMode'] == 'string') {
@@ -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/SSViewer_FromString.php b/src/View/SSViewer_FromString.php
index 40dfed6a0b7..c7712a9f2b3 100644
--- a/src/View/SSViewer_FromString.php
+++ b/src/View/SSViewer_FromString.php
@@ -3,6 +3,7 @@
namespace SilverStripe\View;
use SilverStripe\Core\Config\Config;
+use SilverStripe\ORM\FieldType\DBField;
/**
* Special SSViewer that will process a template passed as a string, rather than a filename.
@@ -71,7 +72,9 @@ public function process($item, $arguments = null, $scope = null)
unlink($cacheFile ?? '');
}
- return $val;
+ $html = DBField::create_field('HTMLFragment', $val);
+
+ return $html;
}
/**
diff --git a/src/View/ViewableData.php b/src/View/ViewableData.php
index d2e7dbccd02..579f827099e 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)'
* );
*
- *
- * @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,18 @@ 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)
- {
+ * @return object|DBField|null The specific object representing the field, or null if there is no
+ * property, method, or dynamic data available for that field.
+ * Note that if there is a property or method that returns null, a relevant DBField instance will
+ * be returned.
+ */
+ public function obj(
+ string $fieldName,
+ array $arguments = [],
+ bool $cache = false,
+ ?string $cacheName = null
+ ): ?object {
+ $hasObj = false;
if (!$cacheName && $cache) {
$cacheName = $this->objCacheName($fieldName, $arguments);
}
@@ -576,11 +517,19 @@ public function obj($fieldName, $arguments = [], $cache = false, $cacheName = nu
// Load value from record
if ($this->hasMethod($fieldName)) {
+ $hasObj = true;
$value = call_user_func_array([$this, $fieldName], $arguments ?: []);
} else {
+ $hasObj = $this->hasField($fieldName) || ($this->hasMethod("get{$fieldName}") && $this->isAccessibleMethod("get{$fieldName}"));
$value = $this->$fieldName;
}
+ // Return null early if there's no backing for this field
+ // i.e. no poperty, no method, etc - it just doesn't exist on this model.
+ if (!$hasObj && $value === null) {
+ return null;
+ }
+
// Try to cast object if we have an explicit cast set
if (!is_object($value)) {
$castingHelper = $this->castingHelper($fieldName, false);
@@ -617,26 +566,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,15 +589,13 @@ 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);
+ if (!$result) {
+ return '';
+ }
// Might contain additional formatting over ->XML(). E.g. parse shortcodes, nl2br()
return $result->forTemplate();
}
@@ -665,9 +604,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 +616,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 +642,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 +670,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..02b4dbe0041 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/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/DBClassNameTest.php b/tests/php/ORM/DBClassNameTest.php
index 4fc2f78251d..72e5524cd25 100644
--- a/tests/php/ORM/DBClassNameTest.php
+++ b/tests/php/ORM/DBClassNameTest.php
@@ -129,9 +129,9 @@ public function testShortName()
$this->assertEquals('stdClass', $test3->getShortName());
$test4 = DBField::create_field('DBClassName', null);
- $this->assertNull($test4->getShortName());
+ $this->assertSame('', $test4->getShortName());
$test5 = DBField::create_field('DBClassName', 'not a class');
- $this->assertNull($test5->getShortName());
+ $this->assertSame('', $test5->getShortName());
}
}
diff --git a/tests/php/ORM/DBCompositeTest/DBDoubleMoney.php b/tests/php/ORM/DBCompositeTest/DBDoubleMoney.php
index 1952e3cec9f..7348b6b6438 100644
--- a/tests/php/ORM/DBCompositeTest/DBDoubleMoney.php
+++ b/tests/php/ORM/DBCompositeTest/DBDoubleMoney.php
@@ -7,7 +7,7 @@
class DBDoubleMoney extends DBMoney implements TestOnly
{
- public function writeToManipulation(&$manipulation)
+ public function writeToManipulation(array &$manipulation): void
{
// Duplicate the amount before writing
$this->setAmount($this->getAmount() * 2);
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..c66906d03f7 100644
--- a/tests/php/ORM/DBFieldTest/TestDbField.php
+++ b/tests/php/ORM/DBFieldTest/TestDbField.php
@@ -6,10 +6,11 @@
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DB;
use SilverStripe\ORM\FieldType\DBField;
+use SilverStripe\View\ViewableData;
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.
@@ -34,9 +35,9 @@ public function requireField()
public $saveIntoCalledCount = 0;
- public function saveInto($dataObject)
+ public function saveInto(ViewableData $model): void
{
$this->saveIntoCalledCount++;
- return parent::saveInto($dataObject);
+ parent::saveInto($model);
}
}
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.php b/tests/php/ORM/DataObjectTest.php
index b944ad34da6..5bb51505dcb 100644
--- a/tests/php/ORM/DataObjectTest.php
+++ b/tests/php/ORM/DataObjectTest.php
@@ -739,15 +739,15 @@ public function testHasOneRelationship()
$fan1 = $this->objFromFixture(DataObjectTest\Fan::class, 'fan1');
// Test relation probing
- $this->assertFalse((bool)$team1->hasValue('Captain', null, false));
- $this->assertFalse((bool)$team1->hasValue('CaptainID', null, false));
+ $this->assertFalse((bool)$team1->hasValue('Captain', [], false));
+ $this->assertFalse((bool)$team1->hasValue('CaptainID', [], false));
// Add a captain to team 1
$team1->setField('CaptainID', $player1->ID);
$team1->write();
- $this->assertTrue((bool)$team1->hasValue('Captain', null, false));
- $this->assertTrue((bool)$team1->hasValue('CaptainID', null, false));
+ $this->assertTrue((bool)$team1->hasValue('Captain', [], false));
+ $this->assertTrue((bool)$team1->hasValue('CaptainID', [], false));
$this->assertEquals(
$player1->ID,
@@ -2075,22 +2075,22 @@ public function testClassNameSetForNewObjects()
public function testHasValue()
{
$team = new DataObjectTest\Team();
- $this->assertFalse($team->hasValue('Title', null, false));
- $this->assertFalse($team->hasValue('DatabaseField', null, false));
+ $this->assertFalse($team->hasValue('Title', [], false));
+ $this->assertFalse($team->hasValue('DatabaseField', [], false));
$team->Title = 'hasValue';
- $this->assertTrue($team->hasValue('Title', null, false));
- $this->assertFalse($team->hasValue('DatabaseField', null, false));
+ $this->assertTrue($team->hasValue('Title', [], false));
+ $this->assertFalse($team->hasValue('DatabaseField', [], false));
$team->Title = '';
$this->assertTrue(
- $team->hasValue('Title', null, false),
+ $team->hasValue('Title', [], false),
'Test that an empty paragraph is a value for non-HTML fields.'
);
$team->DatabaseField = 'hasValue';
- $this->assertTrue($team->hasValue('Title', null, false));
- $this->assertTrue($team->hasValue('DatabaseField', null, false));
+ $this->assertTrue($team->hasValue('Title', [], false));
+ $this->assertTrue($team->hasValue('DatabaseField', [], false));
}
public function testHasMany()
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/ORM/DecimalTest.php b/tests/php/ORM/DecimalTest.php
index fffd2aecb5a..0705da1094f 100644
--- a/tests/php/ORM/DecimalTest.php
+++ b/tests/php/ORM/DecimalTest.php
@@ -4,6 +4,7 @@
use SilverStripe\Dev\SapphireTest;
use SilverStripe\ORM\FieldType\DBDecimal;
+use TypeError;
class DecimalTest extends SapphireTest
{
@@ -45,11 +46,8 @@ public function testSpecifiedDefaultValue()
public function testInvalidSpecifiedDefaultValue()
{
- $this->assertEquals(
- $this->testDataObject->MyDecimal3,
- 0,
- 'Invalid default value for Decimal type is casted to 0'
- );
+ $this->expectException(TypeError::class);
+ new DBDecimal(defaultValue: 'Invalid');
}
public function testSpecifiedDefaultValueInDefaultsArray()
diff --git a/tests/php/ORM/DecimalTest/TestObject.php b/tests/php/ORM/DecimalTest/TestObject.php
index 2dd5da89850..0f3caecbb1c 100644
--- a/tests/php/ORM/DecimalTest/TestObject.php
+++ b/tests/php/ORM/DecimalTest/TestObject.php
@@ -13,7 +13,6 @@ class TestObject extends DataObject implements TestOnly
'Name' => 'Varchar',
'MyDecimal1' => 'Decimal',
'MyDecimal2' => 'Decimal(5,3,2.5)',
- 'MyDecimal3' => 'Decimal(4,2,"Invalid default value")',
'MyDecimal4' => 'Decimal',
'MyDecimal5' => 'Decimal(20,18,0.99999999999999999999)',
'MyDecimal6' => 'Decimal',
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.php b/tests/php/View/SSViewerTest.php
index 956b30aaa21..1dbc3aa36d6 100644
--- a/tests/php/View/SSViewerTest.php
+++ b/tests/php/View/SSViewerTest.php
@@ -653,12 +653,14 @@ public function testEscapedArguments()
public function testLoopWhitespace()
{
+ $data = new ArrayList([new SSViewerTest\TestFixture()]);
$this->assertEquals(
- 'before[out:SingleItem.Test]after
+ 'before[out:Test]after
beforeTestafter',
$this->render(
- 'before<% loop SingleItem %>$Test<% end_loop %>after
- before<% loop SingleItem %>Test<% end_loop %>after'
+ 'before<% loop %>$Test<% end_loop %>after
+ before<% loop %>Test<% end_loop %>after',
+ $data
)
);
@@ -668,15 +670,16 @@ public function testLoopWhitespace()
$this->assertEquals(
'before
-[out:SingleItem.ItemOnItsOwnLine]
+[out:ItemOnItsOwnLine]
after',
$this->render(
'before
-<% loop SingleItem %>
+<% loop %>
$ItemOnItsOwnLine
<% end_loop %>
-after'
+after',
+$data
)
);
@@ -2369,7 +2372,7 @@ public function testPrimitivesConvertedToDBFields()
public function testMe(): void
{
$mockArrayData = $this->getMockBuilder(ArrayData::class)->addMethods(['forTemplate'])->getMock();
- $mockArrayData->expects($this->once())->method('forTemplate');
+ $mockArrayData->expects($this->once())->method('forTemplate')->willReturn('');
$this->render('$Me', $mockArrayData);
}
}
diff --git a/tests/php/View/SSViewerTest/TestFixture.php b/tests/php/View/SSViewerTest/TestFixture.php
index 7f1da4b265c..d294517138c 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.php b/tests/php/View/ViewableDataTest.php
index 5b8ba205fad..9f24042cd6b 100644
--- a/tests/php/View/ViewableDataTest.php
+++ b/tests/php/View/ViewableDataTest.php
@@ -133,7 +133,7 @@ public function testDefaultValueWrapping()
$this->assertFalse($data->hasValue('SomethingElse'));
// this should cast the raw string to a StringField since we are
// passing true as the third argument:
- $obj = $data->obj('Title', null, true);
+ $obj = $data->obj('Title', [], true);
$this->assertTrue(is_object($obj));
// and the string field should have the value of the raw string:
$this->assertEquals('SomeTitleValue', $obj->forTemplate());
@@ -164,11 +164,11 @@ public function testObjWithCachedStringValueReturnsValidObject()
// Save a literal string into cache
$cache = true;
- $uncastedData = $obj->obj('noCastingInformation', null, false, $cache);
+ $uncastedData = $obj->obj('noCastingInformation', [], false, $cache);
// Fetch the cached string as an object
$forceReturnedObject = true;
- $castedData = $obj->obj('noCastingInformation', null, $forceReturnedObject);
+ $castedData = $obj->obj('noCastingInformation', [], $forceReturnedObject);
// Uncasted data should always be the nonempty string
$this->assertNotEmpty($uncastedData, 'Uncasted data was empty.');
@@ -189,15 +189,15 @@ public function testCaching()
$objCached->Test = 'AAA';
$objNotCached->Test = 'AAA';
- $this->assertEquals('AAA', $objCached->obj('Test', null, true, true));
- $this->assertEquals('AAA', $objNotCached->obj('Test', null, true, true));
+ $this->assertEquals('AAA', $objCached->obj('Test', [], true, true));
+ $this->assertEquals('AAA', $objNotCached->obj('Test', [], true, true));
$objCached->Test = 'BBB';
$objNotCached->Test = 'BBB';
// Cached data must be always the same
- $this->assertEquals('AAA', $objCached->obj('Test', null, true, true));
- $this->assertEquals('BBB', $objNotCached->obj('Test', null, true, true));
+ $this->assertEquals('AAA', $objCached->obj('Test', [], true, true));
+ $this->assertEquals('BBB', $objNotCached->obj('Test', [], true, true));
}
public function testSetFailover()
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);
}