Skip to content

Commit

Permalink
API Strong typing for the view layer
Browse files Browse the repository at this point in the history
  • Loading branch information
GuySartorelli committed Aug 22, 2024
1 parent f3ff4e0 commit fef4b86
Show file tree
Hide file tree
Showing 57 changed files with 650 additions and 1,213 deletions.
19 changes: 19 additions & 0 deletions src/Core/Exception/NotImplementedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace SilverStripe\Core\Exception;

use LogicException;

/**
* Thrown when a requested method or operation is not implemented.
*
* This is useful when method signatures are provided by parent classes, but the method body
* should be implemented by a subclass.
*
* This is also useful when method signatures are required by an interface or abstract superclass,
* but the subclass cannot implement it and therefore the method should not be called.
*/
class NotImplementedException extends LogicException
{
// no-op
}
2 changes: 1 addition & 1 deletion src/Dev/TaskRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ public function canInit(): bool
}
return count($this->getTaskList()) > 0;
}

public function providePermissions(): array
{
return [
Expand Down
6 changes: 2 additions & 4 deletions src/Forms/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ public function setFieldMessage(
return $this;
}

public function castingHelper($field, bool $useFallback = true)
public function castingHelper(string $field, bool $useFallback = true): ?string
{
// Override casting for field message
if (strcasecmp($field ?? '', 'Message') === 0 && ($helper = $this->getMessageCastingHelper())) {
Expand Down Expand Up @@ -1547,10 +1547,8 @@ public function getData()
*
* This is returned when you access a form as $FormObject rather
* than <% with FormObject %>
*
* @return DBHTMLText
*/
public function forTemplate()
public function forTemplate(): string
{
if (!$this->canBeCached()) {
HTTPCacheControlMiddleware::singleton()->disableCache();
Expand Down
6 changes: 2 additions & 4 deletions src/Forms/FormField.php
Original file line number Diff line number Diff line change
Expand Up @@ -790,7 +790,7 @@ public function securityTokenEnabled()
return $form->getSecurityToken()->isEnabled();
}

public function castingHelper($field, bool $useFallback = true)
public function castingHelper(string $field, bool $useFallback = true): ?string
{
// Override casting for field message
if (strcasecmp($field ?? '', 'Message') === 0 && ($helper = $this->getMessageCastingHelper())) {
Expand Down Expand Up @@ -1286,10 +1286,8 @@ public function debug()
/**
* This function is used by the template processor. If you refer to a field as a $ variable, it
* will return the $Field value.
*
* @return string
*/
public function forTemplate()
public function forTemplate(): string
{
return $this->Field();
}
Expand Down
2 changes: 1 addition & 1 deletion src/Forms/FormRequestHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ public function validationResult()
return $result;
}

public function forTemplate()
public function forTemplate(): ?string
{
return $this->form->forTemplate();
}
Expand Down
2 changes: 1 addition & 1 deletion src/Forms/ReadonlyField.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function Type()
return 'readonly';
}

public function castingHelper($field, bool $useFallback = true)
public function castingHelper(string $field, bool $useFallback = true): ?string
{
// Get dynamic cast for 'Value' field
if (strcasecmp($field ?? '', 'Value') === 0) {
Expand Down
4 changes: 1 addition & 3 deletions src/ORM/ArrayList.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,8 @@ public function count(): int

/**
* Returns true if this list has items
*
* @return bool
*/
public function exists()
public function exists(): bool
{
return !empty($this->items);
}
Expand Down
4 changes: 1 addition & 3 deletions src/ORM/DataList.php
Original file line number Diff line number Diff line change
Expand Up @@ -1702,10 +1702,8 @@ public function last()

/**
* Returns true if this DataList has items
*
* @return bool
*/
public function exists()
public function exists(): bool
{
return $this->dataQuery->exists();
}
Expand Down
77 changes: 30 additions & 47 deletions src/ORM/DataObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -816,10 +816,8 @@ public function defineMethods()
* Returns true if this object "exists", i.e., has a sensible value.
* The default behaviour for a DataObject is to return true if
* the object exists in the database, you can override this in subclasses.
*
* @return boolean true if this object exists
*/
public function exists()
public function exists(): bool
{
return $this->isInDB();
}
Expand Down Expand Up @@ -2687,19 +2685,16 @@ public function getFrontEndFields($params = null)
return $untabbedFields;
}

public function getViewerTemplates($suffix = '')
public function getViewerTemplates(string $suffix = ''): array
{
return SSViewer::get_templates_by_class(static::class, $suffix, $this->baseClass());
}

/**
* Gets the value of a field.
* Called by {@link __get()} and any getFieldName() methods you might create.
*
* @param string $field The name of the field
* @return mixed The field value
*/
public function getField($field)
public function getField(string $field): mixed
{
// If we already have a value in $this->record, then we should just return that
if (isset($this->record[$field])) {
Expand Down Expand Up @@ -2910,12 +2905,8 @@ public function isChanged($fieldName = null, $changeLevel = DataObject::CHANGE_S
/**
* Set the value of the field
* Called by {@link __set()} and any setFieldName() methods you might create.
*
* @param string $fieldName Name of the field
* @param mixed $val New field value
* @return $this
*/
public function setField($fieldName, $val)
public function setField(string $fieldName, mixed $value): static
{
$this->objCacheClear();
//if it's a has_one component, destroy the cache
Expand All @@ -2934,42 +2925,42 @@ public function setField($fieldName, $val)
if ($schema->unaryComponent(static::class, $fieldName)) {
unset($this->components[$fieldName]);
// Assign component directly
if (is_null($val) || $val instanceof DataObject) {
return $this->setComponent($fieldName, $val);
if (is_null($value) || $value instanceof DataObject) {
return $this->setComponent($fieldName, $value);
}
// Assign by ID instead of object
if (is_numeric($val)) {
if (is_numeric($value)) {
$fieldName .= 'ID';
}
}

// Situation 1: Passing an DBField
if ($val instanceof DBField) {
$val->setName($fieldName);
$val->saveInto($this);
if ($value instanceof DBField) {
$value->setName($fieldName);
$value->saveInto($this);

// Situation 1a: Composite fields should remain bound in case they are
// later referenced to update the parent dataobject
if ($val instanceof DBComposite) {
$val->bindTo($this);
$this->setFieldValue($fieldName, $val);
if ($value instanceof DBComposite) {
$value->bindTo($this);
$this->setFieldValue($fieldName, $value);
}
// Situation 2: Passing a literal or non-DBField object
} else {
$this->setFieldValue($fieldName, $val);
$this->setFieldValue($fieldName, $value);
}
return $this;
}

private function setFieldValue(string $fieldName, mixed $val): void
private function setFieldValue(string $fieldName, mixed $value): void
{
$schema = static::getSchema();
// If this is a proper database field, we shouldn't be getting non-DBField objects
if (is_object($val) && !($val instanceof DBField) && $schema->fieldSpec(static::class, $fieldName)) {
if (is_object($value) && !($value instanceof DBField) && $schema->fieldSpec(static::class, $fieldName)) {
throw new InvalidArgumentException('DataObject::setFieldValue: passed an object that is not a DBField');
}

if (!empty($val) && !is_scalar($val)) {
if (!empty($value) && !is_scalar($value)) {
$dbField = $this->dbObject($fieldName);
if ($dbField && $dbField->scalarValueOnly()) {
throw new InvalidArgumentException(
Expand All @@ -2982,12 +2973,12 @@ private function setFieldValue(string $fieldName, mixed $val): void
}

// if a field is not existing or has strictly changed
if (!array_key_exists($fieldName, $this->original ?? []) || $this->original[$fieldName] !== $val) {
if (!array_key_exists($fieldName, $this->original ?? []) || $this->original[$fieldName] !== $value) {
// At the very least, the type has changed
$this->changed[$fieldName] = DataObject::CHANGE_STRICT;

if ((!array_key_exists($fieldName, $this->original ?? []) && $val)
|| (array_key_exists($fieldName, $this->original ?? []) && $this->original[$fieldName] != $val)
if ((!array_key_exists($fieldName, $this->original ?? []) && $value)
|| (array_key_exists($fieldName, $this->original ?? []) && $this->original[$fieldName] != $value)
) {
// Value has changed as well, not just the type
$this->changed[$fieldName] = DataObject::CHANGE_VALUE;
Expand All @@ -2998,7 +2989,7 @@ private function setFieldValue(string $fieldName, mixed $val): void
}

// Value is saved regardless, since the change detection relates to the last write
$this->record[$fieldName] = $val;
$this->record[$fieldName] = $value;
}

/**
Expand Down Expand Up @@ -3029,7 +3020,7 @@ public function setCastedField($fieldName, $value)
/**
* {@inheritdoc}
*/
public function castingHelper($field, bool $useFallback = true)
public function castingHelper(string $field, bool $useFallback = true): ?string
{
$fieldSpec = static::getSchema()->fieldSpec(static::class, $field);
if ($fieldSpec) {
Expand All @@ -3054,19 +3045,16 @@ public function castingHelper($field, bool $useFallback = true)
* Returns true if the given field exists in a database column on any of
* the objects tables and optionally look up a dynamic getter with
* get<fieldName>().
*
* @param string $field Name of the field
* @return boolean True if the given field exists
*/
public function hasField($field)
public function hasField(string $fieldName): bool
{
$schema = static::getSchema();
return (
array_key_exists($field, $this->record ?? [])
|| array_key_exists($field, $this->components ?? [])
|| $schema->fieldSpec(static::class, $field)
|| $schema->unaryComponent(static::class, $field)
|| $this->hasMethod("get{$field}")
array_key_exists($fieldName, $this->record ?? [])
|| array_key_exists($fieldName, $this->components ?? [])
|| $schema->fieldSpec(static::class, $fieldName)
|| $schema->unaryComponent(static::class, $fieldName)
|| $this->hasMethod("get{$fieldName}")
);
}

Expand Down Expand Up @@ -3214,7 +3202,7 @@ public function canCreate($member = null, $context = [])
*
* @return string HTML data representing this object
*/
public function debug()
public function debug(): string
{
$class = static::class;
$val = "<h3>Database record: {$class}</h3>\n<ul>\n";
Expand Down Expand Up @@ -4386,13 +4374,8 @@ public function provideI18nEntities()
/**
* Returns true if the given method/parameter has a value
* (Uses the DBField::hasValue if the parameter is a database field)
*
* @param string $field The field name
* @param array $arguments
* @param bool $cache
* @return boolean
*/
public function hasValue($field, $arguments = null, $cache = true)
public function hasValue(string $field, array $arguments = [], bool $cache = true): bool
{
// has_one fields should not use dbObject to check if a value is given
$hasOne = static::getSchema()->hasOneComponent(static::class, $field);
Expand Down
2 changes: 1 addition & 1 deletion src/ORM/DataObjectInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function delete();
* @param string $fieldName
* @return mixed
*/
public function __get($fieldName);
public function __get(string $property): mixed;

/**
* Save content from a form into a field on this data object.
Expand Down
2 changes: 1 addition & 1 deletion src/ORM/FieldType/DBBigInt.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
class DBBigInt extends DBInt
{

public function requireField()
public function requireField(): void
{
$parts = [
'datatype' => 'bigint',
Expand Down
Loading

0 comments on commit fef4b86

Please sign in to comment.