2
0
forked from Wavyzz/dolibarr

Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop

This commit is contained in:
Laurent Destailleur
2024-02-25 23:49:57 +01:00
17 changed files with 370 additions and 130 deletions

View File

@@ -83,6 +83,7 @@ return [
// Alternately, you can pass in the full path to a PHP file
// with the plugin's implementation (e.g. 'vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php')
'plugins' => [
__DIR__.'/plugins/NoVarDumpPlugin.php',
// checks if a function, closure or method unconditionally returns.
// can also be written as 'vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php'
//'DeprecateAliasPlugin',
@@ -149,9 +150,13 @@ return [
'PhanPluginShortArray',
'PhanPluginNumericalComparison',
'PhanPluginUnknownObjectMethodCall',
'PhanPluginCanUseParamType',
'PhanPluginNonBoolInLogicalArith',
'PhanPluginCanUseReturnType',
// Fixers From PHPDocToRealTypesPlugin:
'PhanPluginCanUseParamType', // Fixer - Report/Add types in the function definition (function abc(string $var) (adds string)
'PhanPluginCanUseReturnType', // Fixer - Report/Add return types in the function definition (function abc(string $var) (adds string)
'PhanPluginCanUseNullableParamType', // Fixer - Report/Add nullable parameter types in the function definition
'PhanPluginCanUseNullableReturnType', // Fixer - Report/Add nullable return types in the function definition
// 'PhanPluginNotFullyQualifiedFunctionCall',
'PhanPluginConstantVariableScalar',
// 'PhanPluginNoCommentOnPublicProperty',
@@ -166,7 +171,6 @@ return [
'PhanPluginUnknownArrayMethodReturnType',
'PhanTypeMismatchArgumentInternal',
'PhanPluginDuplicateAdjacentStatement',
'PhanPluginCanUseNullableParamType',
'PhanTypeInvalidLeftOperandOfNumericOp',
'PhanTypeMismatchProperty',
// 'PhanPluginNoCommentOnPublicMethod',
@@ -190,7 +194,6 @@ return [
'PhanTypeInvalidLeftOperandOfAdd',
// 'PhanPluginNoCommentOnPrivateProperty',
// 'PhanPluginNoCommentOnFunction',
'PhanPluginCanUseNullableReturnType',
'PhanPluginUnknownArrayFunctionParamType',
// 'PhanPluginDescriptionlessCommentOnPublicProperty',
'PhanPluginUnknownFunctionParamType',

View File

@@ -83,6 +83,7 @@ return [
// Alternately, you can pass in the full path to a PHP file
// with the plugin's implementation (e.g. 'vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php')
'plugins' => [
__DIR__.'/plugins/NoVarDumpPlugin.php',
'DeprecateAliasPlugin',
//'EmptyMethodAndFunctionPlugin',
'InvalidVariableIssetPlugin',
@@ -102,7 +103,7 @@ return [
'NonBoolBranchPlugin', // Requires test on bool, nont on ints
'NonBoolInLogicalArithPlugin',
'NumericalComparisonPlugin',
'PHPDocToRealTypesPlugin',
// 'PHPDocToRealTypesPlugin', // Report/Add types to function definitions
'PHPDocInWrongCommentPlugin', // Missing /** (/* was used)
//'ShortArrayPlugin', // Checks that [] is used
//'StrictLiteralComparisonPlugin',
@@ -138,10 +139,11 @@ return [
'PhanPluginCanUsePHP71Void', // Dolibarr is maintaining 7.0 compatibility
'PhanPluginShortArray', // Dolibarr uses array()
'PhanPluginShortArrayList', // Dolibarr uses array()
// The following may require that --quick is not used
'PhanPluginCanUseParamType', // Does not seem useful: is reporting types already in PHPDoc?
'PhanPluginCanUseReturnType', // Does not seem useful: is reporting types already in PHPDoc?
'PhanPluginCanUseNullableParamType', // Does not seem useful: is reporting types already in PHPDoc?
// Fixers From PHPDocToRealTypesPlugin:
'PhanPluginCanUseParamType', // Fixer - Report/Add types in the function definition (function abc(string $var) (adds string)
'PhanPluginCanUseReturnType', // Fixer - Report/Add return types in the function definition (function abc(string $var) (adds string)
'PhanPluginCanUseNullableParamType', // Fixer - Report/Add nullable parameter types in the function definition
'PhanPluginCanUseNullableReturnType', // Fixer - Report/Add nullable return types in the function definition
'PhanPluginNonBoolBranch', // Not essential - 31240+ occurrences
'PhanPluginNumericalComparison', // Not essential - 19870+ occurrences
'PhanTypeMismatchArgument', // Not essential - 12300+ occurrences

View File

@@ -0,0 +1,81 @@
<?php
/* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
*/
declare(strict_types=1);
use ast\Node;
use Phan\PluginV3;
use Phan\PluginV3\PluginAwarePostAnalysisVisitor;
use Phan\PluginV3\PostAnalyzeNodeCapability;
/**
* NoVarDumpPlugin hooks into one event:
*
* - getPostAnalyzeNodeVisitorClassName
* This method returns a visitor that is called on every AST node from every
* file being analyzed
*
* A plugin file must
*
* - Contain a class that inherits from \Phan\PluginV3
*
* - End by returning an instance of that class.
*
* It is assumed without being checked that plugins aren't
* mangling state within the passed code base or context.
*
* Note: When adding new plugins,
* add them to the corresponding section of README.md
*/
class NoVarDumpPlugin extends PluginV3 implements PostAnalyzeNodeCapability
{
/**
* @return string - name of PluginAwarePostAnalysisVisitor subclass
*/
public static function getPostAnalyzeNodeVisitorClassName(): string
{
return NoVarDumpVisitor::class;
}
}
/**
* When __invoke on this class is called with a node, a method
* will be dispatched based on the `kind` of the given node.
*
* Visitors such as this are useful for defining lots of different
* checks on a node based on its kind.
*/
class NoVarDumpVisitor extends PluginAwarePostAnalysisVisitor
{
// A plugin's visitors should not override visit() unless they need to.
/**
* @param Node $node A node to analyze
*
* @return void
*
* @override
*/
public function visitCall(Node $node): void
{
$name = $node->children['expr']->children['name'] ?? null;
if (!is_string($name)) {
return;
}
if (strcasecmp($name, 'var_dump') !== 0) {
return;
}
$this->emitPluginIssue(
$this->code_base,
$this->context,
'NoVarDumpPlugin',
'var_dump() should be commented in submitted code',
[]
);
}
}
// Every plugin needs to return an instance of itself at the
// end of the file in which it's defined.
return new NoVarDumpPlugin();

View File

@@ -106,13 +106,14 @@ if (($action == 'update' && !GETPOST("cancel", 'alpha'))
$db->begin();
dolibarr_set_const($db, "MAIN_INFO_SOCIETE_NOM", GETPOST("nom", 'alphanohtml'), 'chaine', 0, '', $conf->entity);
dolibarr_set_const($db, "MAIN_INFO_SOCIETE_NOM", GETPOST("name", 'alphanohtml'), 'chaine', 0, '', $conf->entity);
dolibarr_set_const($db, "MAIN_INFO_SOCIETE_ADDRESS", GETPOST("MAIN_INFO_SOCIETE_ADDRESS", 'alphanohtml'), 'chaine', 0, '', $conf->entity);
dolibarr_set_const($db, "MAIN_INFO_SOCIETE_TOWN", GETPOST("MAIN_INFO_SOCIETE_TOWN", 'alphanohtml'), 'chaine', 0, '', $conf->entity);
dolibarr_set_const($db, "MAIN_INFO_SOCIETE_ZIP", GETPOST("MAIN_INFO_SOCIETE_ZIP", 'alphanohtml'), 'chaine', 0, '', $conf->entity);
dolibarr_set_const($db, "MAIN_INFO_SOCIETE_REGION", GETPOST("region_code", 'alphanohtml'), 'chaine', 0, '', $conf->entity);
dolibarr_set_const($db, "MAIN_MONNAIE", GETPOST("currency", 'aZ09'), 'chaine', 0, '', $conf->entity);
dolibarr_set_const($db, "MAIN_INFO_SOCIETE_TEL", GETPOST("tel", 'alphanohtml'), 'chaine', 0, '', $conf->entity);
dolibarr_set_const($db, "MAIN_INFO_SOCIETE_TEL", GETPOST("phone", 'alphanohtml'), 'chaine', 0, '', $conf->entity);
dolibarr_set_const($db, "MAIN_INFO_SOCIETE_MOBILE", GETPOST("phone_mobile", 'alphanohtml'), 'chaine', 0, '', $conf->entity);
dolibarr_set_const($db, "MAIN_INFO_SOCIETE_FAX", GETPOST("fax", 'alphanohtml'), 'chaine', 0, '', $conf->entity);
dolibarr_set_const($db, "MAIN_INFO_SOCIETE_MAIL", GETPOST("mail", 'alphanohtml'), 'chaine', 0, '', $conf->entity);
dolibarr_set_const($db, "MAIN_INFO_SOCIETE_WEB", GETPOST("web", 'alphanohtml'), 'chaine', 0, '', $conf->entity);
@@ -440,18 +441,18 @@ print '<tr class="liste_titre"><th class="titlefieldcreate wordbreak">'.$langs->
// Name
print '<tr class="oddeven"><td class="fieldrequired wordbreak"><label for="name">'.$langs->trans("CompanyName").'</label></td><td>';
print '<input name="nom" id="name" class="minwidth200" value="'.dol_escape_htmltag((GETPOSTISSET('nom') ? GETPOST('nom', 'alphanohtml') : (getDolGlobalString('MAIN_INFO_SOCIETE_NOM') ? $conf->global->MAIN_INFO_SOCIETE_NOM : ''))).'"'.(!getDolGlobalString('MAIN_INFO_SOCIETE_NOM') ? ' autofocus="autofocus"' : '').'></td></tr>'."\n";
print '<input name="name" id="name" class="minwidth200" value="'.dol_escape_htmltag((GETPOSTISSET('name') ? GETPOST('name', 'alphanohtml') : (getDolGlobalString('MAIN_INFO_SOCIETE_NOM')))).'"'.(!getDolGlobalString('MAIN_INFO_SOCIETE_NOM') ? ' autofocus="autofocus"' : '').'></td></tr>'."\n";
// Address
print '<tr class="oddeven"><td><label for="MAIN_INFO_SOCIETE_ADDRESS">'.$langs->trans("CompanyAddress").'</label></td><td>';
print '<textarea name="MAIN_INFO_SOCIETE_ADDRESS" id="MAIN_INFO_SOCIETE_ADDRESS" class="quatrevingtpercent" rows="'.ROWS_3.'">'.(GETPOSTISSET('MAIN_INFO_SOCIETE_ADDRESS') ? GETPOST('MAIN_INFO_SOCIETE_ADDRESS', 'alphanohtml') : (getDolGlobalString('MAIN_INFO_SOCIETE_ADDRESS') ? $conf->global->MAIN_INFO_SOCIETE_ADDRESS : '')).'</textarea></td></tr>'."\n";
print '<textarea name="MAIN_INFO_SOCIETE_ADDRESS" id="MAIN_INFO_SOCIETE_ADDRESS" class="quatrevingtpercent" rows="'.ROWS_3.'">'.(GETPOSTISSET('MAIN_INFO_SOCIETE_ADDRESS') ? GETPOST('MAIN_INFO_SOCIETE_ADDRESS', 'alphanohtml') : (getDolGlobalString('MAIN_INFO_SOCIETE_ADDRESS'))).'</textarea></td></tr>'."\n";
// Zip
print '<tr class="oddeven" id="trzipbeforecountry"><td><label for="MAIN_INFO_SOCIETE_ZIP">'.$langs->trans("CompanyZip").'</label></td><td>';
print '<input class="width100" name="MAIN_INFO_SOCIETE_ZIP" id="MAIN_INFO_SOCIETE_ZIP" value="'.dol_escape_htmltag((GETPOSTISSET('MAIN_INFO_SOCIETE_ZIP') ? GETPOST('MAIN_INFO_SOCIETE_ZIP', 'alphanohtml') : (getDolGlobalString('MAIN_INFO_SOCIETE_ZIP') ? $conf->global->MAIN_INFO_SOCIETE_ZIP : ''))).'"></td></tr>'."\n";
print '<input class="width100" name="MAIN_INFO_SOCIETE_ZIP" id="MAIN_INFO_SOCIETE_ZIP" value="'.dol_escape_htmltag((GETPOSTISSET('MAIN_INFO_SOCIETE_ZIP') ? GETPOST('MAIN_INFO_SOCIETE_ZIP', 'alphanohtml') : (getDolGlobalString('MAIN_INFO_SOCIETE_ZIP')))).'"></td></tr>'."\n";
print '<tr class="oddeven" id="trtownbeforecountry"><td><label for="MAIN_INFO_SOCIETE_TOWN">'.$langs->trans("CompanyTown").'</label></td><td>';
print '<input name="MAIN_INFO_SOCIETE_TOWN" class="minwidth200" id="MAIN_INFO_SOCIETE_TOWN" value="'.dol_escape_htmltag((GETPOSTISSET('MAIN_INFO_SOCIETE_TOWN') ? GETPOST('MAIN_INFO_SOCIETE_TOWN', 'alphanohtml') : (getDolGlobalString('MAIN_INFO_SOCIETE_TOWN') ? $conf->global->MAIN_INFO_SOCIETE_TOWN : ''))).'"></td></tr>'."\n";
print '<input name="MAIN_INFO_SOCIETE_TOWN" class="minwidth200" id="MAIN_INFO_SOCIETE_TOWN" value="'.dol_escape_htmltag((GETPOSTISSET('MAIN_INFO_SOCIETE_TOWN') ? GETPOST('MAIN_INFO_SOCIETE_TOWN', 'alphanohtml') : (getDolGlobalString('MAIN_INFO_SOCIETE_TOWN')))).'"></td></tr>'."\n";
// Country
print '<tr class="oddeven"><td class="fieldrequired"><label for="selectcountry_id">'.$langs->trans("Country").'</label></td><td>';
@@ -481,13 +482,19 @@ print '</td></tr>'."\n";
// Phone
print '<tr class="oddeven"><td><label for="phone">'.$langs->trans("Phone").'</label></td><td>';
print img_picto('', 'object_phoning', '', false, 0, 0, '', 'pictofixedwidth');
print '<input class="maxwidth150 widthcentpercentminusx" name="phone" id="phone" value="'.dol_escape_htmltag((GETPOSTISSET('phone') ? GETPOST('phone', 'alphanohtml') : (getDolGlobalString('MAIN_INFO_SOCIETE_TEL') ? $conf->global->MAIN_INFO_SOCIETE_TEL : ''))).'"></td></tr>';
print '<input class="maxwidth150 widthcentpercentminusx" name="phone" id="phone" value="'.dol_escape_htmltag((GETPOSTISSET('phone') ? GETPOST('phone', 'alphanohtml') : (getDolGlobalString('MAIN_INFO_SOCIETE_TEL')))).'"></td></tr>';
print '</td></tr>'."\n";
// Phone mobile
print '<tr class="oddeven"><td><label for="phone">'.$langs->trans("PhoneMobile").'</label></td><td>';
print img_picto('', 'object_phoning_mobile', '', false, 0, 0, '', 'pictofixedwidth');
print '<input class="maxwidth150 widthcentpercentminusx" name="phone_mobile" id="phone_mobile" value="'.dol_escape_htmltag((GETPOSTISSET('phone_mobile') ? GETPOST('phone_mobile', 'alphanohtml') : (getDolGlobalString('MAIN_INFO_SOCIETE_MOBILE')))).'"></td></tr>';
print '</td></tr>'."\n";
// Fax
print '<tr class="oddeven"><td><label for="fax">'.$langs->trans("Fax").'</label></td><td>';
print img_picto('', 'object_phoning_fax', '', false, 0, 0, '', 'pictofixedwidth');
print '<input class="maxwidth150" name="fax" id="fax" value="'.dol_escape_htmltag((GETPOSTISSET('fax') ? GETPOST('fax', 'alphanohtml') : (getDolGlobalString('MAIN_INFO_SOCIETE_FAX') ? $conf->global->MAIN_INFO_SOCIETE_FAX : ''))).'"></td></tr>';
print '<input class="maxwidth150" name="fax" id="fax" value="'.dol_escape_htmltag((GETPOSTISSET('fax') ? GETPOST('fax', 'alphanohtml') : (getDolGlobalString('MAIN_INFO_SOCIETE_FAX')))).'"></td></tr>';
print '</td></tr>'."\n";
// Email

View File

@@ -10,7 +10,7 @@
* Copyright (C) 2015 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
* Copyright (C) 2016 Charlie Benke <charlie@patas-monkey.com>
* Copyright (C) 2018-2023 Frédéric France <frederic.france@netlogic.fr>
* Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2023 Benjamin Falière <benjamin.faliere@altairis.fr>
*
* This program is free software; you can redistribute it and/or modify
@@ -1707,7 +1707,7 @@ class Categorie extends CommonObject
* @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
* @return string Chaine avec URL
*/
public function getNomUrl($withpicto = 0, $option = '', $maxlength = 0, $moreparam = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = '')
public function getNomUrl($withpicto = 0, $option = '', $maxlength = 0, $moreparam = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = 0)
{
global $conf, $langs, $hookmanager;

View File

@@ -2727,7 +2727,7 @@ while ($i < $imaxinloop) {
// Billed
if (!empty($arrayfields['c.facture']['checked'])) {
print '<td class="center">'.yn($obj->billed).'</td>';
print '<td class="center">'.yn($obj->billed, 4).'</td>';
if (!$i) {
$totalarray['nbfield']++;
}

View File

@@ -2660,14 +2660,14 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity,
}
}
// Fix modulepart for backward compatibility
if ($modulepart == 'users') {
if ($modulepart == 'facture') {
$modulepart = 'invoice';
} elseif ($modulepart == 'users') {
$modulepart = 'user';
}
if ($modulepart == 'tva') {
} elseif ($modulepart == 'tva') {
$modulepart = 'tax-vat';
}
} elseif ($modulepart == 'expedition' && strpos($original_file, 'receipt/') === 0) {
// Fix modulepart delivery
if ($modulepart == 'expedition' && strpos($original_file, 'receipt/') === 0) {
$modulepart = 'delivery';
}

View File

@@ -7236,6 +7236,9 @@ function yn($yesno, $case = 1, $color = 0)
if ($case == 3) {
$result = '<input type="checkbox" value="1" checked disabled> '.$result;
}
if ($case == 4) {
$result = img_picto('check', 'check');
}
$classname = 'ok';
} elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
@@ -7249,6 +7252,9 @@ function yn($yesno, $case = 1, $color = 0)
if ($case == 3) {
$result = '<input type="checkbox" value="0" disabled> '.$result;
}
if ($case == 4) {
$result = img_picto('uncheck', 'uncheck');
}
if ($color == 2) {
$classname = 'ok';

View File

@@ -1912,7 +1912,7 @@ function getListOfModels($db, $type, $maxfilenamelength = 0)
}
if (is_dir($tmpdir)) {
// all type of template is allowed
$tmpfiles = dol_dir_list($tmpdir, 'files', 0, '', '', 'name', SORT_ASC, 0);
$tmpfiles = dol_dir_list($tmpdir, 'files', 0, '', null, 'name', SORT_ASC, 0);
if (count($tmpfiles)) {
$listoffiles = array_merge($listoffiles, $tmpfiles);
}

View File

@@ -6,7 +6,7 @@
* Copyright (C) 2011-2023 Philippe Grand <philippe.grand@atoo-net.com>
* Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2023 Frédéric France <frederic.france@netlogic.fr>
* Copyright (C) 2023-2024 Frédéric France <frederic.france@free.fr>
*
* 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
@@ -602,7 +602,7 @@ class Delivery extends CommonObject
* @param array $array_options extrafields array
* @return int Return integer <0 if KO, >0 if OK
*/
public function update_line($id, $array_options = 0)
public function update_line($id, $array_options = [])
{
// phpcs:enable
global $conf;

View File

@@ -3689,7 +3689,7 @@ class FactureFournisseur extends CommonInvoice
return 0;
} else {
$this->error = 'Nb of emails sent : '.$nbMailSend.', '.(!empty($errorsMsg)) ? join(', ', $errorsMsg) : $error;
$this->error = 'Nb of emails sent : '.$nbMailSend.', '.(!empty($errorsMsg)) ? implode(', ', $errorsMsg) : $error;
dol_syslog(__METHOD__." end - ".$this->error, LOG_INFO);

View File

@@ -69,7 +69,7 @@ if (!$sortfield) {
$search_month = GETPOST('search_month', 'int');
$search_year = GETPOST('search_year', 'int');
if (GETPOSTISARRAY('search_status')) {
$search_status = join(',', GETPOST('search_status', 'array:intcomma'));
$search_status = implode(',', GETPOST('search_status', 'array:intcomma'));
} else {
$search_status = (GETPOST('search_status', 'intcomma') != '' ? GETPOST('search_status', 'intcomma') : GETPOST('statut', 'intcomma'));
}

View File

@@ -69,7 +69,7 @@ if (!$sortfield) {
$search_month = GETPOST('search_month', 'int');
$search_year = GETPOST('search_year', 'int');
if (GETPOSTISARRAY('search_status')) {
$search_status = join(',', GETPOST('search_status', 'array:intcomma'));
$search_status = implode(',', GETPOST('search_status', 'array:intcomma'));
} else {
$search_status = (GETPOST('search_status', 'intcomma') != '' ? GETPOST('search_status', 'intcomma') : GETPOST('statut', 'intcomma'));
}

View File

@@ -2,7 +2,7 @@
/* Copyright (C) 2008-2014 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2010-2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2014 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2018-2024 Frédéric France <frederic.france@netlogic.fr>
* Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2020 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2022 Charlene Benke <charlene@patas-monkey.com>
* Copyright (C) 2023 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
@@ -903,14 +903,14 @@ class Task extends CommonObjectLine
{
$this->id = 0;
$this->fk_project = '';
$this->fk_project = 0;
$this->ref = 'TK01';
$this->fk_task_parent = null;
$this->fk_task_parent = 0;
$this->label = 'Specimen task TK01';
$this->duration_effective = '';
$this->fk_user_creat = null;
$this->fk_user_creat = 1;
$this->progress = '25';
$this->status = null;
$this->status = 0;
$this->note = 'This is a specimen task not';
}

View File

@@ -937,7 +937,7 @@ class Reception extends CommonObject
$this->fk_user_valid = trim($this->fk_user_valid);
}
if (isset($this->shipping_method_id)) {
$this->shipping_method_id = trim($this->shipping_method_id);
$this->shipping_method_id = (int) $this->shipping_method_id;
}
if (isset($this->tracking_number)) {
$this->tracking_number = trim($this->tracking_number);

View File

@@ -1,6 +1,6 @@
<?php
/* Copyright (C) - 2013-2015 Jean-François FERRY <jfefe@aternatik.fr>
* Copyright (C) 2019 Frédéric France <frederic.france@netlogic.fr>
* Copyright (C) 2019-2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2022 Ferran Marcet <fmarcet@2byte.es>
* Copyright (C) 2023 William Mead <william.mead@manchenumerique.fr>
*
@@ -80,7 +80,7 @@ class FormResource
* @param bool $multiple add [] in the name of element and add 'multiple' attribute
* @return string|array HTML string with
*/
public function select_resource_list($selected = 0, $htmlname = 'fk_resource', array $filter = [], $showempty = 0, $showtype = 0, $forcecombo = 0, $event = [], $filterkey = '', $outputmode = 0, $limit = 20, $morecss = '', $multiple = false)
public function select_resource_list($selected = 0, $htmlname = 'fk_resource', array $filter = [], $showempty = 0, $showtype = 0, $forcecombo = 0, $event = [], $filterkey = [], $outputmode = 0, $limit = 20, $morecss = '', $multiple = false)
{
// phpcs:enable
global $conf, $user, $langs;

View File

@@ -131,14 +131,20 @@ class CodingPhpTest extends CommonClassTest
//print 'Check php file '.$file['relativename']."\n";
$filecontent = file_get_contents($file['fullname']);
$this->verifyIsModuleEnabledOk($filecontent, "htdocs/{$file['relativename']}");
// We are not interested in the comments
$filecontent = $this->removePhpComments(file_get_contents($file['fullname']));
// File path for reports
$report_filepath = "htdocs/{$file['relativename']}";
$this->verifyIsModuleEnabledOk($filecontent, $report_filepath);
if (preg_match('/\.class\.php/', $file['relativename'])
|| preg_match('/boxes\/box_/', $file['relativename'])
|| preg_match('/modules\/.*\/doc\/(doc|pdf)_/', $file['relativename'])
|| preg_match('/modules\/(import|mailings|printing)\//', $file['relativename'])
|| in_array($file['name'], array('modules_boxes.php', 'TraceableDB.php'))) {
// Check into Class files
// Check Class files
if (! in_array($file['name'], array(
'api.class.php',
'commonobject.class.php',
@@ -152,18 +158,18 @@ class CodingPhpTest extends CommonClassTest
// Must not find $db->
$ok = true;
$matches = array();
// Check string $db-> inside a class.php file (it should be $this->db-> into such classes)
// Check string $db-> inside a class.php file (it should be $this->db-> in such classes)
preg_match_all('/'.preg_quote('$db->', '/').'/', $filecontent, $matches, PREG_SET_ORDER);
foreach ($matches as $key => $val) {
$ok = false;
break;
}
//print __METHOD__." Result for checking we don't have non escaped string in sql requests for file ".$file."\n";
$this->assertTrue($ok, 'Found string $db-> into a .class.php file in '.$file['relativename'].'. Inside a .class file, you should use $this->db-> instead.');
$this->assertTrue($ok, 'Found string $db-> in a .class.php file in '.$file['relativename'].'. Inside a .class file, you should use $this->db-> instead.');
//exit;
}
if (preg_match('/\.class\.php/', $file['relativename']) && ! in_array($file['relativename'], array(
if (preg_match('/\.class\.php$/', $file['relativename']) && ! in_array($file['relativename'], array(
'adherents/canvas/actions_adherentcard_common.class.php',
'contact/canvas/actions_contactcard_common.class.php',
'compta/facture/class/facture.class.php',
@@ -191,7 +197,7 @@ class CodingPhpTest extends CommonClassTest
// Must not find GETPOST
$ok = true;
$matches = array();
// Check string GETPOSTFLOAT a class.php file (should not be found into classes)
// Check string GETPOSTFLOAT a class.php file (should not be found in classes)
preg_match_all('/GETPOST\(["\'](....)/', $filecontent, $matches, PREG_SET_ORDER);
foreach ($matches as $key => $val) {
if (in_array($val[1], array('lang', 'forc', 'mass', 'conf'))) {
@@ -201,11 +207,10 @@ class CodingPhpTest extends CommonClassTest
$ok = false;
break;
}
//print __METHOD__." Result for checking we don't have non escaped string in sql requests for file ".$file."\n";
$this->assertTrue($ok, 'Found string GETPOST into a .class.php file in '.$file['relativename'].'.');
$this->assertTrue($ok, 'Found string GETPOST in a .class.php file in '.$file['relativename'].'.');
}
} else {
// Check into Include files
// Check Include files
if (! in_array($file['name'], array(
'objectline_view.tpl.php',
'extrafieldsinexport.inc.php',
@@ -216,7 +221,7 @@ class CodingPhpTest extends CommonClassTest
// Must not found $this->db->
$ok = true;
$matches = array();
// Check string $this->db-> into a non class.php file (it should be $db-> into such classes)
// Check string $this->db-> in a non class.php file (it should be $db-> in such classes)
preg_match_all('/'.preg_quote('$this->db->', '/').'/', $filecontent, $matches, PREG_SET_ORDER);
foreach ($matches as $key => $val) {
$ok = false;
@@ -228,9 +233,9 @@ class CodingPhpTest extends CommonClassTest
}
}
// Check we don't miss top_httphead() into any ajax pages
// Check we don't miss top_httphead() in any ajax pages
if (preg_match('/ajax\//', $file['relativename'])) {
print "Analyze ajax page ".$file['relativename']."\n";
//print "Analyze ajax page ".$file['relativename']."\n";
$ok = true;
$matches = array();
preg_match_all('/top_httphead/', $filecontent, $matches, PREG_SET_ORDER);
@@ -238,28 +243,13 @@ class CodingPhpTest extends CommonClassTest
$ok = false;
}
//print __METHOD__." Result for checking we don't have non escaped string in sql requests for file ".$file."\n";
$this->assertTrue($ok, 'Did not find top_httphead into the ajax page '.$file['relativename']);
$this->assertTrue($ok, 'Did not find top_httphead in the ajax page '.$file['relativename']);
//exit;
}
// Check if a var_dump has been forgotten
// Check for unauthorised vardumps
if (!preg_match('/test\/phpunit/', $file['fullname'])) {
if (! in_array($file['name'], array('class.nusoap_base.php'))) {
$ok = true;
$matches = array();
preg_match_all('/(.)\s*var_dump\(/', $filecontent, $matches, PREG_SET_ORDER);
//var_dump($matches);
foreach ($matches as $key => $val) {
if ($val[1] != '/' && $val[1] != '*') {
$ok = false;
break;
}
break;
}
//print __METHOD__." Result for checking we don't have non escaped string in sql requests for file ".$file."\n";
$this->assertTrue($ok, 'Found string var_dump that is not just after /* or // in '.$file['relativename']);
//exit;
}
$this->verifyNoActiveVardump($filecontent, $report_filepath);
}
// Check get_class followed by __METHOD__
@@ -311,7 +301,7 @@ class CodingPhpTest extends CommonClassTest
break;
}
//print __METHOD__." Result for checking we don't have non escaped string in sql requests for file ".$file."\n";
$this->assertTrue($ok, 'Found non quoted or not casted var into sql request '.$file['relativename'].' - Bad.');
$this->assertTrue($ok, 'Found non quoted or not casted var in sql request '.$file['relativename'].' - Bad.');
//exit;
// Check that forged sql string is using ' instead of " as string PHP quotes
@@ -327,7 +317,7 @@ class CodingPhpTest extends CommonClassTest
//if ($reg[0] != 'db') $ok=false;
}
//print __METHOD__." Result for checking we don't have non escaped string in sql requests for file ".$file."\n";
$this->assertTrue($ok, 'Found a forged SQL string that mix on same line the use of \' for PHP string and PHP variables into file '.$file['relativename'].' Use " to forge PHP string like this: $sql = "SELECT ".$myvar...');
$this->assertTrue($ok, 'Found a forged SQL string that mix on same line the use of \' for PHP string and PHP variables in file '.$file['relativename'].' Use " to forge PHP string like this: $sql = "SELECT ".$myvar...');
//exit;
// Check that forged sql string is using ' instead of " as string PHP quotes
@@ -339,7 +329,7 @@ class CodingPhpTest extends CommonClassTest
$ok = false;
break;
}
$this->assertTrue($ok, 'Found a forged SQL string that mix on same line the use of \' for PHP string and PHP variables into file '.$file['relativename'].' Use " to forge PHP string like this: $sql = "SELECT ".$myvar...');
$this->assertTrue($ok, 'Found a forged SQL string that mix on same line the use of \' for PHP string and PHP variables in file '.$file['relativename'].' Use " to forge PHP string like this: $sql = "SELECT ".$myvar...');
// Check sql string VALUES ... , ".$xxx
// with xxx that is not 'db-' (for $db->escape). It means we forget a ' if string, or an (int) if int, when forging sql request.
@@ -358,7 +348,7 @@ class CodingPhpTest extends CommonClassTest
break;
}
//print __METHOD__." Result for checking we don't have non escaped string in sql requests for file ".$file."\n";
$this->assertTrue($ok, 'Found non quoted or not casted var into sql request '.$file['relativename'].' - Bad.');
$this->assertTrue($ok, 'Found non quoted or not casted var in sql request '.$file['relativename'].' - Bad.');
//exit;
// Check '".$xxx non escaped
@@ -514,7 +504,7 @@ class CodingPhpTest extends CommonClassTest
break;
}
}
$this->assertTrue($ok, 'Found a forbidden string sequence into '.$file['relativename'].' : name="token" value="\'.$_SESSION[..., you must use a newToken() instead of $_SESSION[\'newtoken\'].');
$this->assertTrue($ok, 'Found a forbidden string sequence in '.$file['relativename'].' : name="token" value="\'.$_SESSION[..., you must use a newToken() instead of $_SESSION[\'newtoken\'].');
// Test we don't have preg_grep with a param without preg_quote
@@ -589,10 +579,93 @@ class CodingPhpTest extends CommonClassTest
$ok = false;
break;
}
$this->assertTrue($ok, 'Found a CURDATE\(\) into code. Do not use this SQL method in file '.$file['relativename'].'. You must use the PHP function dol_now() instead.');
$this->assertTrue($ok, 'Found a CURDATE\(\) in code. Do not use this SQL method in file '.$file['relativename'].'. You must use the PHP function dol_now() instead.');
}
/**
* Verify that no active var_dump was left over in the code
*
* @param string $filecontent Contents to check for php code that uses a module name
* @param string $filename File name for the contents (used for reporting)
*
* @return void
*/
private function verifyNoActiveVardump(&$filecontent, $filename)
{
$ok = true;
$matches = array();
// Match!:
// - Line-start, whitespace, var_dump
// - Line-start, no-comment-leader, var_dump
// no-commen-leader=
// - Any character not / or *
// - Any / not preceded with / and not followed by / or *
// - Any * not preceded with /
preg_match_all('{^(?:^|^(?:[ \t]*|(?:(?:[^*/]|(?<![^/])/(?![*/])|(?!/)\*)(\S))))\bvar_dump\(}m', $filecontent, $matches, PREG_SET_ORDER);
$failing_string = "";
foreach ($matches as $key => $val) {
if (!isset($val[1]) || $val[1] != '/' && $val[1] != '*') {
$ok = false;
$failing_string = $val[0];
break;
}
}
$this->assertTrue($ok, "Found string var_dump that is not just after /* or // in '$filename': $failing_string");
}
/**
* Provide test data for testing the method detecting var_dump presence.
*
* @return array<string,array{0:string,1:bool}> Test sets
*/
public function vardumpTesterProvider()
{
return [
'var_dump at start of file' => ["var_dump(\$help)\n", true],
'var_dump at start of line' => ["\nvar_dump(\$help)\n", true],
'var_dump after comment next line' => ["/* Hello */\nvar_dump(\$help)\n", true],
'var_dump with space' => [" var_dump(\$help)\n", true],
'var_dump after comment' => [" // var_dump(\$help)\n", false],
'2 var_dumps after comment' => [" // var_dump(\$help); var_dump(\$help)\n", false],
'var_dump before and after comment' => [" var_dump(\$help); // var_dump(\$help)\n", true],
];
}
/**
* Test that verifyNoActiveVardump generates a notification
*
* @param string $filecontent Fake file content
* @param bool $hasVardump When true, expect var_dump detection
*
* @return void
*
* @dataProvider vardumpTesterProvider
*/
public function testVerifyNoActiveVardump(&$filecontent, $hasVardump)
{
$this->nbLinesToShow = 1;
// Create some dummy file content
$filename = $this->getName(false);
$notification = false;
ob_start(); // Do not disturb the output with tests that are meant to fail.
try {
$this->verifyNoActiveVardump($filecontent, $filename);
} catch (Throwable $e) {
$notification = (string) $e;
}
$output = ob_get_clean();
// Assert that a notification was generated
if ($hasVardump) {
$this->assertStringContainsString("Found string var_dump", $notification ?? '', "Expected notification not found.");
} else {
$this->assertFalse($notification, "Unexpection detection of var_dump");
}
}
/**
* Verify that only known modules are used
*
@@ -635,4 +708,72 @@ class CodingPhpTest extends CommonClassTest
);
}
}
/**
* Remove php comments from source string
*
* @param string $string The string from which the PHP comments are removed
*
* @return string The string without the comments
*/
private function removePhpComments($string)
{
return preg_replace_callback(
'{(//.*?$)|(/\*.*?\*/)}ms',
static function ($match) {
if (isset($match[2])) {
// Count the number of newline characters in the comment
$num_newlines = substr_count($match[0], "\n");
// Generate whitespace equivalent to the number of newlines
if ($num_newlines == 0) {
// /* Comment on single line -> space
return " ";
} else {
// /* Comment on multiple lines -> new lines
return str_repeat("\n", $num_newlines);
}
} else {
// Double slash comment, just remove
return "";
}
},
$string
);
}
/**
* Provide test data for testing the comments remover
*
* @return array<string,array{0:string,1:string}> Test sets
*/
public function commentRemovalTestProvider()
{
return [
'complete line 1' => ["/*Comment complete line*/", " "],
'complete line 2' => ["// Comment complete line", ""],
'partial line 1' => ["a/*Comment complete line*/b", "a b"],
'partial line 2' => ["a// Comment complete line", "a"],
'multi line full 1' => ["/*Comment\ncomplete line*/", "\n"],
'multi line full 2' => ["/*Comment\ncomplete line*/\n", "\n\n"],
'multi line partials 1' => ["a/*Comment\ncomplete line*/b", "a\nb"],
];
}
/**
* Test that comments are properly removed
*
* @param string $source Fake file content
* @param bool $expected When true, expect var_dump detection
*
* @return void
*
* @dataProvider commentRemovalTestProvider
*/
public function testRemovePhpComments(&$source, &$expected)
{
$this->nbLinesToShow = 0;
$this->assertEquals($expected, $this->removePhpComments($source), "Comments not removed as expected");
}
}