Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 43 additions & 11 deletions modules/backend/classes/FormField.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ class FormField
public $span = 'full';

/**
* @var string Specifies a size. Possible values: tiny, small, large, huge, giant.
* @var string|int Specifies a size. Possible values for textarea: tiny, small, large, huge, giant.
*/
public $size = 'large';
public $size;
Comment on lines +94 to +96
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all usages of $field->size in form partials and related template files

echo "=== Searching for \$field->size usages ==="
rg -n '\$field->size' --glob '*.php' modules/backend/widgets/form/

echo ""
echo "=== Searching for direct size comparisons/operations ==="
rg -n '->size\s*[!=<>]' --glob '*.php' modules/backend/widgets/form/

echo ""
echo "=== Checking is_numeric guards and other null checks ==="
rg -n 'is_numeric.*size|size.*is_numeric' --glob '*.php' modules/backend/widgets/form/

Repository: wintercms/winter

Length of output: 1420


🏁 Script executed:

cat -n modules/backend/widgets/form/partials/_field_textarea.php | head -20

Repository: wintercms/winter

Length of output: 610


🏁 Script executed:

# Check FormField constructor to see if $size is initialized
sed -n '1,150p' modules/backend/classes/FormField.php | grep -A 30 'public function __construct'

Repository: wintercms/winter

Length of output: 42


🏁 Script executed:

# Search for the constructor in FormField
rg -n 'function __construct' modules/backend/classes/FormField.php -A 20

Repository: wintercms/winter

Length of output: 617


🏁 Script executed:

# Check displayAs() method to see when it sets $size = 'large'
rg -n 'public function displayAs' modules/backend/classes/FormField.php -A 80 | head -100

Repository: wintercms/winter

Length of output: 2632


Fix null-safety issue in _field_textarea.php template.

The $size property no longer has a default value, and while displayAs() auto-sets 'large' for textarea types, the _field_textarea.php template unsafely outputs size-<?= $field->size ?> without checking for null. This produces an invalid CSS class name size- when $size is null.

Update _field_textarea.php line 9 to guard against null, similar to how _field_url.php and _field_tel.php handle it:

