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
25 changes: 10 additions & 15 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,19 @@
"role": "lead"
}
],
"time": "2026-03-15",
"time": "2026-03-17",
"repositories": [],
"require": {
"php": "^7.4 || ^8",
"horde/core": "^3 || dev-FRAMEWORK_6_0",
"horde/date": "^3 || dev-FRAMEWORK_6_0",
"horde/exception": "^3 || dev-FRAMEWORK_6_0",
"horde/mail": "^3 || dev-FRAMEWORK_6_0",
"horde/mime": "^3 || dev-FRAMEWORK_6_0",
"horde/nls": "^3 || dev-FRAMEWORK_6_0",
"horde/token": "^3 || dev-FRAMEWORK_6_0",
"horde/translation": "^3 || dev-FRAMEWORK_6_0",
"horde/util": "^3 || dev-FRAMEWORK_6_0",
"horde/core": "^3",
"horde/date": "^3",
"horde/exception": "^3",
"horde/mail": "^3",
"horde/mime": "^3",
"horde/nls": "^3",
"horde/token": "^3",
"horde/translation": "^3",
"horde/util": "^3",
"ext-json": "*"
},
"require-dev": {
Expand All @@ -52,10 +52,5 @@
},
"config": {
"allow-plugins": {}
},
"extra": {
"branch-alias": {
"dev-FRAMEWORK_6_0": "3.x-dev"
}
}
}
78 changes: 68 additions & 10 deletions lib/Horde/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
require_once 'Horde/Form/Type.php';
}

use Horde\Util\ArrayUtils;

/**
* Horde_Form Master Class.
*
Expand Down Expand Up @@ -55,7 +57,8 @@ class Horde_Form
public function __construct($vars, $title = '', $name = null)
{
if (empty($name)) {
$name = Horde_String::lower(get_class($this));
static $counter = 0;
$name = Horde_String::lower(get_class($this)) . '_' . (++$counter);
}
$name = str_replace('\\', '_', $name);

Expand Down Expand Up @@ -161,9 +164,21 @@ public function getRenderer($params = [])
}

/**
* Initialize a Horde_Form_Type object from a type id
* Initialize a Horde_Form_Type object from a type id (internal use only)
*
* This method is private as of 3.0.0-beta4 to encapsulate parameter normalization logic.
* External code should use Horde_Form_Type::create() instead for instantiating types.
*
* For legacy code not yet migrated to src/V3, use Horde_Form_Type::create($type, $params)
* which provides the same functionality with a stable public API.
*
* @param string $type Type identifier
* @param array $params Type initialization parameters
*
* @return Horde_Form_Type
* @throws Horde_Exception
*
* @since 3.0.0-beta4 Changed to private visibility
*/
private function getType($type, $params = [])
{
Expand Down Expand Up @@ -256,6 +271,50 @@ public function getSectionExpandedState($section, $boolean = false)
}
}

/**
* Get information about all form sections.
*
* Returns an array of section metadata for all sections in the form.
* Useful for rendering section navigation or inspecting section structure.
* Migration aid for V3 compatibility.
*
* @return array Array of section info keyed by section name, each containing:
* - title: Section description/title
* - image: Section image (if any)
* - expanded: Whether section is expanded (boolean)
*
* @since 3.0.0
*/
public function getSectionInfo(): array
{
$info = [];
foreach ($this->_sections as $section => $data) {
$info[$section] = [
'title' => $data['desc'] ?? '',
'image' => $data['image'] ?? '',
'expanded' => $data['expanded'] ?? true,
];
}
return $info;
}

/**
* Get the form encoding type.
*
* Returns the encoding type needed for the form (e.g., 'multipart/form-data'
* for forms with file upload fields). This is automatically set when file
* or image field types are added to the form.
* Migration aid for V3 compatibility.
*
* @return string|null The encoding type, or null if not set
*
* @since 3.0.0
*/
public function getEnctype(): ?string
{
return $this->_enctype;
}

/**
* Add a new form field variable to the form.
*/
Expand Down Expand Up @@ -682,6 +741,9 @@ public function validate($vars = null, $canAutoFill = false)
$vars = $this->_vars;
}

// Clear previous validation errors
$this->_errors = [];

