diff --git a/moodle/Sniffs/PHP/DeprecatedFunctionsSniff.php b/moodle/Sniffs/PHP/DeprecatedFunctionsSniff.php new file mode 100644 index 00000000..b461fa55 --- /dev/null +++ b/moodle/Sniffs/PHP/DeprecatedFunctionsSniff.php @@ -0,0 +1,111 @@ +. + +/** + * Sniff for various Moodle deprecated functions which uses should be replaced. + * + * Note that strictly speaking we don't need to extend the Generic Sniff, + * just configure it in the ruleset.xml like this, for example: + * + * + * + * + * + * + * + * + * + * But we have decided to, instead, extend and keep the functions + * together with the Sniff. Also, this enables to test the Sniff + * without having to perform any configuration in the fixture files. + * (because unit tests DO NOT parse the ruleset.xml details, like + * properties, excludes... and other info). + * + * @package local_codechecker + * @copyright 2021 onwards Eloy Lafuente (stronk7) {@link https://stronk7.com} + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace MoodleCodeSniffer\moodle\Sniffs\PHP; + +use PHP_CodeSniffer\Standards\Generic\Sniffs\PHP\DeprecatedFunctionsSniff as GenericDeprecatedFunctionsSniff; +use PHP_CodeSniffer\Sniffs\Sniff; +use PHP_CodeSniffer\Files\File; + +class DeprecatedFunctionsSniff extends GenericDeprecatedFunctionsSniff { + + /** + * If true, an error will be thrown; otherwise a warning. + * + * @var boolean + */ + public $error = false; // Consider deprecations just warnings. + + /** + * A list of forbidden functions with their alternatives. + * + * The value is NULL if no alternative exists. IE, the + * function should just not be used. + * + * @var array + */ + public $forbiddenFunctions = [ + // Moodle deprecated functions. + 'print_error' => 'throw new moodle_exception() (using lang strings only if meant to be shown to final user)', + // Note that, apart from these Moodle explicitly set functions, also, all the internal PHP functions + // that are deprecated are detected automatically, {@see Generic\Sniffs\PHP\DeprecatedFunctionsSniff}. + ]; + + /** + * Generates the error or warning for this sniff. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the forbidden function + * in the token array. + * @param string $function The name of the forbidden function. + * @param string $pattern The pattern used for the match. + * + * @return void + * + * @todo: This method can be removed once/if this PR accepted: + * https://github.com/squizlabs/PHP_CodeSniffer/pull/3295 + */ + protected function addError($phpcsFile, $stackPtr, $function, $pattern=null) + { + $data = [$function]; + $error = 'Function %s() has been deprecated'; + $type = 'Deprecated'; + + if ($pattern === null) { + $pattern = strtolower($function); + } + + if ($this->forbiddenFunctions[$pattern] !== null + && $this->forbiddenFunctions[$pattern] !== 'null' + ) { + $type .= 'WithAlternative'; + $data[] = $this->forbiddenFunctions[$pattern]; + $error .= '; use %s() instead'; + } + + if ($this->error === true) { + $phpcsFile->addError($error, $stackPtr, $type, $data); + } else { + $phpcsFile->addWarning($error, $stackPtr, $type, $data); + } + + }//end addError() +} diff --git a/moodle/Sniffs/PHP/ForbiddenFunctionsSniff.php b/moodle/Sniffs/PHP/ForbiddenFunctionsSniff.php index 6b7ef25d..c03a8b98 100644 --- a/moodle/Sniffs/PHP/ForbiddenFunctionsSniff.php +++ b/moodle/Sniffs/PHP/ForbiddenFunctionsSniff.php @@ -17,6 +17,23 @@ /** * Sniff for debugging and other functions that we don't want used in finished code. * + * Note that strictly speaking we don't need to extend the Generic Sniff, + * just configure it in the ruleset.xml like this, for example: + * + * + * + * + * + * + * + * + * + * But we have decided to, instead, extend and keep the functions + * together with the Sniff. Also, this enables to test the Sniff + * without having to perform any configuration in the fixture files. + * (because unit tests DO NOT parse the ruleset.xml details, like + * properties, excludes... and other info). + * * @package local_codechecker * @copyright 2011 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later @@ -30,17 +47,22 @@ class ForbiddenFunctionsSniff extends GenericForbiddenFunctionsSniff { - /** Constructor. */ - public function __construct() { - $this->forbiddenFunctions = array( - // Usual development debugging functions. - 'sizeof' => 'count', - 'delete' => 'unset', - 'error_log' => null, - 'print_r' => null, - 'print_object' => null, - // Dangerous functions. From coding style. - 'extract' => null, - ); - } + /** + * A list of forbidden functions with their alternatives. + * + * The value is NULL if no alternative exists. IE, the + * function should just not be used. + * + * @var array + */ + public $forbiddenFunctions = [ + // Usual development debugging functions. + 'sizeof' => 'count', + 'delete' => 'unset', + 'error_log' => null, + 'print_r' => null, + 'print_object' => null, + // Dangerous functions. From coding style. + 'extract' => null, + ]; } diff --git a/moodle/ruleset.xml b/moodle/ruleset.xml index 962f3104..212084f8 100644 --- a/moodle/ruleset.xml +++ b/moodle/ruleset.xml @@ -24,7 +24,6 @@ - diff --git a/moodle/tests/fixtures/moodle_php_deprecatedfunctions.php b/moodle/tests/fixtures/moodle_php_deprecatedfunctions.php new file mode 100644 index 00000000..863893c8 --- /dev/null +++ b/moodle/tests/fixtures/moodle_php_deprecatedfunctions.php @@ -0,0 +1,10 @@ +verify_cs_results(); } + public function test_moodle_php_deprecatedfunctions() { + + // Define the standard, sniff and fixture to use. + $this->set_standard('moodle'); + $this->set_sniff('moodle.PHP.DeprecatedFunctions'); + $this->set_fixture(__DIR__ . '/fixtures/moodle_php_deprecatedfunctions.php'); + + // Define expected results (errors and warnings). Format, array of: + // - line => number of problems, or + // - line => array of contents for message / source problem matching. + // - line => string of contents for message / source problem matching (only 1). + $this->set_errors(array()); + $warnings = array(7 => 'print_error() has been deprecated; use throw new moodle_exception()'); + if (PHP_VERSION_ID >= 70300 && PHP_VERSION_ID < 80000) { + $warnings[10] = 'mbsplit() has been deprecated'; + } + $this->set_warnings($warnings); + + // Let's do all the hard work! + $this->verify_cs_results(); + } + public function test_moodle_php_forbiddenfunctions() { // Define the standard, sniff and fixture to use. diff --git a/tests/local_codechecker_testcase.php b/tests/local_codechecker_testcase.php index 43a42e64..dac7757a 100644 --- a/tests/local_codechecker_testcase.php +++ b/tests/local_codechecker_testcase.php @@ -191,7 +191,7 @@ protected function set_errors(array $errors) { // Let's normalize numeric, empty and string errors. foreach ($this->errors as $line => $errordef) { if (is_int($errordef) and $errordef > 0) { - $this->errors[$line] = array_fill(0, $errordef, null); + $this->errors[$line] = array_fill(0, $errordef, $errordef); } else if (empty($errordef)) { $this->errors[$line] = array(); } else if (is_string($errordef)) { @@ -210,7 +210,7 @@ protected function set_warnings(array $warnings) { // Let's normalize numeric, empty and string warnings. foreach ($this->warnings as $line => $warningdef) { if (is_int($warningdef) and $warningdef > 0) { - $this->warnings[$line] = array_fill(0, $warningdef, null); + $this->warnings[$line] = array_fill(0, $warningdef, $warningdef); } else if (empty($warningdef)) { $this->warnings[$line] = array(); } else if (is_string($warningdef)) {