Fix CI #39247: Fix phan notices by reorganising code (#39264)

* Fix #39247: Fix phan notices by reorganising code

# Fix #39247: Fix phan notices by reorganising code

Phan typing analysis was disturbed by conditional assignment to array fields of array items.

This reorganises the the code and uses a foreach and a loop item by reference.
The result array is filtered after the initial loop to find matching ECM items.

Not that this does not fix an unreported issue when paging is applied for filtered items
(paging is applied on the full list, not the filtered items and may result in small(er)
or empty pages).

* Qual: Fix Phan notices

# Qual: Fix Phan notices

- The page_count value was simply ceil(...) which is typed float, now casting to int.
- Add missing properties share and agenda_id to EcmFilesLine
This commit is contained in:
MDW 2026-07-23 16:09:22 +02:00 committed by GitHub
parent 68297cad59
commit cbf8748dd2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 67 additions and 47 deletions

View file

@ -5,7 +5,7 @@
* Copyright (C) 2016 Jean-François Ferry <jfefe@aternatik.fr>
* Copyright (C) 2023 Romain Neil <contact@romain-neil.fr>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2025 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2025-2026 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2025 William Mead <william@m34d.com>
* Copyright (C) 2025-2026 Charlene Benke <charlene@patas-monkey.com>
*
@ -449,7 +449,7 @@ class Documents extends DolibarrApi
* @param string $content_type Filter on content-type (example 'application/pdf' or 'application/pdf,image/jpeg'))
* @param bool $pagination_data If this parameter is set to true the response will include pagination data. Default value is false. Page starts from 0*
* @return array Array of documents with path
* @phan-return array<array<string,int|string>>
* @phan-return array{data:array<mixed,array{name:string,path:string,level1name:string,relativename:string,fullname:string,date:string,size:int,perm:int,type:string}>,pagination:array{total:int,page:int,page_count:int,limit:int}}|array<mixed,array{name:string,path:string,level1name:string,relativename:string,fullname:string,date:string,size:int,perm:int,type:string}>
* @phpstan-return array<array<string,int|string>>
*
* @url GET /
@ -458,7 +458,7 @@ class Documents extends DolibarrApi
* @throws RestException 403 Access denied
* @throws RestException 404 Object (Thirdparty, User, Member, Order, Invoice, Proposal...) not found
* @throws RestException 500 Error while fetching object
* @throws RestException 503 Error when retrieve ecm list
* @throws RestException 503 Error when retrieving ecm list
*/
public function getDocumentsListByElement($modulepart, $id = 0, $ref = '', $sortfield = '', $sortorder = '', $limit = 100, $page = 0, $content_type = '', $pagination_data = false)
{
@ -612,16 +612,14 @@ class Documents extends DolibarrApi
$objectType = $object->table_element;
}
$filearraytmp = dol_dir_list($upload_dir, $type, $recursive, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ? SORT_DESC : SORT_ASC), 1);
$filearray = dol_dir_list($upload_dir, $type, $recursive, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ? SORT_DESC : SORT_ASC), 1);
$countarray = is_array($filearray) ? count($filearray) : 0;
$filearray = $filearraytmp; // We store answer into an array that we will extends with ecm data
/** @var array<array{name:string,path:string,level1name:string,relativename:string,fullname:string,date:string,size:int,perm:int,type:string,ref:string,label:string,filepath:string,filename:string,fullpath_orig:string,position:int,gen_or_uploaded:int,description:string,keywords:string,cover:int,share:int,date_c:string,agenda_id:int,fk_user_c:int,fk_user_m:int,note_private:string,note_public:string,content-type:string}> $filearray */
'@phan-var-force array<array{name:string,path:string,level1name:string,relativename:string,fullname:string,date:string,size:int,perm:int,type:string,ref:string,label:string,filepath:string,filename:string,fullpath_orig:string,position:int,gen_or_uploaded:int,description:string,keywords:string,cover:int,share:int,date_c:string,agenda_id:int,fk_user_c:int,fk_user_m:int,note_private:string,note_public:string,content-type:string}> $filearray';
$countarray = count($filearray);
$filearray = array_slice($filearray, $limit * $page, $limit);
if (empty($filearray)) {
throw new RestException(404, 'Search for modulepart '.$modulepart.' with Id '.$object->id.(!empty($object->ref) ? ' or Ref '.$object->ref : '').' does not return any document.');
} else {
$filearray = array_slice($filearray, $limit * $page, $limit);
if (($object->id) > 0 && !empty($modulepart)) {
require_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
$ecmfile = new EcmFiles($this->db);
@ -629,54 +627,63 @@ class Documents extends DolibarrApi
if ($result < 0) {
throw new RestException(503, 'Error when retrieve ecm list : '.$this->db->lasterror());
} elseif (is_array($ecmfile->lines) && count($ecmfile->lines) > 0) {
$count = count($filearray);
for ($i = 0 ; $i < $count ; $i++) {
foreach ($filearray as &$fileitem) {
foreach ($ecmfile->lines as $line) {
if ($filearray[$i]['name'] == $line->filename) {
if ($fileitem['name'] == $line->filename) {
// Next line converts EcmFilesLine properties to array
//$filearray[$i] = array_merge($filearray[$i], (array) $line);
$filearray[$i]['ref'] = $line->ref;
$filearray[$i]['label'] = $line->label;
$filearray[$i]['filepath'] = $line->filepath;
$filearray[$i]['filename'] = $line->filename;
$filearray[$i]['fullpath_orig'] = $line->fullpath_orig;
$filearray[$i]['position'] = $line->position;
$filearray[$i]['gen_or_uploaded'] = $line->gen_or_uploaded;
$filearray[$i]['description'] = $line->desc;
$filearray[$i]['keywords'] = $line->keywords;
$filearray[$i]['cover'] = $line->cover;
$filearray[$i]['share'] = $line->share;
$filearray[$i]['date_c'] = $line->date_c;
$filearray[$i]['agenda_id'] = $line->agenda_id;
$filearray[$i]['fk_user_c'] = $line->fk_user_c;
$filearray[$i]['fk_user_m'] = $line->fk_user_m;
$filearray[$i]['note_private'] = $line->note_private;
$filearray[$i]['note_public'] = $line->note_public;
//$fileitem = array_merge($fileitem, (array) $line);
$fileitem['ref'] = $line->ref;
$fileitem['label'] = $line->label;
$fileitem['filepath'] = $line->filepath;
$fileitem['filename'] = $line->filename;
$fileitem['fullpath_orig'] = $line->fullpath_orig;
$fileitem['position'] = $line->position;
$fileitem['gen_or_uploaded'] = $line->gen_or_uploaded;
$fileitem['description'] = $line->desc;
$fileitem['keywords'] = $line->keywords;
$fileitem['cover'] = $line->cover;
$fileitem['share'] = $line->share;
$fileitem['date_c'] = $line->date_c;
$fileitem['agenda_id'] = $line->agenda_id;
$fileitem['fk_user_c'] = $line->fk_user_c;
$fileitem['fk_user_m'] = $line->fk_user_m;
$fileitem['note_private'] = $line->note_private;
$fileitem['note_public'] = $line->note_public;
}
}
if (isset($filearray[$i]['relativename'])) {
$filearray[$i]['content-type'] = dol_mimetype((string) $filearray[$i]['relativename']);
}
$arraycontenttype = explode(",", $content_type);
if (!empty($content_type) && isset($filearray[$i]['relativename']) && !in_array(dol_mimetype((string) $filearray[$i]['relativename']), $arraycontenttype)) {
unset($filearray[$i]);
$countarray -= 1;
if (isset($fileitem['relativename'])) {
$fileitem['content-type'] = dol_mimetype((string) $fileitem['relativename']);
}
}
// Select only files that match the requested $content_type, if provided
$arraycontenttype = explode(",", $content_type);
if (!empty($arraycontenttype)) {
$filearray = array_filter(
$filearray,
/**
* @param array{name:string,path:string,level1name:string,relativename:string,fullname:string,date:string,size:int,perm:int,type:string,content-type?:string} $fileitem Item to check for content type (for filtering)
* @return bool True if file matches a content type
*/
static function ($fileitem) use (&$arraycontenttype) {
return in_array(($fileitem['content-type'] ?: 'UNKNOWN'), $arraycontenttype);
}
);
}
}
}
}
//if $pagination_data is true the response will contain element data with all values and element pagination with pagination data(total,page,limit)
if ($pagination_data) {
$tmp = $filearray;
$filearray = [];
$filearray['data'] = $tmp;
$filearray['pagination'] = [
'total' => (int) $countarray,
'page' => $page, //count starts from 0
'page_count' => ceil((int) $countarray / $limit),
'limit' => $limit
$filearray = [
'data' => $filearray,
'pagination' => [
'total' => (int) $countarray,
'page' => $page, // count starts from 0
'page_count' => (int) ceil((int) $countarray / $limit),
'limit' => $limit
]
];
}
@ -843,12 +850,12 @@ class Documents extends DolibarrApi
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
$object = new Societe($this->db);
$fetchbyid = true;
} elseif ($modulepart == 'knowledgemanagement' ) {
} elseif ($modulepart == 'knowledgemanagement') {
$modulepart = 'knowledgemanagement';
require_once DOL_DOCUMENT_ROOT.'/knowledgemanagement/class/knowledgerecord.class.php';
$object = new KnowledgeRecord($this->db);
$fetchbyid = true;
} elseif ($modulepart == 'ticket' ) {
} elseif ($modulepart == 'ticket') {
$modulepart = 'ticket';
require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';
$object = new Ticket($this->db);

View file

@ -62,6 +62,11 @@ class EcmFiles extends CommonObject
*/
public $share;
/**
* @var EcmFilesLine[]
*/
public $lines = array();
/**
* @var ?string filename, Note: Into ecm database record, the entry never ends with .noexe
*/
@ -1255,4 +1260,12 @@ class EcmFilesLine extends CommonObjectLine
* @var int
*/
public $src_object_id;
/**
* @var int
*/
public $agenda_id;
/**
* @var ?string Hash for file sharing
*/
public $share;
}