/* Get submitted status. */
if ($this->isSubmitted() || $canAutoFill) {
/* Form was submitted or can autofill; check for any variable
Expand Down Expand Up @@ -777,7 +839,7 @@ public function clearError($var)

public function isValid()
{
return ($this->_autofilled || count($this->_errors) == 0);
return count($this->_errors) == 0;
}

public function execute()
Expand Down Expand Up @@ -832,10 +894,10 @@ public function _getInfoFromVariables($variables, $vars, $info)
} else {
// A field name like example[key1][key2][key3]
$varName = $var->getVarName();
if (Horde_Array::getArrayParts($varName, $base, $keys)) {
if (ArrayUtils::getArrayParts($varName, $base, $keys)) {
$res = $var->getInfo($vars, $varName);
$path = array_merge([$base], $keys);
Horde_Array::setElement($info, $path, $res);
ArrayUtils::setElement($info, $path, $res);
} else {
if (!isset($info[$varName])) {
$info[$varName] = null;
Expand Down Expand Up @@ -867,11 +929,7 @@ public function hasHelp()
public function isSubmitted()
{
if (is_null($this->_submitted)) {
if ($this->_vars->get('formname') == $this->getName()) {
$this->_submitted = true;
} else {
$this->_submitted = false;
}
return ($this->_vars->get('formname') == $this->getName());
}

return $this->_submitted;
Expand Down
2 changes: 2 additions & 0 deletions lib/Horde/Form/Action.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
* @package Form
*/

use Horde\Exception\PEAR;

/**
* The Horde_Form_Action class provides an API for adding actions to
* Horde_Form variables.
Expand Down
4 changes: 3 additions & 1 deletion lib/Horde/Form/Renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
* @package Form
*/

use Horde\Util\ArrayUtils;

/**
* The Horde_Form_Renderer class provides HTML and other renderings of
* forms for the Horde_Form:: package.
Expand Down Expand Up @@ -235,7 +237,7 @@ public function getFormVars($form)
// past. The actual forms need to be fixed instead.
if ($var->isHidden() || !$var->isReadonly()) {
$varname = $var->getVarName();
if (Horde_Array::getArrayParts($varname, $base, $keys)) {
if (ArrayUtils::getArrayParts($varname, $base, $keys)) {
array_unshift($keys, $base);
$place = &$vars;
while ($key = array_shift($keys)) {
Expand Down
61 changes: 51 additions & 10 deletions lib/Horde/Form/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
* @package Form
*/

use Horde\Util\ArrayUtils;

/**
* Horde_Form_Type Class
*
Expand Down Expand Up @@ -96,6 +98,45 @@ public function about()
return ['name' => $this->getTypeName()];
}

/**
* Factory method to create form type instances
*
* This is the public API for instantiating form types outside of Form context.
* Use this instead of calling Horde_Form::getType() which is private.
*
* @param string $type Type name (e.g., 'html', 'email') or app-prefixed (e.g., 'turba:TurbaTags')
* @param array $params Type parameters to pass to init()
*
* @return Horde_Form_Type
* @throws Horde_Exception If type class does not exist
*
* @since 3.0.0
*/
public static function create(string $type, array $params = []): Horde_Form_Type
{
if (strpos($type, ':') !== false) {
[$app, $type] = explode(':', $type);
$type_class = $app . '_Form_Type_' . $type;
} else {
$type_class = 'Horde_Form_Type_' . $type;
}

if (!class_exists($type_class)) {
throw new Horde_Exception(sprintf(
'Nonexistent class "%s" for field type "%s"',
$type_class,
$type
));
}

$type_obj = new $type_class();
if (!empty($params)) {
$type_obj->init(...array_values($params));
}

return $type_obj;
}

}

class Horde_Form_Type_spacer extends Horde_Form_Type
Expand Down Expand Up @@ -381,7 +422,7 @@ public function isValid($var, $vars, $value, $message)
}

