forked from Wavyzz/dolibarr
Qual: New Phan plugin for testing that argument matches regex (#28424)
* Qual: New Phan plugin for testing that argument matches regex # Qual: New Phan plugin for testing that argument matches regex This Plugin - currently applied to GETPOST - allows verifying that a selected argument of a function matches a regular expression. * Qual: Add isModEnabled verification to phan # Qual: Add isModEnabled verification in phan Using ParamMatchRegexPlugin, add isModEnabled parameter verification. * Qual: Verify sanitizeVal check value # Qual: Verify sanitizeVal check value Use ParamMatchPlugin to check sanitizeVal check value * Qual: Extend ParamMatchRegexPlugin with class_method # Qual: Extend ParamMatchRegexPlugin with class_method Allow matching class methods for argument verification. * Update config.php * Qual: New Phan plugin for testing that argument matches regex # Qual: New Phan plugin for testing that argument matches regex This Plugin - currently applied to GETPOST - allows verifying that a selected argument of a function matches a regular expression. * Qual: Add isModEnabled verification to phan # Qual: Add isModEnabled verification in phan Using ParamMatchRegexPlugin, add isModEnabled parameter verification. * Qual: Verify sanitizeVal check value # Qual: Verify sanitizeVal check value Use ParamMatchPlugin to check sanitizeVal check value * Qual: Extend ParamMatchRegexPlugin with class_method # Qual: Extend ParamMatchRegexPlugin with class_method Allow matching class methods for argument verification. * Report scalar values (see null, etc) * Qual: Ignore false Phan Notification * Qual: Ignore false Phan Notification * Qual: Fix Phan needs specific message keys for coloring. --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
This commit is contained in:
@@ -4,6 +4,187 @@
|
|||||||
define('DOL_PROJECT_ROOT', __DIR__.'/../../..');
|
define('DOL_PROJECT_ROOT', __DIR__.'/../../..');
|
||||||
define('DOL_DOCUMENT_ROOT', DOL_PROJECT_ROOT.'/htdocs');
|
define('DOL_DOCUMENT_ROOT', DOL_PROJECT_ROOT.'/htdocs');
|
||||||
define('PHAN_DIR', __DIR__);
|
define('PHAN_DIR', __DIR__);
|
||||||
|
$sanitizeRegex
|
||||||
|
= '/^(array:)?(?:'.implode(
|
||||||
|
'|',
|
||||||
|
array(
|
||||||
|
// Documented:
|
||||||
|
'none',
|
||||||
|
'array',
|
||||||
|
'int',
|
||||||
|
'intcomma',
|
||||||
|
'alpha',
|
||||||
|
'alphawithlgt',
|
||||||
|
'alphanohtml',
|
||||||
|
'MS',
|
||||||
|
'aZ',
|
||||||
|
'aZ09',
|
||||||
|
'aZ09arobase',
|
||||||
|
'aZ09comma',
|
||||||
|
'san_alpha',
|
||||||
|
'restricthtml',
|
||||||
|
'nohtml',
|
||||||
|
'custom',
|
||||||
|
// Not documented:
|
||||||
|
'email',
|
||||||
|
'restricthtmlallowclass',
|
||||||
|
'restricthtmlallowunvalid',
|
||||||
|
'restricthtmlnolink',
|
||||||
|
//'ascii',
|
||||||
|
//'categ_id',
|
||||||
|
//'chaine',
|
||||||
|
|
||||||
|
//'html',
|
||||||
|
//'boolean',
|
||||||
|
//'double',
|
||||||
|
//'float',
|
||||||
|
//'string',
|
||||||
|
)
|
||||||
|
).')*$/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map deprecated module names to new module names
|
||||||
|
*/
|
||||||
|
$DEPRECATED_MODULE_MAPPING = array(
|
||||||
|
'actioncomm' => 'agenda',
|
||||||
|
'adherent' => 'member',
|
||||||
|
'adherent_type' => 'member_type',
|
||||||
|
'banque' => 'bank',
|
||||||
|
'categorie' => 'category',
|
||||||
|
'commande' => 'order',
|
||||||
|
'contrat' => 'contract',
|
||||||
|
'entrepot' => 'stock',
|
||||||
|
'expedition' => 'delivery_note',
|
||||||
|
'facture' => 'invoice',
|
||||||
|
'ficheinter' => 'intervention',
|
||||||
|
'product_fournisseur_price' => 'productsupplierprice',
|
||||||
|
'product_price' => 'productprice',
|
||||||
|
'projet' => 'project',
|
||||||
|
'propale' => 'propal',
|
||||||
|
'socpeople' => 'contact',
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map module names to the 'class' name (the class is: mod<CLASSNAME>)
|
||||||
|
* Value is null when the module is not internal to the default
|
||||||
|
* Dolibarr setup.
|
||||||
|
*/
|
||||||
|
$VALID_MODULE_MAPPING = array(
|
||||||
|
'accounting' => 'Accounting',
|
||||||
|
'agenda' => 'Agenda',
|
||||||
|
'ai' => 'Ai',
|
||||||
|
'anothermodule' => null,
|
||||||
|
'api' => 'Api',
|
||||||
|
'asset' => 'Asset',
|
||||||
|
'bank' => 'Banque',
|
||||||
|
'barcode' => 'Barcode',
|
||||||
|
'blockedlog' => 'BlockedLog',
|
||||||
|
'bom' => 'Bom',
|
||||||
|
'bookcal' => 'BookCal',
|
||||||
|
'bookmark' => 'Bookmark',
|
||||||
|
'cashdesk' => null, // TODO: fill in proper class
|
||||||
|
'category' => 'Categorie',
|
||||||
|
'clicktodial' => 'ClickToDial',
|
||||||
|
'collab' => 'Collab',
|
||||||
|
'comptabilite' => 'Comptabilite',
|
||||||
|
'contact' => null, // TODO: fill in proper class
|
||||||
|
'contract' => 'Contrat',
|
||||||
|
'cron' => 'Cron',
|
||||||
|
'datapolicy' => 'DataPolicy',
|
||||||
|
'dav' => 'Dav',
|
||||||
|
'debugbar' => 'DebugBar',
|
||||||
|
'delivery_note' => 'Expedition',
|
||||||
|
'deplacement' => 'Deplacement',
|
||||||
|
"documentgeneration" => 'DocumentGeneration',
|
||||||
|
'don' => 'Don',
|
||||||
|
'dynamicprices' => 'DynamicPrices',
|
||||||
|
'ecm' => 'ECM',
|
||||||
|
'ecotax' => null, // TODO: External module ?
|
||||||
|
'emailcollector' => 'EmailCollector',
|
||||||
|
'eventorganization' => 'EventOrganization',
|
||||||
|
'expensereport' => 'ExpenseReport',
|
||||||
|
'export' => 'Export',
|
||||||
|
'externalrss' => 'ExternalRss',
|
||||||
|
'externalsite' => 'ExternalSite',
|
||||||
|
'fckeditor' => 'Fckeditor',
|
||||||
|
'fournisseur' => 'Fournisseur',
|
||||||
|
'ftp' => 'FTP',
|
||||||
|
'geoipmaxmind' => 'GeoIPMaxmind',
|
||||||
|
'google' => null, // External ?
|
||||||
|
'gravatar' => 'Gravatar',
|
||||||
|
'holiday' => 'Holiday',
|
||||||
|
'hrm' => 'HRM',
|
||||||
|
'import' => 'Import',
|
||||||
|
'incoterm' => 'Incoterm',
|
||||||
|
'intervention' => 'Ficheinter',
|
||||||
|
'intracommreport' => 'Intracommreport',
|
||||||
|
'invoice' => 'Facture',
|
||||||
|
'knowledgemanagement' => 'KnowledgeManagement',
|
||||||
|
'label' => 'Label',
|
||||||
|
'ldap' => 'Ldap',
|
||||||
|
'loan' => 'Loan',
|
||||||
|
'mailing' => 'Mailing',
|
||||||
|
'mailman' => null, // Same module as mailmanspip -> MailmanSpip ??
|
||||||
|
'mailmanspip' => 'MailmanSpip',
|
||||||
|
'margin' => 'Margin',
|
||||||
|
'member' => 'Adherent',
|
||||||
|
'memcached' => null, // TODO: External module?
|
||||||
|
'modulebuilder' => 'ModuleBuilder',
|
||||||
|
'mrp' => 'Mrp',
|
||||||
|
'multicompany' => null, // Not provided by default, no module tests
|
||||||
|
'multicurrency' => 'MultiCurrency',
|
||||||
|
'mymodule' => null, // modMyModule - Name used in module builder (avoid false positives)
|
||||||
|
'notification' => 'Notification',
|
||||||
|
'numberwords' => null, // Not provided by default, no module tests
|
||||||
|
'oauth' => 'Oauth',
|
||||||
|
'openstreetmap' => null, // External module?
|
||||||
|
'opensurvey' => 'OpenSurvey',
|
||||||
|
'order' => 'Commande',
|
||||||
|
'partnership' => 'Partnership',
|
||||||
|
'paybox' => 'Paybox',
|
||||||
|
'paymentbybanktransfer' => 'PaymentByBankTransfer',
|
||||||
|
'paypal' => 'Paypal',
|
||||||
|
'paypalplus' => null,
|
||||||
|
'prelevement' => 'Prelevement',
|
||||||
|
'printing' => 'Printing',
|
||||||
|
'product' => 'Product',
|
||||||
|
'productbatch' => 'ProductBatch',
|
||||||
|
'productprice' => null,
|
||||||
|
'productsupplierprice' => null,
|
||||||
|
'project' => 'Projet',
|
||||||
|
'propal' => 'Propale',
|
||||||
|
'receiptprinter' => 'ReceiptPrinter',
|
||||||
|
'reception' => 'Reception',
|
||||||
|
'recruitment' => 'Recruitment',
|
||||||
|
'resource' => 'Resource',
|
||||||
|
'salaries' => 'Salaries',
|
||||||
|
'service' => 'Service',
|
||||||
|
'socialnetworks' => 'SocialNetworks',
|
||||||
|
'societe' => 'Societe',
|
||||||
|
'stock' => 'Stock',
|
||||||
|
'stocktransfer' => 'StockTransfer',
|
||||||
|
'stripe' => 'Stripe',
|
||||||
|
'supplier_invoice' => null, // Special case, uses invoice
|
||||||
|
'supplier_order' => null, // Special case, uses invoice
|
||||||
|
'supplier_proposal' => 'SupplierProposal',
|
||||||
|
'syslog' => 'Syslog',
|
||||||
|
'takepos' => 'TakePos',
|
||||||
|
'tax' => 'Tax',
|
||||||
|
'ticket' => 'Ticket',
|
||||||
|
'user' => 'User',
|
||||||
|
'variants' => 'Variants',
|
||||||
|
'webhook' => 'Webhook',
|
||||||
|
'webportal' => 'WebPortal',
|
||||||
|
'webservices' => 'WebServices',
|
||||||
|
'webservicesclient' => 'WebServicesClient',
|
||||||
|
'website' => 'Website',
|
||||||
|
'workflow' => 'Workflow',
|
||||||
|
'workstation' => 'Workstation',
|
||||||
|
'zapier' => 'Zapier',
|
||||||
|
);
|
||||||
|
|
||||||
|
$moduleNameRegex = '/^(?:'.implode('|', array_merge(array_keys($DEPRECATED_MODULE_MAPPING), array_keys($VALID_MODULE_MAPPING), array('\$modulename'))).')$/';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This configuration will be read and overlaid on top of the
|
* This configuration will be read and overlaid on top of the
|
||||||
* default configuration. Command line arguments will be applied
|
* default configuration. Command line arguments will be applied
|
||||||
@@ -71,6 +252,7 @@ return [
|
|||||||
.'|htdocs/includes/restler/.*' // @phpstan-ignore-line
|
.'|htdocs/includes/restler/.*' // @phpstan-ignore-line
|
||||||
// Included as stub (did not seem properly analysed by phan without it)
|
// Included as stub (did not seem properly analysed by phan without it)
|
||||||
.'|htdocs/includes/stripe/.*' // @phpstan-ignore-line
|
.'|htdocs/includes/stripe/.*' // @phpstan-ignore-line
|
||||||
|
// .'|htdocs/[^h].*/.*' // For testing @phpstan-ignore-line
|
||||||
.')@', // @phpstan-ignore-line
|
.')@', // @phpstan-ignore-line
|
||||||
|
|
||||||
// A list of plugin files to execute.
|
// A list of plugin files to execute.
|
||||||
@@ -82,8 +264,14 @@ return [
|
|||||||
//
|
//
|
||||||
// Alternately, you can pass in the full path to a PHP file
|
// 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')
|
// with the plugin's implementation (e.g. 'vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php')
|
||||||
|
'ParamMatchRegexPlugin' => [
|
||||||
|
'/^GETPOST$/' => [1, $sanitizeRegex],
|
||||||
|
'/^isModEnabled$/' => [0, $moduleNameRegex],
|
||||||
|
'/^sanitizeVal$/' => [1, $sanitizeRegex],
|
||||||
|
],
|
||||||
'plugins' => [
|
'plugins' => [
|
||||||
__DIR__.'/plugins/NoVarDumpPlugin.php',
|
__DIR__.'/plugins/NoVarDumpPlugin.php',
|
||||||
|
__DIR__.'/plugins/ParamMatchRegexPlugin.php',
|
||||||
// checks if a function, closure or method unconditionally returns.
|
// checks if a function, closure or method unconditionally returns.
|
||||||
// can also be written as 'vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php'
|
// can also be written as 'vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php'
|
||||||
//'DeprecateAliasPlugin',
|
//'DeprecateAliasPlugin',
|
||||||
|
|||||||
@@ -4,6 +4,187 @@
|
|||||||
define('DOL_PROJECT_ROOT', __DIR__.'/../../..');
|
define('DOL_PROJECT_ROOT', __DIR__.'/../../..');
|
||||||
define('DOL_DOCUMENT_ROOT', DOL_PROJECT_ROOT.'/htdocs');
|
define('DOL_DOCUMENT_ROOT', DOL_PROJECT_ROOT.'/htdocs');
|
||||||
define('PHAN_DIR', __DIR__);
|
define('PHAN_DIR', __DIR__);
|
||||||
|
$sanitizeRegex
|
||||||
|
= '/^(array:)?(?:'.implode(
|
||||||
|
'|',
|
||||||
|
array(
|
||||||
|
// Documented:
|
||||||
|
'none',
|
||||||
|
'array',
|
||||||
|
'int',
|
||||||
|
'intcomma',
|
||||||
|
'alpha',
|
||||||
|
'alphawithlgt',
|
||||||
|
'alphanohtml',
|
||||||
|
'MS',
|
||||||
|
'aZ',
|
||||||
|
'aZ09',
|
||||||
|
'aZ09arobase',
|
||||||
|
'aZ09comma',
|
||||||
|
'san_alpha',
|
||||||
|
'restricthtml',
|
||||||
|
'nohtml',
|
||||||
|
'custom',
|
||||||
|
// Not documented:
|
||||||
|
'email',
|
||||||
|
'restricthtmlallowclass',
|
||||||
|
'restricthtmlallowunvalid',
|
||||||
|
'restricthtmlnolink',
|
||||||
|
//'ascii',
|
||||||
|
//'categ_id',
|
||||||
|
//'chaine',
|
||||||
|
|
||||||
|
//'html',
|
||||||
|
//'boolean',
|
||||||
|
//'double',
|
||||||
|
//'float',
|
||||||
|
//'string',
|
||||||
|
)
|
||||||
|
).')*$/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map deprecated module names to new module names
|
||||||
|
*/
|
||||||
|
$DEPRECATED_MODULE_MAPPING = array(
|
||||||
|
'actioncomm' => 'agenda',
|
||||||
|
'adherent' => 'member',
|
||||||
|
'adherent_type' => 'member_type',
|
||||||
|
'banque' => 'bank',
|
||||||
|
'categorie' => 'category',
|
||||||
|
'commande' => 'order',
|
||||||
|
'contrat' => 'contract',
|
||||||
|
'entrepot' => 'stock',
|
||||||
|
'expedition' => 'delivery_note',
|
||||||
|
'facture' => 'invoice',
|
||||||
|
'ficheinter' => 'intervention',
|
||||||
|
'product_fournisseur_price' => 'productsupplierprice',
|
||||||
|
'product_price' => 'productprice',
|
||||||
|
'projet' => 'project',
|
||||||
|
'propale' => 'propal',
|
||||||
|
'socpeople' => 'contact',
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map module names to the 'class' name (the class is: mod<CLASSNAME>)
|
||||||
|
* Value is null when the module is not internal to the default
|
||||||
|
* Dolibarr setup.
|
||||||
|
*/
|
||||||
|
$VALID_MODULE_MAPPING = array(
|
||||||
|
'accounting' => 'Accounting',
|
||||||
|
'agenda' => 'Agenda',
|
||||||
|
'ai' => 'Ai',
|
||||||
|
'anothermodule' => null,
|
||||||
|
'api' => 'Api',
|
||||||
|
'asset' => 'Asset',
|
||||||
|
'bank' => 'Banque',
|
||||||
|
'barcode' => 'Barcode',
|
||||||
|
'blockedlog' => 'BlockedLog',
|
||||||
|
'bom' => 'Bom',
|
||||||
|
'bookcal' => 'BookCal',
|
||||||
|
'bookmark' => 'Bookmark',
|
||||||
|
'cashdesk' => null, // TODO: fill in proper class
|
||||||
|
'category' => 'Categorie',
|
||||||
|
'clicktodial' => 'ClickToDial',
|
||||||
|
'collab' => 'Collab',
|
||||||
|
'comptabilite' => 'Comptabilite',
|
||||||
|
'contact' => null, // TODO: fill in proper class
|
||||||
|
'contract' => 'Contrat',
|
||||||
|
'cron' => 'Cron',
|
||||||
|
'datapolicy' => 'DataPolicy',
|
||||||
|
'dav' => 'Dav',
|
||||||
|
'debugbar' => 'DebugBar',
|
||||||
|
'delivery_note' => 'Expedition',
|
||||||
|
'deplacement' => 'Deplacement',
|
||||||
|
"documentgeneration" => 'DocumentGeneration',
|
||||||
|
'don' => 'Don',
|
||||||
|
'dynamicprices' => 'DynamicPrices',
|
||||||
|
'ecm' => 'ECM',
|
||||||
|
'ecotax' => null, // TODO: External module ?
|
||||||
|
'emailcollector' => 'EmailCollector',
|
||||||
|
'eventorganization' => 'EventOrganization',
|
||||||
|
'expensereport' => 'ExpenseReport',
|
||||||
|
'export' => 'Export',
|
||||||
|
'externalrss' => 'ExternalRss',
|
||||||
|
'externalsite' => 'ExternalSite',
|
||||||
|
'fckeditor' => 'Fckeditor',
|
||||||
|
'fournisseur' => 'Fournisseur',
|
||||||
|
'ftp' => 'FTP',
|
||||||
|
'geoipmaxmind' => 'GeoIPMaxmind',
|
||||||
|
'google' => null, // External ?
|
||||||
|
'gravatar' => 'Gravatar',
|
||||||
|
'holiday' => 'Holiday',
|
||||||
|
'hrm' => 'HRM',
|
||||||
|
'import' => 'Import',
|
||||||
|
'incoterm' => 'Incoterm',
|
||||||
|
'intervention' => 'Ficheinter',
|
||||||
|
'intracommreport' => 'Intracommreport',
|
||||||
|
'invoice' => 'Facture',
|
||||||
|
'knowledgemanagement' => 'KnowledgeManagement',
|
||||||
|
'label' => 'Label',
|
||||||
|
'ldap' => 'Ldap',
|
||||||
|
'loan' => 'Loan',
|
||||||
|
'mailing' => 'Mailing',
|
||||||
|
'mailman' => null, // Same module as mailmanspip -> MailmanSpip ??
|
||||||
|
'mailmanspip' => 'MailmanSpip',
|
||||||
|
'margin' => 'Margin',
|
||||||
|
'member' => 'Adherent',
|
||||||
|
'memcached' => null, // TODO: External module?
|
||||||
|
'modulebuilder' => 'ModuleBuilder',
|
||||||
|
'mrp' => 'Mrp',
|
||||||
|
'multicompany' => null, // Not provided by default, no module tests
|
||||||
|
'multicurrency' => 'MultiCurrency',
|
||||||
|
'mymodule' => null, // modMyModule - Name used in module builder (avoid false positives)
|
||||||
|
'notification' => 'Notification',
|
||||||
|
'numberwords' => null, // Not provided by default, no module tests
|
||||||
|
'oauth' => 'Oauth',
|
||||||
|
'openstreetmap' => null, // External module?
|
||||||
|
'opensurvey' => 'OpenSurvey',
|
||||||
|
'order' => 'Commande',
|
||||||
|
'partnership' => 'Partnership',
|
||||||
|
'paybox' => 'Paybox',
|
||||||
|
'paymentbybanktransfer' => 'PaymentByBankTransfer',
|
||||||
|
'paypal' => 'Paypal',
|
||||||
|
'paypalplus' => null,
|
||||||
|
'prelevement' => 'Prelevement',
|
||||||
|
'printing' => 'Printing',
|
||||||
|
'product' => 'Product',
|
||||||
|
'productbatch' => 'ProductBatch',
|
||||||
|
'productprice' => null,
|
||||||
|
'productsupplierprice' => null,
|
||||||
|
'project' => 'Projet',
|
||||||
|
'propal' => 'Propale',
|
||||||
|
'receiptprinter' => 'ReceiptPrinter',
|
||||||
|
'reception' => 'Reception',
|
||||||
|
'recruitment' => 'Recruitment',
|
||||||
|
'resource' => 'Resource',
|
||||||
|
'salaries' => 'Salaries',
|
||||||
|
'service' => 'Service',
|
||||||
|
'socialnetworks' => 'SocialNetworks',
|
||||||
|
'societe' => 'Societe',
|
||||||
|
'stock' => 'Stock',
|
||||||
|
'stocktransfer' => 'StockTransfer',
|
||||||
|
'stripe' => 'Stripe',
|
||||||
|
'supplier_invoice' => null, // Special case, uses invoice
|
||||||
|
'supplier_order' => null, // Special case, uses invoice
|
||||||
|
'supplier_proposal' => 'SupplierProposal',
|
||||||
|
'syslog' => 'Syslog',
|
||||||
|
'takepos' => 'TakePos',
|
||||||
|
'tax' => 'Tax',
|
||||||
|
'ticket' => 'Ticket',
|
||||||
|
'user' => 'User',
|
||||||
|
'variants' => 'Variants',
|
||||||
|
'webhook' => 'Webhook',
|
||||||
|
'webportal' => 'WebPortal',
|
||||||
|
'webservices' => 'WebServices',
|
||||||
|
'webservicesclient' => 'WebServicesClient',
|
||||||
|
'website' => 'Website',
|
||||||
|
'workflow' => 'Workflow',
|
||||||
|
'workstation' => 'Workstation',
|
||||||
|
'zapier' => 'Zapier',
|
||||||
|
);
|
||||||
|
|
||||||
|
$moduleNameRegex = '/^(?:'.implode('|', array_merge(array_keys($DEPRECATED_MODULE_MAPPING), array_keys($VALID_MODULE_MAPPING))).')$/';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This configuration will be read and overlaid on top of the
|
* This configuration will be read and overlaid on top of the
|
||||||
* default configuration. Command line arguments will be applied
|
* default configuration. Command line arguments will be applied
|
||||||
@@ -82,8 +263,14 @@ return [
|
|||||||
//
|
//
|
||||||
// Alternately, you can pass in the full path to a PHP file
|
// 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')
|
// with the plugin's implementation (e.g. 'vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php')
|
||||||
|
'ParamMatchRegexPlugin' => [
|
||||||
|
'/^GETPOST$/' => [1, $sanitizeRegex],
|
||||||
|
'/^isModEnabled$/' => [0, $moduleNameRegex],
|
||||||
|
'/^sanitizeVal$/' => [1, $sanitizeRegex],
|
||||||
|
],
|
||||||
'plugins' => [
|
'plugins' => [
|
||||||
__DIR__.'/plugins/NoVarDumpPlugin.php',
|
__DIR__.'/plugins/NoVarDumpPlugin.php',
|
||||||
|
__DIR__.'/plugins/ParamMatchRegexPlugin.php',
|
||||||
'DeprecateAliasPlugin',
|
'DeprecateAliasPlugin',
|
||||||
//'EmptyMethodAndFunctionPlugin',
|
//'EmptyMethodAndFunctionPlugin',
|
||||||
'InvalidVariableIssetPlugin',
|
'InvalidVariableIssetPlugin',
|
||||||
|
|||||||
203
dev/tools/phan/config_fixer.php
Normal file
203
dev/tools/phan/config_fixer.php
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
<?php
|
||||||
|
/* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
|
||||||
|
*/
|
||||||
|
define('DOL_PROJECT_ROOT', __DIR__.'/../../..');
|
||||||
|
define('DOL_DOCUMENT_ROOT', DOL_PROJECT_ROOT.'/htdocs');
|
||||||
|
define('PHAN_DIR', __DIR__);
|
||||||
|
/**
|
||||||
|
* This configuration will be read and overlaid on top of the
|
||||||
|
* default configuration. Command line arguments will be applied
|
||||||
|
* after this file is read.
|
||||||
|
*/
|
||||||
|
return [
|
||||||
|
// 'processes' => 6,
|
||||||
|
'backward_compatibility_checks' => false,
|
||||||
|
'simplify_ast' => true,
|
||||||
|
'analyzed_file_extensions' => ['php','inc'],
|
||||||
|
'globals_type_map' => [
|
||||||
|
'conf' => '\Conf',
|
||||||
|
'db' => '\DoliDB',
|
||||||
|
'extrafields' => '\ExtraFields',
|
||||||
|
'hookmanager' => '\HookManager',
|
||||||
|
'langs' => '\Translate',
|
||||||
|
'mysoc' => '\Societe',
|
||||||
|
'nblines' => '\int',
|
||||||
|
'user' => '\User',
|
||||||
|
],
|
||||||
|
|
||||||
|
// Supported values: `'5.6'`, `'7.0'`, `'7.1'`, `'7.2'`, `'7.3'`, `'7.4'`, `null`.
|
||||||
|
// If this is set to `null`,
|
||||||
|
// then Phan assumes the PHP version which is closest to the minor version
|
||||||
|
// of the php executable used to execute Phan.
|
||||||
|
//"target_php_version" => null,
|
||||||
|
"target_php_version" => '8.2',
|
||||||
|
//"target_php_version" => '7.3',
|
||||||
|
//"target_php_version" => '5.6',
|
||||||
|
|
||||||
|
// A list of directories that should be parsed for class and
|
||||||
|
// method information. After excluding the directories
|
||||||
|
// defined in exclude_analysis_directory_list, the remaining
|
||||||
|
// files will be statically analyzed for errors.
|
||||||
|
//
|
||||||
|
// Thus, both first-party and third-party code being used by
|
||||||
|
// your application should be included in this list.
|
||||||
|
'directory_list' => [
|
||||||
|
'htdocs',
|
||||||
|
PHAN_DIR . '/stubs/',
|
||||||
|
],
|
||||||
|
|
||||||
|
// A directory list that defines files that will be excluded
|
||||||
|
// from static analysis, but whose class and method
|
||||||
|
// information should be included.
|
||||||
|
//
|
||||||
|
// Generally, you'll want to include the directories for
|
||||||
|
// third-party code (such as "vendor/") in this list.
|
||||||
|
//
|
||||||
|
// n.b.: If you'd like to parse but not analyze 3rd
|
||||||
|
// party code, directories containing that code
|
||||||
|
// should be added to the `directory_list` as
|
||||||
|
// to `exclude_analysis_directory_list`.
|
||||||
|
"exclude_analysis_directory_list" => [
|
||||||
|
'htdocs/includes/',
|
||||||
|
'htdocs/core/class/lessc.class.php', // External library
|
||||||
|
PHAN_DIR . '/stubs/',
|
||||||
|
],
|
||||||
|
//'exclude_file_regex' => '@^vendor/.*/(tests?|Tests?)/@',
|
||||||
|
'exclude_file_regex' => '@^(' // @phpstan-ignore-line
|
||||||
|
.'dummy' // @phpstan-ignore-line
|
||||||
|
.'|htdocs/.*/canvas/.*/tpl/.*.tpl.php' // @phpstan-ignore-line
|
||||||
|
.'|htdocs/modulebuilder/template/.*' // @phpstan-ignore-line
|
||||||
|
// Included as stub (old version + incompatible typing hints)
|
||||||
|
.'|htdocs/includes/restler/.*' // @phpstan-ignore-line
|
||||||
|
// Included as stub (did not seem properly analysed by phan without it)
|
||||||
|
.'|htdocs/includes/stripe/.*' // @phpstan-ignore-line
|
||||||
|
.')@', // @phpstan-ignore-line
|
||||||
|
|
||||||
|
// A list of plugin files to execute.
|
||||||
|
// Plugins which are bundled with Phan can be added here by providing their name
|
||||||
|
// (e.g. 'AlwaysReturnPlugin')
|
||||||
|
//
|
||||||
|
// Documentation about available bundled plugins can be found
|
||||||
|
// at https://github.com/phan/phan/tree/master/.phan/plugins
|
||||||
|
//
|
||||||
|
// 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' => [
|
||||||
|
//'DeprecateAliasPlugin',
|
||||||
|
// __DIR__.'/plugins/NoVarDumpPlugin.php',
|
||||||
|
//'PHPDocToRealTypesPlugin',
|
||||||
|
|
||||||
|
/*
|
||||||
|
//'EmptyMethodAndFunctionPlugin',
|
||||||
|
'InvalidVariableIssetPlugin',
|
||||||
|
//'MoreSpecificElementTypePlugin',
|
||||||
|
'NoAssertPlugin',
|
||||||
|
'NotFullyQualifiedUsagePlugin',
|
||||||
|
'PHPDocRedundantPlugin',
|
||||||
|
'PHPUnitNotDeadCodePlugin',
|
||||||
|
//'PossiblyStaticMethodPlugin',
|
||||||
|
'PreferNamespaceUsePlugin',
|
||||||
|
'PrintfCheckerPlugin',
|
||||||
|
'RedundantAssignmentPlugin',
|
||||||
|
|
||||||
|
'ConstantVariablePlugin', // Warns about values that are actually constant
|
||||||
|
//'HasPHPDocPlugin', // Requires PHPDoc
|
||||||
|
'InlineHTMLPlugin', // html in PHP file, or at end of file
|
||||||
|
'NonBoolBranchPlugin', // Requires test on bool, nont on ints
|
||||||
|
'NonBoolInLogicalArithPlugin',
|
||||||
|
'NumericalComparisonPlugin',
|
||||||
|
'PHPDocToRealTypesPlugin',
|
||||||
|
'PHPDocInWrongCommentPlugin', // Missing /** (/* was used)
|
||||||
|
//'ShortArrayPlugin', // Checks that [] is used
|
||||||
|
//'StrictLiteralComparisonPlugin',
|
||||||
|
'UnknownClassElementAccessPlugin',
|
||||||
|
'UnknownElementTypePlugin',
|
||||||
|
'WhitespacePlugin',
|
||||||
|
//'RemoveDebugStatementPlugin', // Reports echo, print, ...
|
||||||
|
//'StrictComparisonPlugin', // Expects ===
|
||||||
|
'SuspiciousParamOrderPlugin',
|
||||||
|
'UnsafeCodePlugin',
|
||||||
|
//'UnusedSuppressionPlugin',
|
||||||
|
|
||||||
|
'AlwaysReturnPlugin',
|
||||||
|
//'DollarDollarPlugin',
|
||||||
|
'DuplicateArrayKeyPlugin',
|
||||||
|
'DuplicateExpressionPlugin',
|
||||||
|
'PregRegexCheckerPlugin',
|
||||||
|
'PrintfCheckerPlugin',
|
||||||
|
'SleepCheckerPlugin',
|
||||||
|
// Checks for syntactically unreachable statements in
|
||||||
|
// the global scope or function bodies.
|
||||||
|
'UnreachableCodePlugin',
|
||||||
|
'UseReturnValuePlugin',
|
||||||
|
'EmptyStatementListPlugin',
|
||||||
|
'LoopVariableReusePlugin',
|
||||||
|
*/
|
||||||
|
],
|
||||||
|
|
||||||
|
// Add any issue types (such as 'PhanUndeclaredMethod')
|
||||||
|
// here to inhibit them from being reported
|
||||||
|
'suppress_issue_types' => [
|
||||||
|
'PhanPluginWhitespaceTab', // Dolibarr used tabs
|
||||||
|
'PhanPluginCanUsePHP71Void', // Dolibarr is maintaining 7.0 compatibility
|
||||||
|
'PhanPluginShortArray', // Dolibarr uses array()
|
||||||
|
'PhanPluginShortArrayList', // Dolibarr uses array()
|
||||||
|
// The following may require that --quick is not used
|
||||||
|
// 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
|
||||||
|
'PhanPluginNonBoolInLogicalArith', // Not essential - 11040+ occurrences
|
||||||
|
'PhanPluginConstantVariableScalar', // Not essential - 5180+ occurrences
|
||||||
|
'PhanPluginDuplicateConditionalTernaryDuplication', // 2750+ occurrences
|
||||||
|
'PhanPluginDuplicateConditionalNullCoalescing', // Not essential - 990+ occurrences
|
||||||
|
|
||||||
|
],
|
||||||
|
// You can put relative paths to internal stubs in this config option.
|
||||||
|
// Phan will continue using its detailed type annotations,
|
||||||
|
// but load the constants, classes, functions, and classes (and their Reflection types)
|
||||||
|
// from these stub files (doubling as valid php files).
|
||||||
|
// Use a different extension from php (and preferably a separate folder)
|
||||||
|
// to avoid accidentally parsing these as PHP (includes projects depending on this).
|
||||||
|
// The 'mkstubs' script can be used to generate your own stubs (compatible with php 7.0+ right now)
|
||||||
|
// Note: The array key must be the same as the extension name reported by `php -m`,
|
||||||
|
// so that phan can skip loading the stubs if the extension is actually available.
|
||||||
|
'autoload_internal_extension_signatures' => [
|
||||||
|
// Stubs may be available at https://github.com/JetBrains/phpstorm-stubs/tree/master
|
||||||
|
|
||||||
|
// Xdebug stubs are bundled with Phan 0.10.1+/0.8.9+ for usage,
|
||||||
|
// because Phan disables xdebug by default.
|
||||||
|
//'xdebug' => 'vendor/phan/phan/.phan/internal_stubs/xdebug.phan_php',
|
||||||
|
//'memcached' => PHAN_DIR . '/your_internal_stubs_folder_name/memcached.phan_php',
|
||||||
|
//'PDO' => PHAN_DIR . '/stubs/PDO.phan_php',
|
||||||
|
'brotli' => PHAN_DIR . '/stubs/brotli.phan_php',
|
||||||
|
'curl' => PHAN_DIR . '/stubs/curl.phan_php',
|
||||||
|
'calendar' => PHAN_DIR . '/stubs/calendar.phan_php',
|
||||||
|
'fileinfo' => PHAN_DIR . '/stubs/fileinfo.phan_php',
|
||||||
|
'ftp' => PHAN_DIR . '/stubs/ftp.phan_php',
|
||||||
|
'gd' => PHAN_DIR . '/stubs/gd.phan_php',
|
||||||
|
'geoip' => PHAN_DIR . '/stubs/geoip.phan_php',
|
||||||
|
'imap' => PHAN_DIR . '/stubs/imap.phan_php',
|
||||||
|
'intl' => PHAN_DIR . '/stubs/intl.phan_php',
|
||||||
|
'ldap' => PHAN_DIR . '/stubs/ldap.phan_php',
|
||||||
|
'mcrypt' => PHAN_DIR . '/stubs/mcrypt.phan_php',
|
||||||
|
'memcache' => PHAN_DIR . '/stubs/memcache.phan_php',
|
||||||
|
'mysqli' => PHAN_DIR . '/stubs/mysqli.phan_php',
|
||||||
|
'pdo_cubrid' => PHAN_DIR . '/stubs/pdo_cubrid.phan_php',
|
||||||
|
'pdo_mysql' => PHAN_DIR . '/stubs/pdo_mysql.phan_php',
|
||||||
|
'pdo_pgsql' => PHAN_DIR . '/stubs/pdo_pgsql.phan_php',
|
||||||
|
'pdo_sqlite' => PHAN_DIR . '/stubs/pdo_sqlite.phan_php',
|
||||||
|
'pgsql' => PHAN_DIR . '/stubs/pgsql.phan_php',
|
||||||
|
'session' => PHAN_DIR . '/stubs/session.phan_php',
|
||||||
|
'simplexml' => PHAN_DIR . '/stubs/SimpleXML.phan_php',
|
||||||
|
'soap' => PHAN_DIR . '/stubs/soap.phan_php',
|
||||||
|
'sockets' => PHAN_DIR . '/stubs/sockets.phan_php',
|
||||||
|
'zip' => PHAN_DIR . '/stubs/zip.phan_php',
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
||||||
248
dev/tools/phan/plugins/ParamMatchRegexPlugin.php
Normal file
248
dev/tools/phan/plugins/ParamMatchRegexPlugin.php
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
|
||||||
|
*
|
||||||
|
* Phan Plugin to validate that arguments match a regex
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* "ParamMatchRegexPlugin" => [
|
||||||
|
* "/^test1$/" => [ 0, "/^OK$/"], // Argument 0 must be 'OK'
|
||||||
|
* "/^test2$/" => [ 1, "/^NOK$/", "Test2Arg1NokError"], // Argument 1 must be 'NOK', error code
|
||||||
|
* "/^\\MyTest::mymethod$/" => [ 0, "/^NOK$/"], // Argument 0 must be 'NOK'
|
||||||
|
* ],
|
||||||
|
* 'plugins' => [
|
||||||
|
* ".phan/plugins/ParamMatchRegexPlugin.php",
|
||||||
|
* // [...]
|
||||||
|
* ],
|
||||||
|
*/
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
|
||||||
|
use ast\Node;
|
||||||
|
use Phan\Config;
|
||||||
|
use Phan\AST\UnionTypeVisitor;
|
||||||
|
//use Phan\Language\Element\FunctionInterface;
|
||||||
|
use Phan\Language\UnionType;
|
||||||
|
use Phan\Language\Type;
|
||||||
|
use Phan\PluginV3;
|
||||||
|
use Phan\PluginV3\PluginAwarePostAnalysisVisitor;
|
||||||
|
use Phan\PluginV3\PostAnalyzeNodeCapability;
|
||||||
|
use Phan\Exception\NodeException;
|
||||||
|
use Phan\Language\FQSEN\FullyQualifiedClassName;
|
||||||
|
use Phan\Exception\FQSENException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ParamMatchPlugin 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 ParamMatchPlugin extends PluginV3 implements PostAnalyzeNodeCapability
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string - name of PluginAwarePostAnalysisVisitor subclass
|
||||||
|
*/
|
||||||
|
public static function getPostAnalyzeNodeVisitorClassName(): string
|
||||||
|
{
|
||||||
|
return ParamMatchVisitor::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 ParamMatchVisitor extends PluginAwarePostAnalysisVisitor
|
||||||
|
{
|
||||||
|
// A plugin's visitors should not override visit() unless they need to.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
* @param Node $node Node to analyze
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function visitMethodCall(Node $node): void
|
||||||
|
{
|
||||||
|
$method_name = $node->children['method'] ?? null;
|
||||||
|
if (!\is_string($method_name)) {
|
||||||
|
return; // Not handled, TODO: handle variable(?) methods
|
||||||
|
// throw new NodeException($node);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// Fetch the list of valid classes, and warn about any undefined classes.
|
||||||
|
$union_type = UnionTypeVisitor::unionTypeFromNode($this->code_base, $this->context, $node->children['expr']);
|
||||||
|
} catch (Exception $_) {
|
||||||
|
// Phan should already throw for this
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$class_list = [];
|
||||||
|
foreach ($union_type->getTypeSet() as $type) {
|
||||||
|
$class_fqsen = "NoFSQENType";
|
||||||
|
if ($type instanceof Type) {
|
||||||
|
try {
|
||||||
|
$class_fqsen = (string) FullyQualifiedClassName::fromFullyQualifiedString($type->getName());
|
||||||
|
} catch (FQSENException $_) {
|
||||||
|
// var_dump([$_, $node]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// var_dump( $type) ;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$class_name = (string) $class_fqsen;
|
||||||
|
$class_list[] = $class_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* May need to check list of classes
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (!$class->hasMethodWithName($this->code_base, $method_name, true)) {
|
||||||
|
throw new NodeException($expr, 'does not have method');
|
||||||
|
}
|
||||||
|
$class_name = $class->getName();
|
||||||
|
*/
|
||||||
|
foreach ($class_list as $class_name) {
|
||||||
|
$this->checkRule($node, "$class_name::$method_name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
* @param Node $node Node to analyze
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function visitStaticCall(Node $node): void
|
||||||
|
{
|
||||||
|
$class_name = $node->children['class']->children['name'] ?? null;
|
||||||
|
if (!\is_string($class_name)) {
|
||||||
|
throw new NodeException($expr, 'does not have class');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$class_name = (string) FullyQualifiedClassName::fromFullyQualifiedString($class_name);
|
||||||
|
} catch (FQSENException $_) {
|
||||||
|
}
|
||||||
|
$method_name = $node->children['method'] ?? null;
|
||||||
|
|
||||||
|
if (!\is_string($method_name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->checkRule($node, "$class_name::$method_name");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*
|
||||||
|
* @param Node $node A node to analyze
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function visitCall(Node $node): void
|
||||||
|
{
|
||||||
|
$name = $node->children['expr']->children['name'] ?? null;
|
||||||
|
if (!\is_string($name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$this->checkRule($node, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param Node $node A node to analyze
|
||||||
|
* @param string $name function name or fqsn of class::<method>
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function checkRule(Node $node, string $name)
|
||||||
|
{
|
||||||
|
$rules = Config::getValue('ParamMatchRegexPlugin');
|
||||||
|
foreach ($rules as $regex => $rule) {
|
||||||
|
if (preg_match($regex, $name)) {
|
||||||
|
$this->checkParam($node, $rule[0], $rule[1], $name, $rule[2] ?? null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that argument matches regex at node
|
||||||
|
*
|
||||||
|
* @param Node $node Visited node for which to verify arguments match regex
|
||||||
|
* @param int $argPosition Position of argument to check
|
||||||
|
* @param string $argRegex Regex to validate against argument
|
||||||
|
* @param string $functionName Function name for report
|
||||||
|
* @param string $messageCode Message code to provide in message
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function checkParam(Node $node, int $argPosition, string $argRegex, $functionName, $messageCode = null): void
|
||||||
|
{
|
||||||
|
$args = $node->children['args']->children;
|
||||||
|
|
||||||
|
if (!array_key_exists($argPosition, $args)) {
|
||||||
|
/*
|
||||||
|
$this->emitPluginIssue(
|
||||||
|
$this->code_base,
|
||||||
|
$this->context,
|
||||||
|
'ParamMatchMissingArgument',
|
||||||
|
"Argument at %s for %s is missing",
|
||||||
|
[$argPosition, $function_name]
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$expr = $args[$argPosition];
|
||||||
|
try {
|
||||||
|
$expr_type = UnionTypeVisitor::unionTypeFromNode($this->code_base, $this->context, $expr, false);
|
||||||
|
} catch (Exception $_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$expr_value = $expr_type->getRealUnionType()->asValueOrNullOrSelf();
|
||||||
|
if (!is_object($expr_value)) {
|
||||||
|
$list = [(string) $expr_value];
|
||||||
|
} elseif ($expr_value instanceof UnionType) {
|
||||||
|
$list = $expr_value->asScalarValues();
|
||||||
|
} else {
|
||||||
|
// Note: maybe more types could be supported
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($list as $argValue) {
|
||||||
|
if (!\preg_match($argRegex, (string) $argValue)) {
|
||||||
|
// Emit an issue if the argument does not match the expected regex pattern
|
||||||
|
// var_dump([$node,$expr_value,$expr_type->getRealUnionType()]); // Information about node
|
||||||
|
$this->emitPluginIssue(
|
||||||
|
$this->code_base,
|
||||||
|
$this->context,
|
||||||
|
$messageCode ?? 'ParamMatchRegexError',
|
||||||
|
"Argument {INDEX} function {FUNCTION} can have value {STRING_LITERAL} that does not match the expected pattern '{STRING_LITERAL}'",
|
||||||
|
[$argPosition, $functionName, json_encode($argValue), $argRegex]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Every plugin needs to return an instance of itself at the
|
||||||
|
// end of the file in which it's defined.
|
||||||
|
return new ParamMatchPlugin();
|
||||||
@@ -932,6 +932,7 @@ function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null
|
|||||||
$out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
|
$out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @phan-suppress-next-line ParamMatchRegexError
|
||||||
$out = sanitizeVal($out, $check, $filter, $options);
|
$out = sanitizeVal($out, $check, $filter, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4186,6 +4186,7 @@ if ($module == 'initmodule') {
|
|||||||
print '<a class="reposition editfielda" href="'.$_SERVER['PHP_SELF'].'?tab='.urlencode($tab).'&tabobj='.$tabobj.'&module='.$module.($forceddirread ? '@'.$dirread : '').'&action=confirm_removefile&token='.newToken().'&file='.urlencode($pathtoapi).'">'.img_picto($langs->trans("Delete"), 'delete').'</a>';
|
print '<a class="reposition editfielda" href="'.$_SERVER['PHP_SELF'].'?tab='.urlencode($tab).'&tabobj='.$tabobj.'&module='.$module.($forceddirread ? '@'.$dirread : '').'&action=confirm_removefile&token='.newToken().'&file='.urlencode($pathtoapi).'">'.img_picto($langs->trans("Delete"), 'delete').'</a>';
|
||||||
print $form->textwithpicto('', $langs->trans("InfoForApiFile"), 1, 'warning');
|
print $form->textwithpicto('', $langs->trans("InfoForApiFile"), 1, 'warning');
|
||||||
print ' ';
|
print ' ';
|
||||||
|
// @phan-suppress-next-line ParamMatchRegexError
|
||||||
if (!isModEnabled($modulelowercase)) { // If module is not activated
|
if (!isModEnabled($modulelowercase)) { // If module is not activated
|
||||||
print '<a href="#" class="classfortooltip" target="apiexplorer" title="'.$langs->trans("ModuleMustBeEnabled", $module).'"><strike>'.$langs->trans("ApiExplorer").'</strike></a>';
|
print '<a href="#" class="classfortooltip" target="apiexplorer" title="'.$langs->trans("ModuleMustBeEnabled", $module).'"><strike>'.$langs->trans("ApiExplorer").'</strike></a>';
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user