Qual: Fix multiple phan notices, update baseline (#36022)

* Qual: Fix phan notice with copy of attribute

* Qual: Fix phan notices with cast and updated param definition

* Qual: Fix phan notice with cast

* Qual: Update parameter type hint in utf8_check function

The parameter type hint for the $str parameter in the utf8_check function has been updated to include nullable types (string or int).

* Qual: Update User parameter type to nullable in call_trigger methods

The User parameter in call_trigger methods has been updated to be nullable to accommodate cases where the user object might not be available. This change ensures better flexibility and robustness in the codebase.

* Qual: Update phan baseline

* Qual: phpstan compatible type

* Qual: Add missing type hints for phan

- Add missing type hints for phan
- Fix indentation issues
- Improved consistency in code structure

* Qual: Update field configuration for backward compatibility

Enhanced the field configuration for backward compatibility by adding expected properties to the 'ref' field array.

---------

Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
This commit is contained in:
MDW
2025-11-01 17:47:28 +01:00
committed by GitHub
parent 0a3d2c7ddc
commit 073426d97e
9 changed files with 69 additions and 59 deletions

View File

@@ -13,7 +13,7 @@ return [
// PhanTypeMismatchProperty : 100+ occurrences
// PhanTypeMismatchArgument : 65+ occurrences
// PhanUndeclaredGlobalVariable : 60+ occurrences
// PhanTypeMismatchArgumentNullable : 40+ occurrences
// PhanTypeMismatchArgumentNullable : 20+ occurrences
// PhanTypeInvalidDimOffset : 15+ occurrences
// PhanTypeMismatchDimFetch : 10+ occurrences
// PhanUndeclaredMethod : 8 occurrences
@@ -82,7 +82,7 @@ return [
'htdocs/core/class/cgenericdic.class.php' => ['PhanUndeclaredProperty'],
'htdocs/core/class/commonobject.class.php' => ['PhanParamTooMany', 'PhanTypeMismatchArgument', 'PhanUndeclaredProperty'],
'htdocs/core/class/commonpeople.class.php' => ['PhanUndeclaredProperty'],
'htdocs/core/class/conf.class.php' => ['PhanTypeMismatchArgumentNullableInternal', 'PhanTypeMismatchProperty'],
'htdocs/core/class/conf.class.php' => ['PhanTypeMismatchProperty'],
'htdocs/core/class/ctyperesource.class.php' => ['PhanUndeclaredProperty'],
'htdocs/core/class/dolgraph.class.php' => ['PhanUndeclaredProperty'],
'htdocs/core/class/emailsenderprofile.class.php' => ['PhanUndeclaredProperty'],
@@ -121,7 +121,7 @@ return [
'htdocs/core/modules/holiday/mod_holiday_immaculate.php' => ['PhanTypeMismatchArgument'],
'htdocs/core/modules/hrm/doc/pdf_standard_evaluation.modules.php' => ['PhanUndeclaredProperty'],
'htdocs/core/modules/hrm/mod_evaluation_advanced.php' => ['PhanUndeclaredProperty'],
'htdocs/core/modules/import/import_csv.modules.php' => ['PhanPossiblyUndeclaredVariable', 'PhanTypeMismatchArgumentNullable', 'PhanTypeMismatchProperty'],
'htdocs/core/modules/import/import_csv.modules.php' => ['PhanPossiblyUndeclaredVariable', 'PhanTypeMismatchProperty'],
'htdocs/core/modules/import/import_xlsx.modules.php' => ['PhanTypeMismatchProperty'],
'htdocs/core/modules/member/modules_cards.php' => ['PhanTypeMismatchArgument'],
'htdocs/core/modules/mrp/doc/pdf_vinci.modules.php' => ['PhanUndeclaredProperty'],
@@ -162,7 +162,7 @@ return [
'htdocs/expedition/card.php' => ['PhanUndeclaredGlobalVariable', 'PhanUndeclaredProperty'],
'htdocs/expedition/class/expedition.class.php' => ['PhanUndeclaredProperty'],
'htdocs/expensereport/card.php' => ['PhanUndeclaredProperty'],
'htdocs/expensereport/class/expensereport.class.php' => ['PhanTypeMismatchArgument', 'PhanTypeMismatchArgumentNullable'],
'htdocs/expensereport/class/expensereport.class.php' => ['PhanTypeMismatchArgument'],
'htdocs/expensereport/payment/info.php' => ['PhanUndeclaredGlobalVariable'],
'htdocs/externalsite/frames.php' => ['PhanUndeclaredGlobalVariable'],
'htdocs/fichinter/card-rec.php' => ['PhanUndeclaredGlobalVariable'],
@@ -223,7 +223,6 @@ return [
'htdocs/projet/tasks.php' => ['PhanTypeMismatchArgument'],
'htdocs/projet/tasks/time.php' => ['PhanTypeInvalidDimOffset', 'PhanUndeclaredProperty'],
'htdocs/projet/tasks/tpl/linkedobjectblock.tpl.php' => ['PhanUndeclaredProperty'],
'htdocs/public/members/new.php' => ['PhanUndeclaredGlobalVariable'],
'htdocs/public/payment/newpayment.php' => ['PhanUndeclaredProperty'],
'htdocs/public/project/suggestbooth.php' => ['PhanUndeclaredGlobalVariable', 'PhanUndeclaredProperty'],
'htdocs/public/project/suggestconference.php' => ['PhanUndeclaredGlobalVariable', 'PhanUndeclaredProperty'],

View File

@@ -1,6 +1,7 @@
<?php
/* Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
* Copyright (C) 2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2025 MDW <mdeweerd@users.noreply.github.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -30,7 +31,7 @@
* @property string $error
* @property string $table_element
* @property array<string,mixed> $context
* @method int call_trigger(string $triggerName, User $user)
* @method int call_trigger(string $triggerName, ?User $user)
*/
trait CommonSignedObject
{

View File

@@ -76,8 +76,8 @@ trait CommonTrigger
* NB: Error from trigger are stacked in interface->errors
* NB2: If return code of triggers are < 0, action calling trigger should cancel all transaction.
*
* @param string $triggerName trigger's name to execute
* @param User $user Object user
* @param string $triggerName Trigger's name to execute
* @param ?User $user Object user
* @return int Result of run_triggers
*/
public function call_trigger($triggerName, $user)

View File

@@ -613,6 +613,7 @@ class Conf extends stdClass
}
if (!empty($newvalue)) {
// @phan-suppress-next-line PhanTypeMismatchArgumentNullableInternal,PhanTypeMismatchProperty
$this->modules_parts[$partname] = array_merge($this->modules_parts[$partname], array($modulename => $newvalue)); // $value may be a string or an array
}
} elseif (preg_match('/^MAIN_MODULE_([0-9A-Z_]+)$/i', $key, $reg)) {

View File

@@ -145,13 +145,13 @@ class Form
* @param string $text Text of label or key to translate
* @param string $htmlname Name of select field ('edit' prefix will be added)
* @param string $preselected Value to show/edit (not used in this function)
* @param object $object Object (on the page we show)
* @param ?object $object Object (on the page we show)
* @param int<0,1>|boolean $perm Permission to allow button to edit parameter. Set it to 0 to have a not edited field.
* @param string $typeofdata Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols', 'datepicker' ('day' do not work, don't know why), 'dayhour' or 'datehourpicker' 'checkbox:ckeditor:dolibarr_zzz:width:height:savemethod:1:rows:cols', 'select;xxx[:class]'...)
* @param string $moreparam More param to add on a href URL.
* @param int<0,1> $fieldrequired 1 if we want to show field as mandatory using the "fieldrequired" CSS.
* @param int<0,3> $notabletag 1=Do not output table tags but output a ':', 2=Do not output table tags and no ':', 3=Do not output table tags but output a ' '
* @param string $paramid Key of parameter for id ('id', 'socid')
* @param 'id'|'socid'|'projectid' $paramid Key of parameter for id ('id', 'socid')
* @param string $help Tooltip help
* @return string HTML edit field
*/
@@ -215,7 +215,7 @@ class Form
if (empty($notabletag) && $perm) {
$ret .= '<td class="right">';
}
if ($htmlname && GETPOST('action', 'aZ09') != 'edit' . $htmlname && $perm) {
if ($htmlname && GETPOST('action', 'aZ09') != 'edit' . $htmlname && $perm && is_object($object)) {
$ret .= '<a class="editfielda reposition" href="' . dolBuildUrl($_SERVER["PHP_SELF"], ['action' => 'edit' . $htmlname, $paramid => $object->id], true) . $moreparam . '">';
$ret .= img_edit($langs->trans('Edit'), ($notabletag ? 0 : 1));
$ret .= '</a>';
@@ -9142,7 +9142,7 @@ class Form
* Output html form to select an object.
* Note, this function is called by selectForForms or by ajax selectobject.php
*
* @param Object $objecttmp Object to know the table to scan for combo.
* @param CommonObject $objecttmp Object to know the table to scan for combo.
* @param string $htmlname Name of HTML select component
* @param int $preselectedvalue Preselected value (ID of element)
* @param string|int<0,1> $showempty ''=empty values not allowed, 'string'=value show if we allow empty values (for example 'All', ...)
@@ -9185,7 +9185,7 @@ class Form
}
} else {
// For backward compatibility
$objecttmp->fields['ref'] = array('type' => 'varchar(30)', 'label' => 'Ref', 'showoncombobox' => 1);
$objecttmp->fields['ref'] = array('type' => 'varchar(30)', 'label' => 'Ref', 'enabled' => 1, 'position' => 10, 'visible' => 4, 'showoncombobox' => 1);
}
if (empty($fieldstoshow)) {
@@ -9219,6 +9219,7 @@ class Form
$sql .= " LEFT JOIN " . $this->db->prefix() . $this->db->sanitize($objecttmp->table_element) . "_extrafields as e ON t.rowid = e.fk_object";
}
if (!empty($objecttmp->parent_element)) { // If parent_element is defined
'@phan-var-force CommonObjectLine $objecttmp';
$parent_properties = getElementProperties($objecttmp->parent_element);
$sql .= " INNER JOIN " . $this->db->prefix() . $this->db->sanitize($parent_properties['table_element']) . " as o ON o.rowid = t.".$objecttmp->fk_parent_attribute;
}
@@ -11055,32 +11056,34 @@ class Form
$email = $object->email;
} elseif ($modulepart == 'contact') {
$dir = $conf->societe->multidir_output[$entity] . '/contact';
if (!empty($object->photo)) {
if (dolIsAllowedForPreview($object->photo)) {
$photo = $object->photo; // Copy to help static analysis
if (!empty($photo)) {
if (dolIsAllowedForPreview($photo)) {
if ((string) $imagesize == 'mini') {
$file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($object->photo, '_mini');
$file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($photo, '_mini');
} elseif ((string) $imagesize == 'small') {
$file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($object->photo, '_small');
$file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($photo, '_small');
} else {
$file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $object->photo;
$file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $photo;
}
$originalfile = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $object->photo;
$originalfile = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $photo;
}
}
$email = $object->email;
$capture = 'user';
} elseif ($modulepart == 'userphoto') {
$dir = $conf->user->dir_output;
if (!empty($object->photo)) {
if (dolIsAllowedForPreview($object->photo)) {
$photo = $object->photo; // Copy to help static analysis
if (!empty($photo)) {
if (dolIsAllowedForPreview($photo)) {
if ((string) $imagesize == 'mini') {
$file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . getImageFileNameForSize($object->photo, '_mini');
$file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . getImageFileNameForSize($photo, '_mini');
} elseif ((string) $imagesize == 'small') {
$file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . getImageFileNameForSize($object->photo, '_small');
$file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . getImageFileNameForSize($photo, '_small');
} else {
$file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . $object->photo;
$file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . $photo;
}
$originalfile = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . $object->photo;
$originalfile = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . $photo;
}
}
if (getDolGlobalString('MAIN_OLD_IMAGE_LINKS')) {
@@ -11090,16 +11093,17 @@ class Form
$capture = 'user';
} elseif ($modulepart == 'memberphoto') {
$dir = $conf->adherent->dir_output;
if (!empty($object->photo)) {
if (dolIsAllowedForPreview($object->photo)) {
$photo = $object->photo; // Copy to help static analysis
if (!empty($photo)) {
if (dolIsAllowedForPreview($photo)) {
if ((string) $imagesize == 'mini') {
$file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($object->photo, '_mini');
$file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($photo, '_mini');
} elseif ((string) $imagesize == 'small') {
$file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($object->photo, '_small');
$file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($photo, '_small');
} else {
$file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $object->photo;
$file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $photo;
}
$originalfile = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $object->photo;
$originalfile = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $photo;
}
}
if (getDolGlobalString('MAIN_OLD_IMAGE_LINKS')) {

View File

@@ -3474,7 +3474,7 @@ function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldi
$object->totaldeposits = $object->getSumDepositsUsed(0);
$object->alreadypaid = $object->totalpaid + $object->totalcreditnotes + $object->totaldeposits;
}
$tmptxt = $object->getLibStatut(6, $object->alreadypaid);
$tmptxt = $object->getLibStatut(6, (float) $object->alreadypaid);
if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
$tmptxt = $object->getLibStatut(5, (float) $object->alreadypaid);
}
@@ -5219,7 +5219,7 @@ function dolGetFirstLetters($s, $nbofchar = 1)
/**
* Make a strlen call. Works even if mbstring module not enabled
*
* @param string $string String to calculate length
* @param ?string $string String to calculate length
* @param string $stringencoding Encoding of string
* @return int Length of string
*/
@@ -10362,7 +10362,8 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null,
$liste_factures[] = ' - '.$outputlangs->trans('Invoice').' '. $objp->ref.' '.$outputlangs->trans('AmountPayed').' '.price($objp->multicurrency_amount, 0, $outputlangs, 0, -1, -1, $objp->multicurrency_code);
}
}
$substitutionarray['__SUPPLIER_PAYMENT_INVOICES_LIST__'] = implode("\n", $liste_factures);;
$substitutionarray['__SUPPLIER_PAYMENT_INVOICES_LIST__'] = implode("\n", $liste_factures);
;
$substitutionarray['__SUPPLIER_PAYMENT_INVOICES_TOTAL__'] = price($object->multicurrency_amount, 0, $outputlangs, 0, -1, -1, $object->multicurrency_code ? $object->multicurrency_code : $conf->currency);
}
if (is_object($object) && $object->element == 'shipping') {
@@ -11203,6 +11204,7 @@ function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sen
// Add other keys
if (!empty($tmpmultikey[1])) {
$newindex = $tmpmultikey[1];
// @phan-suppress-next-line PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
$temp[$key] .= '__' . (empty($array[$key][$newindex]) ? 0 : $array[$key][$newindex]);
}
}
@@ -11243,7 +11245,7 @@ function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sen
/**
* Check if a string is in UTF8. Seems similar to utf8_valid() but in pure PHP.
*
* @param string $str String to check
* @param null|string|int $str String to check
* @return boolean True if string is UTF8 or ISO compatible with UTF8, False if not (ISO with special non utf8 char or Binary)
* @see utf8_valid()
*/
@@ -14187,7 +14189,6 @@ function dolGetButtonAction($label, $text = '', $actionType = 'default', $url =
*
* @return array<string, string> An array where each key corresponds to the attribute name
* and each value is a full `key="escaped_value"` string ready for HTML output.
*
*/
function commonHtmlAttributeBuilder($attr, array $unescapedAttr = [])
{
@@ -14199,7 +14200,9 @@ function commonHtmlAttributeBuilder($attr, array $unescapedAttr = [])
foreach ($attr as $key => $value) {
// special boolean attributes case
if (in_array($key, getListOfHtmlBooleanAttributes())) {
if ($value) { $TCompiledAttr[$key] = $key; }
if ($value) {
$TCompiledAttr[$key] = $key;
}
continue;
}

View File

@@ -1005,7 +1005,7 @@ class pdf_squille extends ModelePdfReception
//$linkedobject->fetchObjectLinked() Get all linked object to the $linkedobject (commonly order) into $linkedobject->linkedObjects
$pdf->SetFont('', '', $default_font_size - 2);
$text = $linkedobject->ref;
$text = (string) $linkedobject->ref;
if (isset($linkedobject->ref_client) && !empty($linkedobject->ref_client)) {
$text .= ' ('.$linkedobject->ref_client.')';
}

View File

@@ -59,6 +59,8 @@ if (empty($object) || !is_object($object)) {
'
@phan-var-force CommonObject|Facture $this
@phan-var-force CommonObject $object
@phan-var-force CommonObjectLine $line
@phan-var-force ExtraFields $extrafields
@phan-var-force Societe $buyer
@phan-var-force Societe $seller
@phan-var-force int<0,1> $usehm

View File

@@ -798,8 +798,8 @@ class EcmDirectory extends CommonObject
* NB2: if trigger fail, action should be canceled.
* NB3: Should be deleted if EcmDirectory extend CommonObject
*
* @param string $triggerName trigger's name to execute
* @param User $user Object user
* @param string $triggerName Trigger's name to execute
* @param ?User $user Object user
* @return int Result of run_triggers
*/
public function call_trigger($triggerName, $user)