Suggested fix
class="form-control field-textarea <?= isset($field->size) ? 'size-' . e($field->size) : '' ?>"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@modules/backend/classes/FormField.php` around lines 94 - 96, The textarea
template `_field_textarea.php` unsafely emits "size-<?= $field->size ?>" when
the FormField::$size property can be null; update the class attribute in
`_field_textarea.php` to only append the size class when $field->size is set
(e.g., use isset($field->size) to conditionally add 'size-' . e($field->size) or
an empty string), mirroring the null-safe pattern used in `_field_url.php` and
`_field_tel.php`; keep the rest of the markup unchanged and continue to rely on
displayAs() defaulting textarea size to 'large' when appropriate.


/**
* @var string Specifies contextual visibility of this form field.
Expand Down Expand Up @@ -211,7 +211,7 @@ public function span($value = 'full')
}

/**
* Sets a side of the field on a form.
* Sets the size of the field on a form.
* @param string $value Specifies a size. Possible values: tiny, small, large, huge, giant
*/
public function size($value = 'large')
Expand Down Expand Up @@ -259,6 +259,11 @@ public function options($value = null)
*/
public function displayAs($type, $config = [])
{
if (in_array($type, ['textarea', 'widget'])) {
// defaults to 'large'
$this->size = 'large';
}

$this->type = strtolower($type) ?: $this->type;
$this->config = $this->evalConfig($config);

Expand All @@ -281,18 +286,18 @@ protected function evalConfig($config)
*/
$applyConfigValues = [
'commentHtml',
'placeholder',
'context',
'cssClass',
'dependsOn',
'required',
'readOnly',
'disabled',
'cssClass',
'stretch',
'context',
'hidden',
'trigger',
'preset',
'path',
'placeholder',
'preset',
'readOnly',
'required',
'stretch',
'trigger',
];

foreach ($applyConfigValues as $value) {
Expand Down Expand Up @@ -729,4 +734,31 @@ protected function getFieldNameFromData($fieldName, $data, $default = null)

return $result;
}

/**
* Implements the getter functionality.
* @param string $name
*/
public function __get($name)
{
if (is_array($this->config) && array_key_exists($name, $this->config)) {
return array_get($this->config, $name);
}
if (property_exists($this, $name)) {
return $this->{$name};
}
return null;
}

/**
* Determine if an attribute exists on the object.
* @param string $name
*/
public function __isset($name)
{
if (is_array($this->config) && array_key_exists($name, $this->config)) {
return true;
}
return property_exists($this, $name) && !is_null($this->{$name});
}
}
2 changes: 1 addition & 1 deletion modules/backend/widgets/form/partials/_field_tel.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class="form-control"
<?= isset($field->maxlength) && is_numeric($field->maxlength) ? 'maxlength="' . e($field->maxlength) . '"' : '' ?>
<?= isset($field->minlength) && is_numeric($field->minlength) ? 'minlength="' . e($field->minlength) . '"' : '' ?>
<?= isset($field->pattern) && is_string($field->pattern) ? 'pattern="' . e($field->pattern) . '"' : '' ?>
<?= isset($field->placeholder) && is_string($field->placeholder) ? 'placeholder="' . e($field->placeholder) . '"' : '' ?>
<?= isset($field->placeholder) && is_string($field->placeholder) ? 'placeholder="' . e(trans($field->placeholder)) . '"' : '' ?>
<?= isset($field->size) && is_numeric($field->size) ? 'size="' . e($field->size) . '"' : '' ?>
<?= $field->getAttributes() ?>
<?= $listId ? 'list="' . e($listId) . '"' : '' ?>
Expand Down
15 changes: 6 additions & 9 deletions modules/backend/widgets/form/partials/_field_url.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,15 @@ class="form-control"
name="<?= $field->getName() ?>"
id="<?= $field->getId() ?>"
value="<?= e($field->value) ?>"
placeholder="<?= e(trans($field->placeholder)) ?>"
class="form-control"
<?= isset($field->autocomplete) && is_string($field->autocomplete) ? 'autocomplete="' . e($field->autocomplete) . '"' : '' ?>
<?= isset($field->maxlength) && is_numeric($field->maxlength) ? 'maxlength="' . e($field->maxlength) . '"' : '' ?>
<?= isset($field->minlength) && is_numeric($field->minlength) ? 'minlength="' . e($field->minlength) . '"' : '' ?>
<?= isset($field->pattern) && is_string($field->pattern) ? 'pattern="' . e($field->pattern) . '"' : '' ?>
<?= isset($field->placeholder) && is_string($field->placeholder) ? 'placeholder="' . e(trans($field->placeholder)) . '"' : '' ?>
<?= isset($field->size) && is_numeric($field->size) ? 'size="' . e($field->size) . '"' : '' ?>
<?= $field->getAttributes() ?>
<?= isset($field->maxlength) ? 'maxlength="' . e($field->maxlength) . '"' : '' ?>
<?= isset($field->minlength) ? 'minlength="' . e($field->minlength) . '"' : '' ?>
<?= isset($field->pattern) ? 'pattern="' . e($field->pattern) . '"' : '' ?>
<?= isset($field->size) ? 'size="' . e($field->size) . '"' : '' ?>
<?= $listId ? 'list="' . e($listId) . '"' : '' ?>
<?= isset($field->autocomplete) ? 'autocomplete="' . e($field->autocomplete) . '"' : '' ?>
<?= isset($field->required) && $field->required ? 'required' : '' ?>
<?= isset($field->readonly) && $field->readonly ? 'readonly' : '' ?>
<?= isset($field->disabled) && $field->disabled ? 'disabled' : '' ?>
/>
<?php if ($hasOptions): ?>
<datalist id="<?= e($listId) ?>">
Expand Down
Loading