Fix: Enforce/use int nature of BookKeeping(Line)::$piece_num, fix hooks typings (#29298)

* Fix: Enforce/use int nature of BookKeeping(Line)::$piece_num

* Fix typing in hookmanager class

* Improve DolHooksCollector phpdoc

* Fix PhanTypePossiblyInvalidDimOffset by testing index's existence

* Ignore PhanTypePossiblyInvalidDimOffset that phan can not detect

* Allow  to be null, initialise instead of unset
This commit is contained in:
MDW 2024-04-09 23:21:28 +02:00 committed by GitHub
parent 55ae5ccedd
commit bec5ff4749
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 71 additions and 38 deletions

View file

@ -4,6 +4,7 @@
* Copyright (C) 2013-2024 Alexandre Spangaro <aspangaro@easya.solutions>
* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2018-2020 Frédéric France <frederic.france@netlogic.fr>
* Copyright (C) 2024 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
@ -227,7 +228,7 @@ if (empty($reshook)) {
$object = new BookKeeping($db);
$result = $object->fetch($id, null, $mode);
$piece_num = $object->piece_num;
$piece_num = (int) $object->piece_num;
if ($result < 0) {
setEventMessages($object->error, $object->errors, 'errors');
@ -284,7 +285,7 @@ if (empty($reshook)) {
}
$action = '';
$id = $object->id;
$piece_num = $object->piece_num;
$piece_num = (int) $object->piece_num;
}
}
}
@ -438,7 +439,7 @@ if ($action == 'create') {
$head = array();
$h = 0;
$head[$h][0] = $_SERVER['PHP_SELF'].'?piece_num='.$object->piece_num.($mode ? '&mode='.$mode : '');
$head[$h][0] = $_SERVER['PHP_SELF'].'?piece_num='.((int) $object->piece_num).($mode ? '&mode='.$mode : '');
$head[$h][1] = $langs->trans("Transaction");
$head[$h][2] = 'transaction';
$h++;
@ -467,14 +468,14 @@ if ($action == 'create') {
if ($action != 'editdate') {
print '<td class="right">';
if ($permissiontoadd) {
print '<a class="editfielda reposition" href="'.$_SERVER["PHP_SELF"].'?action=editdate&token='.newToken().'&piece_num='.urlencode((string) $object->piece_num).'&mode='.urlencode((string) $mode).'">'.img_edit($langs->transnoentitiesnoconv('SetDate'), 1).'</a>';
print '<a class="editfielda reposition" href="'.$_SERVER["PHP_SELF"].'?action=editdate&token='.newToken().'&piece_num='.((int) $object->piece_num).'&mode='.urlencode((string) $mode).'">'.img_edit($langs->transnoentitiesnoconv('SetDate'), 1).'</a>';
}
print '</td>';
}
print '</tr></table>';
print '</td><td colspan="3">';
if ($action == 'editdate') {
print '<form name="setdate" action="'.$_SERVER["PHP_SELF"].'?piece_num='.urlencode((string) $object->piece_num).'" method="POST">';
print '<form name="setdate" action="'.$_SERVER["PHP_SELF"].'?piece_num='.((int) $object->piece_num).'" method="POST">';
if ($optioncss != '') {
print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
}
@ -498,14 +499,14 @@ if ($action == 'create') {
if ($action != 'editjournal') {
print '<td class="right">';
if ($permissiontoadd) {
print '<a class="editfielda reposition" href="'.$_SERVER["PHP_SELF"].'?action=editjournal&token='.newToken().'&piece_num='.urlencode((string) $object->piece_num).'&mode='.urlencode((string) $mode).'">'.img_edit($langs->transnoentitiesnoconv('Edit'), 1).'</a>';
print '<a class="editfielda reposition" href="'.$_SERVER["PHP_SELF"].'?action=editjournal&token='.newToken().'&piece_num='.((int) $object->piece_num).'&mode='.urlencode((string) $mode).'">'.img_edit($langs->transnoentitiesnoconv('Edit'), 1).'</a>';
}
print '</td>';
}
print '</tr></table>';
print '</td><td>';
if ($action == 'editjournal') {
print '<form name="setjournal" action="'.$_SERVER["PHP_SELF"].'?piece_num='.urlencode((string) $object->piece_num).'" method="POST">';
print '<form name="setjournal" action="'.$_SERVER["PHP_SELF"].'?piece_num='.((int) $object->piece_num).'" method="POST">';
if ($optioncss != '') {
print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
}
@ -529,14 +530,14 @@ if ($action == 'create') {
if ($action != 'editdocref') {
print '<td class="right">';
if ($permissiontoadd) {
print '<a class="editfielda reposition" href="'.$_SERVER["PHP_SELF"].'?action=editdocref&token='.newToken().'&piece_num='.urlencode((string) $object->piece_num).'&mode='.urlencode((string) $mode).'">'.img_edit($langs->transnoentitiesnoconv('Edit'), 1).'</a>';
print '<a class="editfielda reposition" href="'.$_SERVER["PHP_SELF"].'?action=editdocref&token='.newToken().'&piece_num='.((int) $object->piece_num).'&mode='.urlencode((string) $mode).'">'.img_edit($langs->transnoentitiesnoconv('Edit'), 1).'</a>';
}
print '</td>';
}
print '</tr></table>';
print '</td><td>';
if ($action == 'editdocref') {
print '<form name="setdocref" action="'.$_SERVER["PHP_SELF"].'?piece_num='.urlencode((string) $object->piece_num).'" method="POST">';
print '<form name="setdocref" action="'.$_SERVER["PHP_SELF"].'?piece_num='.((int) $object->piece_num).'" method="POST">';
if ($optioncss != '') {
print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
}
@ -664,7 +665,7 @@ if ($action == 'create') {
// List of movements
print load_fiche_titre($langs->trans("ListeMvts"), '', '');
print '<form action="'.$_SERVER["PHP_SELF"].'?piece_num='.$object->piece_num.'" method="post">';
print '<form action="'.$_SERVER["PHP_SELF"].'?piece_num='.((int) $object->piece_num).'" method="post">';
if ($optioncss != '') {
print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
}
@ -787,7 +788,7 @@ if ($action == 'create') {
print '<td class="center nowraponall">';
if ($permissiontoadd) {
if (empty($line->date_export) && empty($line->date_validation)) {
print '<a class="editfielda reposition" href="' . $_SERVER["PHP_SELF"] . '?action=update&id=' . $line->id . '&piece_num=' . urlencode($line->piece_num) . '&mode=' . urlencode((string) $mode) . '&token=' . urlencode(newToken()) . '">';
print '<a class="editfielda reposition" href="' . $_SERVER["PHP_SELF"] . '?action=update&id=' . $line->id . '&piece_num=' . ((int) $line->piece_num) . '&mode=' . urlencode((string) $mode) . '&token=' . urlencode(newToken()) . '">';
print img_edit('', 0, 'class="marginrightonly"');
print '</a> &nbsp;';
} else {
@ -802,7 +803,7 @@ if ($action == 'create') {
$actiontodelete = 'confirm_delete';
}
print '<a href="' . $_SERVER["PHP_SELF"] . '?action=' . $actiontodelete . '&id=' . $line->id . '&piece_num=' . urlencode($line->piece_num) . '&mode=' . urlencode((string) $mode) . '&token=' . urlencode(newToken()) . '">';
print '<a href="' . $_SERVER["PHP_SELF"] . '?action=' . $actiontodelete . '&id=' . $line->id . '&piece_num=' . ((int) $line->piece_num) . '&mode=' . urlencode((string) $mode) . '&token=' . urlencode(newToken()) . '">';
print img_delete();
print '</a>';
} else {
@ -830,7 +831,7 @@ if ($action == 'create') {
print '<br>';
print '<div class="center">';
if ($total_debit == $total_credit) {
print '<a class="button" href="'.$_SERVER["PHP_SELF"].'?piece_num='.$object->piece_num.'&action=valid">'.$langs->trans("ValidTransaction").'</a>';
print '<a class="button" href="'.$_SERVER["PHP_SELF"].'?piece_num='.((int) $object->piece_num).'&action=valid">'.$langs->trans("ValidTransaction").'</a>';
} else {
print '<input type="submit" class="button" disabled="disabled" href="#" title="'.dol_escape_htmltag($langs->trans("MvtNotCorrectlyBalanced", $debit, $credit)).'" value="'.dol_escape_htmltag($langs->trans("ValidTransaction")).'">';
}

View file

@ -3151,6 +3151,9 @@ class BookKeepingLine extends CommonObjectLine
public $import_key;
public $code_journal;
public $journal_label;
/**
* @var int accounting transaction id
*/
public $piece_num;
/**

View file

@ -1,5 +1,6 @@
<?php
/* Copyright (C) 2023 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2024 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
@ -28,7 +29,7 @@
abstract class CommonHookActions
{
/**
* @var string String of results.
* @var ?string String of results.
*/
public $resprints;

View file

@ -2,6 +2,7 @@
/* Copyright (C) 2010-2016 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2010-2014 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2010-2011 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2024 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
@ -44,28 +45,41 @@ class HookManager
*/
public $errors = array();
// Context hookmanager was created for ('thirdpartycard', 'thirdpartydao', ...)
/**
* @var string[] Context hookmanager was created for ('thirdpartycard', 'thirdpartydao', ...)
*/
public $contextarray = array();
// Array with instantiated classes
/**
* array<string,array<string,null|string|CommonHookActions>> Array with instantiated classes
*/
public $hooks = array();
/**
* @var array List of hooks called during this request
* @var array<string,array{name:string,contexts:string[],file:string,line:string,count:int}> List of hooks called during this request (key = hash)
*/
public $hooksHistory = [];
// Array result
/**
* @var mixed[] Result
*/
public $resArray = array();
// Printable result
/**
* @var string Printable result
*/
public $resPrint = '';
// Nb of qualified hook ran
/**
* @var int Nb of qualified hook ran
*/
public $resNbOfHooks = 0;
/**
* Constructor
*
* @param DoliDB $db Database handler
* @return void
*/
public function __construct($db)
{
@ -82,7 +96,7 @@ class HookManager
* Then when a hook executeHooks('aMethod'...) is called, the method aMethod found into class will be executed.
*
* @param string[] $arraycontext Array list of searched hooks tab/features. For example: 'thirdpartycard' (for hook methods into page card thirdparty), 'thirdpartydao' (for hook methods into Societe), ...
* @return int 0 or 1
* @return int<0,1> 0 or 1
*/
public function initHooks($arraycontext)
{
@ -125,6 +139,7 @@ class HookManager
if ($resaction) {
$controlclassname = 'Actions'.ucfirst($module);
$actionInstance = new $controlclassname($this->db);
'@phan-var-force CommonHookActions $actionInstance';
$priority = empty($actionInstance->priority) ? 50 : $actionInstance->priority;
$this->hooks[$context][$priority.':'.$module] = $actionInstance;
}
@ -150,10 +165,10 @@ class HookManager
* Execute hooks (if they were initialized) for the given method
*
* @param string $method Name of method hooked ('doActions', 'printSearchForm', 'showInputField', ...)
* @param array $parameters Array of parameters
* @param Object $object Object to use hooks on
* @param array<string,mixed> $parameters Array of parameters
* @param object $object Object to use hooks on
* @param string $action Action code on calling page ('create', 'edit', 'view', 'add', 'update', 'delete'...)
* @return int For 'addreplace' hooks (doActions, formConfirm, formObjectOptions, pdf_xxx,...): Return 0 if we want to keep standard actions, >0 if we want to stop/replace standard actions, <0 if KO. Things to print are returned into ->resprints and set into ->resPrint. Things to return are returned into ->results by hook and set into ->resArray for caller.
* @return int<-1,1> For 'addreplace' hooks (doActions, formConfirm, formObjectOptions, pdf_xxx,...): Return 0 if we want to keep standard actions, >0 if we want to stop/replace standard actions, <0 if KO. Things to print are returned into ->resprints and set into ->resPrint. Things to return are returned into ->results by hook and set into ->resArray for caller.
* For 'output' hooks (printLeftBlock, formAddObjectLine, formBuilddocOptions, ...): Return 0 if we want to keep standard actions, >0 uf we want to stop/replace standard actions (at least one > 0 and replacement will be done), <0 if KO. Things to print are returned into ->resprints and set into ->resPrint. Things to return are returned into ->results by hook and set into ->resArray for caller.
* All types can also return some values into an array ->results that will be merged into this->resArray for caller.
* $this->error or this->errors are also defined by class called by this function if error.
@ -170,6 +185,7 @@ class HookManager
'contexts' => $this->contextarray,
'file' => $trace[0]['file'],
'line' => $trace[0]['line'],
'count' => 0,
];
$hash = md5(json_encode($hookInformations));
if (!empty($this->hooksHistory[$hash])) {
@ -245,6 +261,7 @@ class HookManager
$error = 0;
foreach ($this->hooks as $context => $modules) { // $this->hooks is an array with context as key and value is an array of modules that handle this context
if (!empty($modules)) {
'@phan-var-force array<string,CommonHookActions> $modules';
// Loop on each active hooks of module for this context
foreach ($modules as $module => $actionclassinstance) {
$module = preg_replace('/^\d+:/', '', $module);
@ -278,6 +295,7 @@ class HookManager
$parameters['currentcontext'] = $context;
// Hooks that must return int (hooks with type 'addreplace')
if ($hooktype == 'addreplace') {
// @phan-suppress-next-line PhanUndeclaredMethod The method's existence is tested above.
$resactiontmp = $actionclassinstance->$method($parameters, $object, $action, $this); // $object and $action can be changed by method ($object->id during creation for example or $action to go back to other action for example)
$resaction += $resactiontmp;
@ -298,9 +316,9 @@ class HookManager
if (!empty($actionclassinstance->resprints)) {
if ($resactiontmp > 0) {
$localResPrint = $actionclassinstance->resprints;
$localResPrint = (string) $actionclassinstance->resprints;
} else {
$localResPrint .= $actionclassinstance->resprints;
$localResPrint .= (string) $actionclassinstance->resprints;
}
}
} else {
@ -315,6 +333,7 @@ class HookManager
dol_syslog("Call method ".$method." of class ".get_class($actionclassinstance).", module=".$module.", hooktype=".$hooktype, LOG_DEBUG);
}
// @phan-suppress-next-line PhanUndeclaredMethod The method's existence is tested above.
$resactiontmp = $actionclassinstance->$method($parameters, $object, $action, $this); // $object and $action can be changed by method ($object->id during creation for example or $action to go back to other action for example)
$resaction += $resactiontmp;
@ -322,7 +341,7 @@ class HookManager
$localResArray = array_merge_recursive($localResArray, $actionclassinstance->results);
}
if (!empty($actionclassinstance->resprints)) {
$localResPrint .= $actionclassinstance->resprints;
$localResPrint .= (string) $actionclassinstance->resprints;
}
if (is_numeric($resactiontmp) && $resactiontmp < 0) {
$error++;
@ -342,8 +361,8 @@ class HookManager
//print "After hook context=".$context." ".get_class($actionclassinstance)." method=".$method." hooktype=".$hooktype." results=".count($actionclassinstance->results)." resprints=".count($actionclassinstance->resprints)." resaction=".$resaction."<br>\n";
unset($actionclassinstance->results);
unset($actionclassinstance->resprints);
$actionclassinstance->results = array();
$actionclassinstance->resprints = null;
}
}
}

View file

@ -999,7 +999,7 @@ class pdf_vinci extends ModelePDFMo
$pdf->line($colDef['xStartPos'], $tab_top, $colDef['xStartPos'], $tab_top + $tab_height);
}
if (empty($hidetop)) {
if (empty($hidetop) && array_key_exists('title', $colDef) && array_key_exists('width', $colDef)) {
$pdf->SetXY($colDef['xStartPos'] + $colDef['title']['padding'][3], $tab_top + $colDef['title']['padding'][0]);
$textWidth = $colDef['width'] - $colDef['title']['padding'][3] - $colDef['title']['padding'][1];

View file

@ -1,5 +1,6 @@
<?php
/* Copyright (C) 2024 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2024 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
@ -23,7 +24,6 @@
use DebugBar\DataCollector\RequestDataCollector;
/**
* DolRequestDataCollector class
*/
@ -32,7 +32,7 @@ class DolHooksCollector extends RequestDataCollector
/**
* Collects the data from the collectors
*
* @return array Array of collected data
* @return array{nb_of_hooks:int,hooks:array<string,array{contexts:string}>} Array of collected data
*/
public function collect()
{
@ -41,7 +41,7 @@ class DolHooksCollector extends RequestDataCollector
*/
global $hookmanager;
$data = ['hooks' => []];
$data = ['hooks' => [], 'nb_of_hooks' => 0];
if (empty($hookmanager->hooksHistory)) {
return $data;
}
@ -62,7 +62,7 @@ class DolHooksCollector extends RequestDataCollector
/**
* Return widget settings
*
* @return array Array
* @return array<string,array{icon?:string,widget?:string,map:string,default:int|string}> Array
*/
public function getWidgets()
{

View file

@ -5,6 +5,7 @@
* Copyright (C) 2011-2012 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2015 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2021 Frédéric France <frederic.france@netlogic.fr>
* Copyright (C) 2024 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
@ -43,9 +44,15 @@ $hookmanager->initHooks(array('index'));
*/
$nbmodulesnotautoenabled = count($conf->modules);
if (in_array('fckeditor', $conf->modules)) $nbmodulesnotautoenabled--;
if (in_array('export', $conf->modules)) $nbmodulesnotautoenabled--;
if (in_array('import', $conf->modules)) $nbmodulesnotautoenabled--;
if (in_array('fckeditor', $conf->modules)) {
$nbmodulesnotautoenabled--;
}
if (in_array('export', $conf->modules)) {
$nbmodulesnotautoenabled--;
}
if (in_array('import', $conf->modules)) {
$nbmodulesnotautoenabled--;
}
// Check if company name is defined (first install)
if (!getDolGlobalString('MAIN_INFO_SOCIETE_NOM') || !getDolGlobalString('MAIN_INFO_SOCIETE_COUNTRY')) {
@ -522,6 +529,7 @@ if (!getDolGlobalString('MAIN_DISABLE_GLOBAL_WORKBOARD') && getDolGlobalInt('MAI
if (!empty($groupElement['lang'])) {
$langs->load($groupElement['lang']);
}
// @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
$groupName = $langs->trans($groupElement['groupName']);
$groupKeyLowerCase = strtolower($groupKey);

View file

@ -1,5 +1,6 @@
<?php
/* Copyright (C) ---Put here your own copyright and developer email---
* Copyright (C) 2024 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
@ -52,7 +53,7 @@ class ActionsMyModule extends CommonHookActions
public $results = array();
/**
* @var string String displayed by executeHook() immediately after return
* @var ?string String displayed by executeHook() immediately after return
*/
public $resprints;