if ($var->isRequired() && empty($this->_regex)) {
if (strlen(trim($value)) == 0) {
if (strlen(trim((string)$value)) == 0) {
return $this->invalid('This field is required.');
}
} elseif (!empty($this->_regex) && !preg_match($this->_regex, $value)) {
Expand Down Expand Up @@ -1264,19 +1305,19 @@ private function _getUpload($vars, $var)
$this->_img['img']['type'] = $this->getUploadedFileType($varname . '[new]');

/* Get the other parts of the upload. */
Horde_Array::getArrayParts($varname . '[new]', $base, $keys);
ArrayUtils::getArrayParts($varname . '[new]', $base, $keys);

/* Get the temporary file name. */
$keys_path = array_merge([$base, 'tmp_name'], $keys);
$this->_img['img']['file'] = Horde_Array::getElement($_FILES, $keys_path);
$this->_img['img']['file'] = ArrayUtils::getElement($_FILES, $keys_path);

/* Get the actual file name. */
$keys_path = array_merge([$base, 'name'], $keys);
$this->_img['img']['name'] = Horde_Array::getElement($_FILES, $keys_path);
$this->_img['img']['name'] = ArrayUtils::getElement($_FILES, $keys_path);

/* Get the file size. */
$keys_path = array_merge([$base, 'size'], $keys);
$this->_img['img']['size'] = Horde_Array::getElement($_FILES, $keys_path);
$this->_img['img']['size'] = ArrayUtils::getElement($_FILES, $keys_path);

/* Get any existing values for the image upload field. */
$upload = $vars->get($var->getVarName());
Expand Down Expand Up @@ -1326,14 +1367,14 @@ private function _getUpload($vars, $var)
public function getUploadedFileType($field)
{
/* Get any index on the field name. */
$index = Horde_Array::getArrayParts($field, $base, $keys);
$index = ArrayUtils::getArrayParts($field, $base, $keys);

if ($index) {
/* Index present, fetch the mime type var to check. */
$keys_path = array_merge([$base, 'type'], $keys);
$type = Horde_Array::getElement($_FILES, $keys_path);
$type = ArrayUtils::getElement($_FILES, $keys_path);
$keys_path = array_merge([$base, 'tmp_name'], $keys);
$tmp_name = Horde_Array::getElement($_FILES, $keys_path);
$tmp_name = ArrayUtils::getElement($_FILES, $keys_path);
} else {
/* No index, simple set up of vars to check. */
$type = $_FILES[$field]['type'];
Expand All @@ -1346,14 +1387,14 @@ public function getUploadedFileType($field)
if ($index) {
/* Get the name value. */
$keys_path = array_merge([$base, 'name'], $keys);
$name = Horde_Array::getElement($_FILES, $keys_path);
$name = ArrayUtils::getElement($_FILES, $keys_path);

/* Work out the type from the file name. */
$type = Horde_Mime_Magic::filenameToMime($name);

/* Set the type. */
$keys_path = array_merge([$base, 'type'], $keys);
Horde_Array::setElement($_FILES, $keys_path, $type);
ArrayUtils::setElement($_FILES, $keys_path, $type);
} else {
/* Work out the type from the file name. */
$type = Horde_Mime_Magic::filenameToMime($_FILES[$field]['name']);
Expand Down
2 changes: 1 addition & 1 deletion src/V3/DateVariable.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function init(...$params)

protected function isValid(Horde_Variables|array $vars, $value): bool
{
if ($this->isRequired() && strlen(trim($value)) == 0) {
if ($this->isRequired() && strlen(trim((string)$value)) == 0) {
$this->message = sprintf(Horde_Form_Translation::t("%s is required"), $this->getHumanName());
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion src/V3/Ip6addressVariable.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Ip6addressVariable extends TextVariable
{
public function isValid(Horde_Variables $vars, $value): bool
{
if (strlen(trim($value)) > 0) {
if (strlen(trim((string)$value)) > 0) {
$valid = @inet_pton($value);
if ($valid === false) {
return $this->invalid('Please enter a valid IP address.');
Expand Down
2 changes: 1 addition & 1 deletion src/V3/IpaddressVariable.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class IpaddressVariable extends TextVariable
{
public function isValid(Horde_Variables $vars, $value): bool
{
if (strlen(trim($value)) > 0) {
if (strlen(trim((string)$value)) > 0) {
$ip = explode('.', $value);
$valid = (count($ip) == 4);
if ($valid) {
Expand Down
2 changes: 1 addition & 1 deletion src/V3/PasswordVariable.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class PasswordVariable extends BaseVariable
{
public function isValid(Horde_Variables $vars, $value): bool
{
if ($this->isRequired() && strlen(trim($value)) == 0) {
if ($this->isRequired() && strlen(trim((string)$value)) == 0) {
return $this->invalid('This field is required.');
}

Expand Down
2 changes: 1 addition & 1 deletion src/V3/PhoneVariable.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public function init(...$params)

public function isValid(Horde_Variables $vars, $value): bool
{
if (!strlen(trim($value))) {
if (!strlen(trim((string)$value))) {
if ($this->isRequired()) {
return $this->invalid('This field is required.');
}
Expand Down
2 changes: 1 addition & 1 deletion src/V3/TextVariable.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public function isValid(Horde_Variables $vars, $value): bool
}

if ($this->isRequired() && empty($this->_regex)) {
if (strlen(trim($value)) == 0) {
if (strlen(trim((string)$value)) == 0) {
return $this->invalid('This field is required.');
}
} elseif (!empty($this->_regex) && !preg_match($this->_regex, $value)) {
Expand Down
Loading
Loading