diff --git a/dev/tools/phan/config_fixer.php b/dev/tools/phan/config_fixer.php index b290e5ef5c7..8f5df967bca 100644 --- a/dev/tools/phan/config_fixer.php +++ b/dev/tools/phan/config_fixer.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2024-2025 MDW * Copyright (C) 2024 Frédéric France */ @@ -9,30 +9,40 @@ $config = include __DIR__.DIRECTORY_SEPARATOR."config.php"; +// Note: When more than one fixer is attached to the same Notice, only the last fix is applied. //require_once __DIR__.'/plugins/DeprecatedModuleNameFixer.php'; //require_once __DIR__.'/plugins/PriceFormFixer.php'; //require_once __DIR__.'/plugins/UrlEncodeStringifyFixer.php'; -require_once __DIR__.'/plugins/SelectDateFixer.php'; +//require_once __DIR__.'/plugins/SelectDateFixer.php'; +require_once __DIR__.'/plugins/setPageOrientationFixer.php'; +//require_once __DIR__.'/plugins/MultiCellFixer.php'; +//require_once __DIR__.'/plugins/setAutoPageBreakFixer.php'; +//require_once __DIR__.'/plugins/CellFixer.php'; //$deprecatedModuleNameRegex = '/^(?!(?:'.implode('|', array_keys($DEPRECATED_MODULE_MAPPING)).')$).*/'; -require_once __DIR__.'/plugins/DeprecatedModuleNameFixer.php'; +//require_once __DIR__.'/plugins/DeprecatedModuleNameFixer.php'; $config['exclude_file_regex'] = '@^(' // @phpstan-ignore-line .'dummy' // @phpstan-ignore-line + //.'|(?!htdocs/modulebuilderrtemplate/core/modules/mymodule/doc/pdf_standard_myobject.modules.php).*' // Only this file for test @php-stan-ignore-line + .'|htdocs/custom/.*' // Ignore all custom modules @phpstan-ignore-line .'|htdocs/.*/canvas/.*/tpl/.*.tpl.php' // @phpstan-ignore-line .'|htdocs/admin/tools/ui/.*' // @phpstan-ignore-line //.'|htdocs/modulebuilder/template/.*' // @phpstan-ignore-line + // Included as stub (better analysis) + .'|htdocs/includes/nusoap/.*' // @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) + // Included as stub (did not seem properly analyzed by phan without it) .'|htdocs/includes/stripe/.*' // @phpstan-ignore-line .'|htdocs/conf/conf.php' // @phpstan-ignore-line - //.'|htdocs/[^c][^o][^r][^e][^/].*' // For testing @phpstan-ignore-line - //.'|htdocs/[^h].*' // For testing on restricted set @phpstan-ignore-line + //.'|htdocs/[^mi](?!.*(pdf_|tcpdf)).*\.php' // @phpstan-ignore-line + //.'|htdocs/(?!.*modules.*(pdf_|pdf.lib)).*\.php' // @phpstan-ignore-line .')@'; // @phpstan-ignore-line // $config['plugins'][] = __DIR__.'/plugins/ParamMatchRegexPlugin.php'; +$config['plugins'] = []; $config['plugins'][] = 'DeprecateAliasPlugin'; // $config['plugins'][] = __DIR__.'/plugins/GetPostFixerPlugin.php'; // $config['plugins'][] = 'PHPDocToRealTypesPlugin'; diff --git a/dev/tools/phan/plugins/CellFixer.php b/dev/tools/phan/plugins/CellFixer.php new file mode 100644 index 00000000000..bb49d415b47 --- /dev/null +++ b/dev/tools/phan/plugins/CellFixer.php @@ -0,0 +1,185 @@ + + * + * For 'price()', replace $form parameter that is '' with 0. + */ + +declare(strict_types=1); + +use ast\flags; +use Microsoft\PhpParser\Node\Expression\CallExpression; +use Microsoft\PhpParser\Node\QualifiedName; +use Phan\AST\TolerantASTConverter\NodeUtils; +use Phan\CodeBase; +use Phan\IssueInstance; +use Phan\Library\FileCacheEntry; +use Phan\Plugin\Internal\IssueFixingPlugin\FileEdit; +use Phan\Plugin\Internal\IssueFixingPlugin\FileEditSet; +use Phan\Plugin\Internal\IssueFixingPlugin\IssueFixer; +use Microsoft\PhpParser\Node\Expression\ArgumentExpression; +use Microsoft\PhpParser\Node\DelimitedList\ArgumentExpressionList; +use Microsoft\PhpParser\Node\StringLiteral; +use Microsoft\PhpParser\Node\NumericLiteral; +use Microsoft\PhpParser\Node\ReservedWord; +use Microsoft\PhpParser\Token; + +/** + * This is a prototype, there are various features it does not implement. + */ + +call_user_func(static function (): void { + /** + * @param $code_base @unused-param + * @return ?FileEditSet a representation of the edit to make to replace a call to a function alias with a call to the original function + */ + $fix = static function (CodeBase $code_base, FileCacheEntry $contents, IssueInstance $instance): ?FileEditSet { + + // Argument {INDEX} (${PARAMETER}) is {CODE} of type {TYPE}{DETAILS} but + // {FUNCTIONLIKE} takes {TYPE}{DETAILS} defined at {FILE}:{LINE} (the inferred real argument type has nothing in common with the parameter's phpdoc type) + + //htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 3 ($h) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type) + //htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 4 ($m) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type) + // var_dump($instance->getTemplateParameters()); + $argument_index = (string) $instance->getTemplateParameters()[0]; + $argument_name = (string) $instance->getTemplateParameters()[1]; + $argument_code = (string) $instance->getTemplateParameters()[2]; + $argument_type = (string) $instance->getTemplateParameters()[3]; + $functionlike = (string) $instance->getTemplateParameters()[4]; + $functiontype = (string) $instance->getTemplateParameters()[5]; + + $expected_functionlike = "\\TCPDI::Cell()"; + $expected_functionlike2 = "\\TCPDF::Cell()"; + $expected_name = "Cell"; + if ($functionlike !== $expected_functionlike + && $functionlike !== $expected_functionlike2) { + print "$functionlike != '$expected_functionlike'|'$expected_functionlike2".PHP_EOL; + return null; + } + + $toBoolReplaceArray = array("0" => "false","1" => "true"); + // Check if we fix any of this + if ( + ($argument_name === 'fill' && in_array($argument_code, array_keys($toBoolReplaceArray))) + //|| ($argument_name === 'm' && $argument_code === "''") + //|| ($argument_name === 'empty' && $argument_code === "''") + ) { + $replacement = $toBoolReplaceArray[$argument_code]; + $argIdx = ($argument_index - 1) * 2; + $expectedStringValue = $argument_code; + } else { + print "ARG$argument_index:$argument_name CODE:$argument_name".PHP_EOL; + return null; + } + + // At this point we established that the notification + // matches some we fix. + + $line = $instance->getLine(); + + $edits = []; + foreach ($contents->getNodesAtLine($line) as $node) { + if (!$node instanceof ArgumentExpressionList) { + continue; + } + $arguments = $node->children; + if (count($arguments) <= $argIdx) { + print "Arg Count is ".count($arguments)." - Skip $instance".PHP_EOL; + continue; + } + + $is_actual_call = $node->parent instanceof CallExpression; + if (!$is_actual_call) { + print "Not actual call - Skip $instance".PHP_EOL; + continue; + } + + print "Actual call - $instance".PHP_EOL; + $callable = $node->parent; + + $callableExpression = $callable->callableExpression; + + if ($callableExpression instanceof Microsoft\PhpParser\Node\QualifiedName) { + $actual_name = $callableExpression->getResolvedName(); + } elseif ($callableExpression instanceof Microsoft\PhpParser\Node\Expression\MemberAccessExpression) { + $memberNameToken = $callableExpression->memberName; + $actual_name = (new NodeUtils($contents->getContents()))->tokenToString($memberNameToken); + } else { + print "Callable expression is ".get_class($callableExpression)."- Skip $instance".PHP_EOL; + continue; + } + + if ((string) $actual_name !== (string) $expected_name) { + // print "Name unexpected '$actual_name'!='$expected_name' - Skip $instance".PHP_EOL; + continue; + } + + foreach ($arguments as $i => $argument) { + if ($argument instanceof ArgumentExpression) { + print "Type$i: ".get_class($argument->expression).PHP_EOL; + } + } + + $fieldValue = null; + + + $arg = $arguments[$argIdx]; + + if ( + $arg instanceof ArgumentExpression + // && $arg->expression instanceof StringLiteral + && $arg->expression instanceof NumericLiteral + ) { + // Get the value of the NumericLiteral + $fieldValue = (string) $arg->expression; + print "Number is '$fieldValue'".PHP_EOL; + /* + // Get the string value of the StringLiteral + $fieldValue = $arg->expression->getStringContentsText(); + print "String is '$fieldValue'".PHP_EOL; + */ + } elseif ($arg instanceof ArgumentExpression && $arg->expression instanceof ReservedWord) { + $child = $arg->expression->children; + if (!$child instanceof Token) { + continue; + } + $token_str = (new NodeUtils($contents->getContents()))->tokenToString($child); + print "$token_str KIND:".($child->kind ?? 'no kind')." ".get_class($child).PHP_EOL; + + if ($token_str !== 'null') { + continue; + } + + $fieldValue = ''; // Fake empty + } else { + print "Expression is not expected type ".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL; + continue; + } + + if ($fieldValue !== $expectedStringValue) { + print "Not replacing $argument_name which is '$fieldValue'/".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL; + continue; + } + + print "Fixture elem on $line - $actual_name(...'$fieldValue'...) - $instance".PHP_EOL; + + + + // Get the first argument (delimiter) + $argument_to_replace = $arg; + + $arg_start_pos = $argument_to_replace->getStartPosition(); + $arg_end_pos = $argument_to_replace->getEndPosition(); + + // Set edit instruction + $edits[] = new FileEdit($arg_start_pos, $arg_end_pos, $replacement); + } + if ($edits) { + return new FileEditSet($edits); + } + return null; + }; + IssueFixer::registerFixerClosure( + 'PhanTypeMismatchArgument', + $fix + ); +}); diff --git a/dev/tools/phan/plugins/MultiCellFixer.php b/dev/tools/phan/plugins/MultiCellFixer.php new file mode 100644 index 00000000000..e126d0c622e --- /dev/null +++ b/dev/tools/phan/plugins/MultiCellFixer.php @@ -0,0 +1,185 @@ + + * + * For 'price()', replace $form parameter that is '' with 0. + */ + +declare(strict_types=1); + +use ast\flags; +use Microsoft\PhpParser\Node\Expression\CallExpression; +use Microsoft\PhpParser\Node\QualifiedName; +use Phan\AST\TolerantASTConverter\NodeUtils; +use Phan\CodeBase; +use Phan\IssueInstance; +use Phan\Library\FileCacheEntry; +use Phan\Plugin\Internal\IssueFixingPlugin\FileEdit; +use Phan\Plugin\Internal\IssueFixingPlugin\FileEditSet; +use Phan\Plugin\Internal\IssueFixingPlugin\IssueFixer; +use Microsoft\PhpParser\Node\Expression\ArgumentExpression; +use Microsoft\PhpParser\Node\DelimitedList\ArgumentExpressionList; +use Microsoft\PhpParser\Node\StringLiteral; +use Microsoft\PhpParser\Node\NumericLiteral; +use Microsoft\PhpParser\Node\ReservedWord; +use Microsoft\PhpParser\Token; + +/** + * This is a prototype, there are various features it does not implement. + */ + +call_user_func(static function (): void { + /** + * @param $code_base @unused-param + * @return ?FileEditSet a representation of the edit to make to replace a call to a function alias with a call to the original function + */ + $fix = static function (CodeBase $code_base, FileCacheEntry $contents, IssueInstance $instance): ?FileEditSet { + + // Argument {INDEX} (${PARAMETER}) is {CODE} of type {TYPE}{DETAILS} but + // {FUNCTIONLIKE} takes {TYPE}{DETAILS} defined at {FILE}:{LINE} (the inferred real argument type has nothing in common with the parameter's phpdoc type) + + //htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 3 ($h) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type) + //htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 4 ($m) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type) + // var_dump($instance->getTemplateParameters()); + $argument_index = (string) $instance->getTemplateParameters()[0]; + $argument_name = (string) $instance->getTemplateParameters()[1]; + $argument_code = (string) $instance->getTemplateParameters()[2]; + $argument_type = (string) $instance->getTemplateParameters()[3]; + $functionlike = (string) $instance->getTemplateParameters()[4]; + $functiontype = (string) $instance->getTemplateParameters()[5]; + + $expected_functionlike = "\\TCPDI::MultiCell()"; + $expected_functionlike2 = "\\TCPDF::MultiCell()"; + $expected_name = "MultiCell"; + if ($functionlike !== $expected_functionlike + && $functionlike !== $expected_functionlike2) { + print "$functionlike != '$expected_functionlike'|'$expected_functionlike2".PHP_EOL; + return null; + } + + $toBoolReplaceArray = array("0" => "false","1" => "true"); + // Check if we fix any of this + if ( + ($argument_name === 'fill' && in_array($argument_code, array_keys($toBoolReplaceArray))) + //|| ($argument_name === 'm' && $argument_code === "''") + //|| ($argument_name === 'empty' && $argument_code === "''") + ) { + $replacement = $toBoolReplaceArray[$argument_code]; + $argIdx = ($argument_index - 1) * 2; + $expectedStringValue = $argument_code; + } else { + print "ARG$argument_index:$argument_name CODE:$argument_name".PHP_EOL; + return null; + } + + // At this point we established that the notification + // matches some we fix. + + $line = $instance->getLine(); + + $edits = []; + foreach ($contents->getNodesAtLine($line) as $node) { + if (!$node instanceof ArgumentExpressionList) { + continue; + } + $arguments = $node->children; + if (count($arguments) <= $argIdx) { + print "Arg Count is ".count($arguments)." - Skip $instance".PHP_EOL; + continue; + } + + $is_actual_call = $node->parent instanceof CallExpression; + if (!$is_actual_call) { + print "Not actual call - Skip $instance".PHP_EOL; + continue; + } + + print "Actual call - $instance".PHP_EOL; + $callable = $node->parent; + + $callableExpression = $callable->callableExpression; + + if ($callableExpression instanceof Microsoft\PhpParser\Node\QualifiedName) { + $actual_name = $callableExpression->getResolvedName(); + } elseif ($callableExpression instanceof Microsoft\PhpParser\Node\Expression\MemberAccessExpression) { + $memberNameToken = $callableExpression->memberName; + $actual_name = (new NodeUtils($contents->getContents()))->tokenToString($memberNameToken); + } else { + print "Callable expression is ".get_class($callableExpression)."- Skip $instance".PHP_EOL; + continue; + } + + if ((string) $actual_name !== (string) $expected_name) { + // print "Name unexpected '$actual_name'!='$expected_name' - Skip $instance".PHP_EOL; + continue; + } + + foreach ($arguments as $i => $argument) { + if ($argument instanceof ArgumentExpression) { + print "Type$i: ".get_class($argument->expression).PHP_EOL; + } + } + + $fieldValue = null; + + + $arg = $arguments[$argIdx]; + + if ( + $arg instanceof ArgumentExpression + // && $arg->expression instanceof StringLiteral + && $arg->expression instanceof NumericLiteral + ) { + // Get the value of the NumericLiteral + $fieldValue = (string) $arg->expression; + print "Number is '$fieldValue'".PHP_EOL; + /* + // Get the string value of the StringLiteral + $fieldValue = $arg->expression->getStringContentsText(); + print "String is '$fieldValue'".PHP_EOL; + */ + } elseif ($arg instanceof ArgumentExpression && $arg->expression instanceof ReservedWord) { + $child = $arg->expression->children; + if (!$child instanceof Token) { + continue; + } + $token_str = (new NodeUtils($contents->getContents()))->tokenToString($child); + print "$token_str KIND:".($child->kind ?? 'no kind')." ".get_class($child).PHP_EOL; + + if ($token_str !== 'null') { + continue; + } + + $fieldValue = ''; // Fake empty + } else { + print "Expression is not expected type ".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL; + continue; + } + + if ($fieldValue !== $expectedStringValue) { + print "Not replacing $argument_name which is '$fieldValue'/".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL; + continue; + } + + print "Fixture elem on $line - $actual_name(...'$fieldValue'...) - $instance".PHP_EOL; + + + + // Get the first argument (delimiter) + $argument_to_replace = $arg; + + $arg_start_pos = $argument_to_replace->getStartPosition(); + $arg_end_pos = $argument_to_replace->getEndPosition(); + + // Set edit instruction + $edits[] = new FileEdit($arg_start_pos, $arg_end_pos, $replacement); + } + if ($edits) { + return new FileEditSet($edits); + } + return null; + }; + IssueFixer::registerFixerClosure( + 'PhanTypeMismatchArgument', + $fix + ); +}); diff --git a/dev/tools/phan/plugins/setAutoPageBreakFixer.php b/dev/tools/phan/plugins/setAutoPageBreakFixer.php new file mode 100644 index 00000000000..2d0c0ba11a7 --- /dev/null +++ b/dev/tools/phan/plugins/setAutoPageBreakFixer.php @@ -0,0 +1,185 @@ + + * + * For 'price()', replace $form parameter that is '' with 0. + */ + +declare(strict_types=1); + +use ast\flags; +use Microsoft\PhpParser\Node\Expression\CallExpression; +use Microsoft\PhpParser\Node\QualifiedName; +use Phan\AST\TolerantASTConverter\NodeUtils; +use Phan\CodeBase; +use Phan\IssueInstance; +use Phan\Library\FileCacheEntry; +use Phan\Plugin\Internal\IssueFixingPlugin\FileEdit; +use Phan\Plugin\Internal\IssueFixingPlugin\FileEditSet; +use Phan\Plugin\Internal\IssueFixingPlugin\IssueFixer; +use Microsoft\PhpParser\Node\Expression\ArgumentExpression; +use Microsoft\PhpParser\Node\DelimitedList\ArgumentExpressionList; +use Microsoft\PhpParser\Node\StringLiteral; +use Microsoft\PhpParser\Node\NumericLiteral; +use Microsoft\PhpParser\Node\ReservedWord; +use Microsoft\PhpParser\Token; + +/** + * This is a prototype, there are various features it does not implement. + */ + +call_user_func(static function (): void { + /** + * @param $code_base @unused-param + * @return ?FileEditSet a representation of the edit to make to replace a call to a function alias with a call to the original function + */ + $fix = static function (CodeBase $code_base, FileCacheEntry $contents, IssueInstance $instance): ?FileEditSet { + + // Argument {INDEX} (${PARAMETER}) is {CODE} of type {TYPE}{DETAILS} but + // {FUNCTIONLIKE} takes {TYPE}{DETAILS} defined at {FILE}:{LINE} (the inferred real argument type has nothing in common with the parameter's phpdoc type) + + //htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 3 ($h) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type) + //htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 4 ($m) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type) + // var_dump($instance->getTemplateParameters()); + $argument_index = (string) $instance->getTemplateParameters()[0]; + $argument_name = (string) $instance->getTemplateParameters()[1]; + $argument_code = (string) $instance->getTemplateParameters()[2]; + $argument_type = (string) $instance->getTemplateParameters()[3]; + $functionlike = (string) $instance->getTemplateParameters()[4]; + $functiontype = (string) $instance->getTemplateParameters()[5]; + + $expected_functionlike = "\\TCPDI::setAutoPageBreak()"; + $expected_functionlike2 = "\\TCPDF::setAutoPageBreak()"; + $expected_name = "setAutoPageBreak"; + if ($functionlike !== $expected_functionlike + && $functionlike !== $expected_functionlike2) { + //print "$functionlike != '$expected_functionlike'|'$expected_functionlike2".PHP_EOL; + return null; + } + + $toBoolReplaceArray = array("0" => "false","1" => "true"); + // Check if we fix any of this + if ( + ($argument_name === 'auto' && in_array($argument_code, array_keys($toBoolReplaceArray))) + //|| ($argument_name === 'm' && $argument_code === "''") + //|| ($argument_name === 'empty' && $argument_code === "''") + ) { + $replacement = $toBoolReplaceArray[$argument_code]; + $argIdx = ($argument_index - 1) * 2; + $expectedStringValue = $argument_code; + } else { + //print "ARG$argument_index:$argument_name CODE:$argument_name".PHP_EOL; + return null; + } + + // At this point we established that the notification + // matches some we fix. + + $line = $instance->getLine(); + + $edits = []; + foreach ($contents->getNodesAtLine($line) as $node) { + if (!$node instanceof ArgumentExpressionList) { + continue; + } + $arguments = $node->children; + if (count($arguments) <= $argIdx) { + // print "Arg Count is ".count($arguments)." - Skip $instance".PHP_EOL; + continue; + } + + $is_actual_call = $node->parent instanceof CallExpression; + if (!$is_actual_call) { + //print "Not actual call - Skip $instance".PHP_EOL; + continue; + } + + //print "Actual call - $instance".PHP_EOL; + $callable = $node->parent; + + $callableExpression = $callable->callableExpression; + + if ($callableExpression instanceof Microsoft\PhpParser\Node\QualifiedName) { + $actual_name = $callableExpression->getResolvedName(); + } elseif ($callableExpression instanceof Microsoft\PhpParser\Node\Expression\MemberAccessExpression) { + $memberNameToken = $callableExpression->memberName; + $actual_name = (new NodeUtils($contents->getContents()))->tokenToString($memberNameToken); + } else { + //print "Callable expression is ".get_class($callableExpression)."- Skip $instance".PHP_EOL; + continue; + } + + if ((string) $actual_name !== (string) $expected_name) { + print "Name unexpected '$actual_name'!='$expected_name' - Skip $instance".PHP_EOL; + continue; + } + + foreach ($arguments as $i => $argument) { + if ($argument instanceof ArgumentExpression) { + //print "Type$i: ".get_class($argument->expression).PHP_EOL; + } + } + + $fieldValue = null; + + + $arg = $arguments[$argIdx]; + + if ( + $arg instanceof ArgumentExpression + // && $arg->expression instanceof StringLiteral + && $arg->expression instanceof NumericLiteral + ) { + // Get the value of the NumericLiteral + $fieldValue = (string) $arg->expression; + print "Number is '$fieldValue'".PHP_EOL; + /* + // Get the string value of the StringLiteral + $fieldValue = $arg->expression->getStringContentsText(); + print "String is '$fieldValue'".PHP_EOL; + */ + } elseif ($arg instanceof ArgumentExpression && $arg->expression instanceof ReservedWord) { + $child = $arg->expression->children; + if (!$child instanceof Token) { + continue; + } + $token_str = (new NodeUtils($contents->getContents()))->tokenToString($child); + print "$token_str KIND:".($child->kind ?? 'no kind')." ".get_class($child).PHP_EOL; + + if ($token_str !== 'null') { + continue; + } + + $fieldValue = ''; // Fake empty + } else { + print "Expression is not expected type ".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL; + continue; + } + + if ($fieldValue !== $expectedStringValue) { + print "Not replacing '$argument_name' which is '$fieldValue'/".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL; + continue; + } + + print "Fixture elem on $line - $actual_name(...'$fieldValue'...) - $instance".PHP_EOL; + + + + // Get the first argument (delimiter) + $argument_to_replace = $arg; + + $arg_start_pos = $argument_to_replace->getStartPosition(); + $arg_end_pos = $argument_to_replace->getEndPosition(); + + // Set edit instruction + $edits[] = new FileEdit($arg_start_pos, $arg_end_pos, $replacement); + } + if ($edits) { + return new FileEditSet($edits); + } + return null; + }; + IssueFixer::registerFixerClosure( + 'PhanTypeMismatchArgument', + $fix + ); +}); diff --git a/dev/tools/phan/plugins/setPageOrientationFixer.php b/dev/tools/phan/plugins/setPageOrientationFixer.php index 657241ca155..09658fe6d23 100644 --- a/dev/tools/phan/plugins/setPageOrientationFixer.php +++ b/dev/tools/phan/plugins/setPageOrientationFixer.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2024-2025 MDW * * For 'price()', replace $form parameter that is '' with 0. */ @@ -19,6 +19,7 @@ use Phan\Plugin\Internal\IssueFixingPlugin\IssueFixer; use Microsoft\PhpParser\Node\Expression\ArgumentExpression; use Microsoft\PhpParser\Node\DelimitedList\ArgumentExpressionList; use Microsoft\PhpParser\Node\StringLiteral; +use Microsoft\PhpParser\Node\NumericLiteral; use Microsoft\PhpParser\Node\ReservedWord; use Microsoft\PhpParser\Token; @@ -38,31 +39,31 @@ call_user_func(static function (): void { //htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 3 ($h) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type) //htdocs\supplier_proposal\card.php:1705 PhanTypeMismatchArgumentProbablyReal Argument 4 ($m) is '' of type '' but \Form::selectDate() takes int (no real type) defined at htdocs\core\class\html.form.class.php:6799 (the inferred real argument type has nothing in common with the parameter's phpdoc type) - + // var_dump($instance->getTemplateParameters()); $argument_index = (string) $instance->getTemplateParameters()[0]; $argument_name = (string) $instance->getTemplateParameters()[1]; $argument_code = (string) $instance->getTemplateParameters()[2]; $argument_type = (string) $instance->getTemplateParameters()[3]; - $details = (string) $instance->getTemplateParameters()[4]; - $functionlike = (string) $instance->getTemplateParameters()[5]; + $functionlike = (string) $instance->getTemplateParameters()[4]; + $functiontype = (string) $instance->getTemplateParameters()[5]; - $expected_functionlike = "\\TCPDI::setPageOrientation()"; + $expected_functionlike = "\\TCPDF::setPageOrientation()"; $expected_name = "setPageOrientation"; if ($functionlike !== $expected_functionlike) { - print "$functionlike != '$expected_functionlike'".PHP_EOL; + //print "$functionlike != '$expected_functionlike'".PHP_EOL; return null; } - $toBoolReplaceArray=array("0"=>"false","1"=>"true"); + $toBoolReplaceArray = array("0" => "false","1" => "true"); // Check if we fix any of this if ( - ($argument_name === 'argument_name' && in_array($argument_code, array_keys($toBoolReplaceArray))) + ($argument_name === 'autopagebreak' && in_array($argument_code, array_keys($toBoolReplaceArray))) //|| ($argument_name === 'm' && $argument_code === "''") //|| ($argument_name === 'empty' && $argument_code === "''") ) { - $replacement = $toBoolReplaceArray[$argument_name]; + $replacement = $toBoolReplaceArray[$argument_code]; $argIdx = ($argument_index - 1) * 2; - $expectedStringValue = ""; + $expectedStringValue = $argument_code; } else { print "ARG$argument_index:$argument_name CODE:$argument_name".PHP_EOL; return null; @@ -112,22 +113,22 @@ call_user_func(static function (): void { foreach ($arguments as $i => $argument) { if ($argument instanceof ArgumentExpression) { - print "Type$i: ".get_class($argument->expression).PHP_EOL; + // print "Type$i: ".get_class($argument->expression).PHP_EOL; } } - $stringValue = null; + $fieldValue = null; $arg = $arguments[$argIdx]; if ( $arg instanceof ArgumentExpression - && $arg->expression instanceof StringLiteral + && $arg->expression instanceof NumericLiteral ) { - // Get the string value of the StringLiteral - $stringValue = $arg->expression->getStringContentsText(); - print "String is '$stringValue'".PHP_EOL; + // Get the string value of the NumericLiteral + $fieldValue = (string) $arg->expression; + //print "Field is '$fieldValue'".PHP_EOL; } elseif ($arg instanceof ArgumentExpression && $arg->expression instanceof ReservedWord) { $child = $arg->expression->children; if (!$child instanceof Token) { @@ -140,18 +141,18 @@ call_user_func(static function (): void { continue; } - $stringValue = ''; // Fake empty + $fieldValue = ''; // Fake empty } else { - print "Expression is not string or null ".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL; + // print "Expression is not expected type ".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL; continue; } - if ($stringValue !== $expectedStringValue) { - print "Not replacing $argument_name which is '$stringValue'/".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL; + if ($fieldValue !== $expectedStringValue) { + // print "Not replacing '$argument_name' which is '$fieldValue'/".get_class($arg)."/".get_class($arg->expression)."- Skip $instance".PHP_EOL; continue; } - print "Fixture elem on $line - $actual_name(...'$stringValue'...) - $instance".PHP_EOL; + print "Fixture elem on $line - $actual_name(...'$fieldValue'...) - $instance".PHP_EOL;