diff --git a/composer.json b/composer.json
index de42e103..a50c8a44 100644
--- a/composer.json
+++ b/composer.json
@@ -5,7 +5,7 @@
"license": "GPL-3.0-or-later",
"require": {
"php": ">=7.4",
- "moodlehq/moodle-cs": "^v3.4.7",
+ "moodlehq/moodle-cs": "^v3.4.8",
"phpcompatibility/php-compatibility": "dev-develop#96072c30"
},
"config": {
diff --git a/composer.lock b/composer.lock
index d97745b9..166057fc 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "557c6dacc8e1efaa1f0ff81060ceddb6",
+ "content-hash": "bd0a96a20590db32325fc04f2993298a",
"packages": [
{
"name": "dealerdirect/phpcodesniffer-composer-installer",
@@ -86,16 +86,16 @@
},
{
"name": "moodlehq/moodle-cs",
- "version": "v3.4.7",
+ "version": "v3.4.8",
"source": {
"type": "git",
"url": "https://github.com/moodlehq/moodle-cs.git",
- "reference": "4f1bc63551da69675d9f5d17efbf35a458ca3da8"
+ "reference": "91661a17a23ed17e7ae4276f8c19df789b8882c2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/moodlehq/moodle-cs/zipball/4f1bc63551da69675d9f5d17efbf35a458ca3da8",
- "reference": "4f1bc63551da69675d9f5d17efbf35a458ca3da8",
+ "url": "https://api.github.com/repos/moodlehq/moodle-cs/zipball/91661a17a23ed17e7ae4276f8c19df789b8882c2",
+ "reference": "91661a17a23ed17e7ae4276f8c19df789b8882c2",
"shasum": ""
},
"require": {
@@ -152,7 +152,7 @@
"source": "https://github.com/moodlehq/moodle-cs",
"wiki": "https://github.com/moodlehq/moodle-cs/wiki"
},
- "time": "2024-05-31T16:28:39+00:00"
+ "time": "2024-06-14T14:47:25+00:00"
},
{
"name": "phpcompatibility/php-compatibility",
@@ -170,8 +170,8 @@
},
"require": {
"php": ">=5.4",
- "phpcsstandards/phpcsutils": "^1.0.9",
- "squizlabs/php_codesniffer": "^3.9.0"
+ "phpcsstandards/phpcsutils": "^1.0.12",
+ "squizlabs/php_codesniffer": "^3.10.0"
},
"replace": {
"wimg/php-compatibility": "*"
@@ -242,7 +242,7 @@
"type": "open_collective"
}
],
- "time": "2024-04-30T23:24:59+00:00"
+ "time": "2024-06-07T09:46:11+00:00"
},
{
"name": "phpcsstandards/phpcsextra",
diff --git a/vendor/autoload.php b/vendor/autoload.php
index c772c200..39f3cf97 100644
--- a/vendor/autoload.php
+++ b/vendor/autoload.php
@@ -22,4 +22,4 @@
require_once __DIR__ . '/composer/autoload_real.php';
-return ComposerAutoloaderInit557c6dacc8e1efaa1f0ff81060ceddb6::getLoader();
+return ComposerAutoloaderInitbd0a96a20590db32325fc04f2993298a::getLoader();
diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php
index 5a444cb8..d43dec46 100644
--- a/vendor/composer/autoload_real.php
+++ b/vendor/composer/autoload_real.php
@@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
-class ComposerAutoloaderInit557c6dacc8e1efaa1f0ff81060ceddb6
+class ComposerAutoloaderInitbd0a96a20590db32325fc04f2993298a
{
private static $loader;
@@ -24,12 +24,12 @@ public static function getLoader()
require __DIR__ . '/platform_check.php';
- spl_autoload_register(array('ComposerAutoloaderInit557c6dacc8e1efaa1f0ff81060ceddb6', 'loadClassLoader'), true, true);
+ spl_autoload_register(array('ComposerAutoloaderInitbd0a96a20590db32325fc04f2993298a', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
- spl_autoload_unregister(array('ComposerAutoloaderInit557c6dacc8e1efaa1f0ff81060ceddb6', 'loadClassLoader'));
+ spl_autoload_unregister(array('ComposerAutoloaderInitbd0a96a20590db32325fc04f2993298a', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
- call_user_func(\Composer\Autoload\ComposerStaticInit557c6dacc8e1efaa1f0ff81060ceddb6::getInitializer($loader));
+ call_user_func(\Composer\Autoload\ComposerStaticInitbd0a96a20590db32325fc04f2993298a::getInitializer($loader));
$loader->register(true);
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index 355deead..7e9c208f 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -4,7 +4,7 @@
namespace Composer\Autoload;
-class ComposerStaticInit557c6dacc8e1efaa1f0ff81060ceddb6
+class ComposerStaticInitbd0a96a20590db32325fc04f2993298a
{
public static $prefixLengthsPsr4 = array (
'P' =>
@@ -72,9 +72,9 @@ class ComposerStaticInit557c6dacc8e1efaa1f0ff81060ceddb6
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
- $loader->prefixLengthsPsr4 = ComposerStaticInit557c6dacc8e1efaa1f0ff81060ceddb6::$prefixLengthsPsr4;
- $loader->prefixDirsPsr4 = ComposerStaticInit557c6dacc8e1efaa1f0ff81060ceddb6::$prefixDirsPsr4;
- $loader->classMap = ComposerStaticInit557c6dacc8e1efaa1f0ff81060ceddb6::$classMap;
+ $loader->prefixLengthsPsr4 = ComposerStaticInitbd0a96a20590db32325fc04f2993298a::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInitbd0a96a20590db32325fc04f2993298a::$prefixDirsPsr4;
+ $loader->classMap = ComposerStaticInitbd0a96a20590db32325fc04f2993298a::$classMap;
}, null, ClassLoader::class);
}
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index 2a74772b..23987323 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -83,17 +83,17 @@
},
{
"name": "moodlehq/moodle-cs",
- "version": "v3.4.7",
- "version_normalized": "3.4.7.0",
+ "version": "v3.4.8",
+ "version_normalized": "3.4.8.0",
"source": {
"type": "git",
"url": "https://github.com/moodlehq/moodle-cs.git",
- "reference": "4f1bc63551da69675d9f5d17efbf35a458ca3da8"
+ "reference": "91661a17a23ed17e7ae4276f8c19df789b8882c2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/moodlehq/moodle-cs/zipball/4f1bc63551da69675d9f5d17efbf35a458ca3da8",
- "reference": "4f1bc63551da69675d9f5d17efbf35a458ca3da8",
+ "url": "https://api.github.com/repos/moodlehq/moodle-cs/zipball/91661a17a23ed17e7ae4276f8c19df789b8882c2",
+ "reference": "91661a17a23ed17e7ae4276f8c19df789b8882c2",
"shasum": ""
},
"require": {
@@ -116,7 +116,7 @@
"sebastian/phpcpd": "^6.0",
"thor-juhasz/phpunit-coverage-check": "^0.3.0"
},
- "time": "2024-05-31T16:28:39+00:00",
+ "time": "2024-06-14T14:47:25+00:00",
"type": "phpcodesniffer-standard",
"installation-source": "dist",
"autoload": {
@@ -171,8 +171,8 @@
},
"require": {
"php": ">=5.4",
- "phpcsstandards/phpcsutils": "^1.0.9",
- "squizlabs/php_codesniffer": "^3.9.0"
+ "phpcsstandards/phpcsutils": "^1.0.12",
+ "squizlabs/php_codesniffer": "^3.10.0"
},
"replace": {
"wimg/php-compatibility": "*"
@@ -188,7 +188,7 @@
"suggest": {
"roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
},
- "time": "2024-04-30T23:24:59+00:00",
+ "time": "2024-06-07T09:46:11+00:00",
"default-branch": true,
"type": "phpcodesniffer-standard",
"extra": {
diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php
index fd29dbe8..bc24b234 100644
--- a/vendor/composer/installed.php
+++ b/vendor/composer/installed.php
@@ -3,7 +3,7 @@
'name' => 'moodlehq/local_codechecker',
'pretty_version' => 'dev-main',
'version' => 'dev-main',
- 'reference' => '6317d1361ee40d2ebcdc40c36b40cfa9fb3f98a9',
+ 'reference' => '3ec6941b73874fb0bc33ded196e8a5c6fe80be6f',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -22,16 +22,16 @@
'moodlehq/local_codechecker' => array(
'pretty_version' => 'dev-main',
'version' => 'dev-main',
- 'reference' => '6317d1361ee40d2ebcdc40c36b40cfa9fb3f98a9',
+ 'reference' => '3ec6941b73874fb0bc33ded196e8a5c6fe80be6f',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
'moodlehq/moodle-cs' => array(
- 'pretty_version' => 'v3.4.7',
- 'version' => '3.4.7.0',
- 'reference' => '4f1bc63551da69675d9f5d17efbf35a458ca3da8',
+ 'pretty_version' => 'v3.4.8',
+ 'version' => '3.4.8.0',
+ 'reference' => '91661a17a23ed17e7ae4276f8c19df789b8882c2',
'type' => 'phpcodesniffer-standard',
'install_path' => __DIR__ . '/../moodlehq/moodle-cs',
'aliases' => array(),
diff --git a/vendor/moodlehq/moodle-cs/moodle/Sniffs/Commenting/MissingDocblockSniff.php b/vendor/moodlehq/moodle-cs/moodle/Sniffs/Commenting/MissingDocblockSniff.php
index 8999a59a..82bc55f6 100644
--- a/vendor/moodlehq/moodle-cs/moodle/Sniffs/Commenting/MissingDocblockSniff.php
+++ b/vendor/moodlehq/moodle-cs/moodle/Sniffs/Commenting/MissingDocblockSniff.php
@@ -101,7 +101,7 @@ protected function processScopes(File $phpcsFile, int $stackPtr): void {
if ($fileblock === null) {
$objectName = TokenUtil::getObjectName($phpcsFile, $stackPtr);
- $phpcsFile->addError('Missing docblock for file %s', $stackPtr, 'Missing', [$objectName]);
+ $phpcsFile->addError('Missing docblock for file %s', $stackPtr, 'File', [$objectName]);
}
}
@@ -110,7 +110,7 @@ protected function processScopes(File $phpcsFile, int $stackPtr): void {
$objectName = TokenUtil::getObjectName($phpcsFile, $typePtr);
$objectType = TokenUtil::getObjectType($phpcsFile, $typePtr);
- $phpcsFile->addError('Missing docblock for %s %s', $typePtr, 'Missing', [$objectType, $objectName]);
+ $phpcsFile->addError('Missing docblock for %s %s', $typePtr, ucfirst($objectType), [$objectType, $objectName]);
}
if ($artifactCount === 1) {
@@ -208,7 +208,7 @@ protected function processFunctions(File $phpcsFile, int $stackPtr): void {
);
}
} else {
- $phpcsFile->addError('Missing docblock for %s %s', $typePtr, 'Missing', [$objectType, $objectName]);
+ $phpcsFile->addError('Missing docblock for %s %s', $typePtr, ucfirst($objectType), [$objectType, $objectName]);
}
}
}
@@ -255,14 +255,14 @@ protected function processConstants(File $phpcsFile, int $stackPtr): void {
$phpcsFile->addError(
'Missing docblock for constant %s::%s',
$typePtr,
- 'Missing',
+ 'Constant',
[$containerName, $objectName]
);
} else {
$phpcsFile->addError(
'Missing docblock for constant %s',
$typePtr,
- 'Missing',
+ 'Constant',
[$objectName]
);
}
diff --git a/vendor/moodlehq/moodle-cs/moodle/Sniffs/Files/BoilerplateCommentSniff.php b/vendor/moodlehq/moodle-cs/moodle/Sniffs/Files/BoilerplateCommentSniff.php
index 57efe20b..eb5f9870 100644
--- a/vendor/moodlehq/moodle-cs/moodle/Sniffs/Files/BoilerplateCommentSniff.php
+++ b/vendor/moodlehq/moodle-cs/moodle/Sniffs/Files/BoilerplateCommentSniff.php
@@ -30,7 +30,7 @@
class BoilerplateCommentSniff implements Sniff
{
- protected static $comment = [
+ protected static array $comment = [
"// This file is part of",
"//",
"// Moodle is free software: you can redistribute it and/or modify",
@@ -44,80 +44,231 @@ class BoilerplateCommentSniff implements Sniff
"// GNU General Public License for more details.",
"//",
"// You should have received a copy of the GNU General Public License",
- "// along with Moodle. If not, see .",
+ "// along with Moodle. If not, see .",
];
- public function register() {
+
+ public string $productName = 'Moodle';
+
+ public string $firstLinePostfix = ' - https://moodle.org/';
+
+ public function register(): array
+ {
return [T_OPEN_TAG];
}
- public function process(File $file, $stackptr) {
+ public function process(File $phpcsFile, $stackPtr): void
+ {
// We only want to do this once per file.
- $prevopentag = $file->findPrevious(T_OPEN_TAG, $stackptr - 1);
+ $prevopentag = $phpcsFile->findPrevious(T_OPEN_TAG, $stackPtr - 1);
if ($prevopentag !== false) {
return; // @codeCoverageIgnore
}
- if ($stackptr > 0) {
- $file->addError('The first thing in a PHP file must be the 0) {
+ $phpcsFile->addError('The first thing in a PHP file must be the getTokens();
+ $tokens = $phpcsFile->getTokens();
// Allow T_PHPCS_XXX comment annotations in the first line (skip them).
- if ($commentptr = $file->findNext(Tokens::$phpcsCommentTokens, $stackptr + 1, $stackptr + 3)) {
- $stackptr = $commentptr;
+ if ($commentptr = $phpcsFile->findNext(Tokens::$phpcsCommentTokens, $stackPtr + 1, $stackPtr + 3)) {
+ $stackPtr = $commentptr;
}
- // Find count the number of newlines after the opening findNext(T_COMMENT, $expectedafter + 1);
+
+ // Check that it appears to be a Moodle boilerplate comment.
+ $regex = $this->regexForLine(self::$comment[0]);
+ $boilerplatefound = ($firstcommentptr !== false) && preg_match($regex, $tokens[$firstcommentptr]['content']);
- if ($numnewlines > 0) {
- $file->addError(
- 'The opening addFixableError(
+ 'Moodle boilerplate not found',
+ $stackPtr,
+ 'NoBoilerplateComment'
);
+
+ if ($fix) {
+ $this->insertBoilerplate($phpcsFile, $expectedafter);
+ }
return;
}
- $offset = $stackptr + $numnewlines + 1;
// Now check the text of the comment.
+ $textfixed = false;
+ $tokenptr = $firstcommentptr;
foreach (self::$comment as $lineindex => $line) {
- $tokenptr = $offset + $lineindex;
+ // We already checked the first line.
+ if ($lineindex === 0) {
+ continue;
+ }
+
+ $tokenptr = $firstcommentptr + $lineindex;
+ $iseof = $tokenptr >= $phpcsFile->numTokens;
+
+ if ($iseof || $tokens[$tokenptr]['code'] != T_COMMENT || strpos($tokens[$tokenptr]['content'], '//') !== 0) {
+ $errorline = $iseof ? $tokenptr - 1 : $tokenptr;
+
+ $fix = $phpcsFile->addFixableError(
+ 'Comment does not contain full Moodle boilerplate',
+ $errorline,
+ 'CommentEndedTooSoon'
+ );
+
+ if ($fix) {
+ $this->completeBoilerplate($phpcsFile, $tokenptr - 1, $lineindex);
+ return;
+ }
- if (!array_key_exists($tokenptr, $tokens)) {
- $file->addError('Reached the end of the file before finding ' .
- 'all of the opening comment.', $tokenptr - 1, 'FileTooShort');
+ // No point checking whitespace after comment if it is incomplete.
return;
}
- $regex = str_replace(
- ['Moodle', 'http\\:'],
- ['.*', 'https?\\:'],
- '/^' . preg_quote($line, '/') . '/'
- );
+ $regex = $this->regexForLine($line);
- if (
- $tokens[$tokenptr]['code'] != T_COMMENT ||
- !preg_match($regex, $tokens[$tokenptr]['content'])
- ) {
- $file->addError(
+ if (!preg_match($regex, $tokens[$tokenptr]['content'])) {
+ $fix = $phpcsFile->addFixableError(
'Line %s of the opening comment must start "%s".',
$tokenptr,
'WrongLine',
[$lineindex + 1, $line]
);
+
+ if ($fix) {
+ $phpcsFile->fixer->replaceToken($tokenptr, $line . "\n");
+ $textfixed = true;
+ }
+ }
+ }
+
+ if ($firstcommentptr !== $expectedafter + 1) {
+ $fix = $phpcsFile->addFixableError(
+ 'Moodle boilerplate not found at first line',
+ $expectedafter + 1,
+ 'NotAtFirstLine'
+ );
+
+ // If the boilerplate comment has been changed we need to commit the fixes before
+ // moving it.
+ if ($fix && !$textfixed) {
+ $this->moveBoilerplate($phpcsFile, $firstcommentptr, $expectedafter);
}
+
+ // There's no point in checking the whitespace after the boilerplate
+ // if it's not in the right place.
+ return;
+ }
+
+ if ($tokenptr === $phpcsFile->numTokens - 1) {
+ return;
+ }
+
+ $tokenptr++;
+
+ $nextnonwhitespace = $phpcsFile->findNext(T_WHITESPACE, $tokenptr, null, true);
+
+ // Allow indentation.
+ if ($nextnonwhitespace !== false && strpos($tokens[$nextnonwhitespace - 1]['content'], "\n") === false) {
+ $nextnonwhitespace--;
}
+
+ if (
+ ($nextnonwhitespace === false) && array_key_exists($tokenptr + 1, $tokens) ||
+ ($nextnonwhitespace !== false && $nextnonwhitespace !== $tokenptr + 1)
+ ) {
+ $fix = $phpcsFile->addFixableError(
+ 'Boilerplate comment must be followed by a single blank line or end of file',
+ $tokenptr,
+ 'SingleTrailingNewLine'
+ );
+
+ if ($fix) {
+ if ($nextnonwhitespace === false) {
+ while (array_key_exists(++$tokenptr, $tokens)) {
+ $phpcsFile->fixer->replaceToken($tokenptr, '');
+ }
+ } elseif ($nextnonwhitespace === $tokenptr) {
+ $phpcsFile->fixer->addContentBefore($tokenptr, "\n");
+ } else {
+ while (++$tokenptr < $nextnonwhitespace) {
+ if ($tokens[$tokenptr]['content'][-1] === "\n") {
+ $phpcsFile->fixer->replaceToken($tokenptr, '');
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private function fullComment(): array
+ {
+ $result = [];
+ foreach (self::$comment as $lineindex => $line) {
+ if ($lineindex === 0) {
+ $result[] = $line . ' ' . $this->productName . $this->firstLinePostfix;
+ } else {
+ $result[] = str_replace('Moodle', $this->productName, $line);
+ }
+ }
+ return $result;
+ }
+
+ private function insertBoilerplate(File $file, int $stackptr): void
+ {
+ $prefix = substr($file->getTokens()[$stackptr]['content'], -1) === "\n" ? '' : "\n";
+ $file->fixer->addContent($stackptr, $prefix . implode("\n", $this->fullComment()) . "\n");
+ }
+
+ private function moveBoilerplate(File $file, int $start, int $target): void
+ {
+ $tokens = $file->getTokens();
+
+ $file->fixer->beginChangeset();
+
+ // If we have only whitespace between expected location and first comment, just remove it.
+ $nextnonwhitespace = $file->findPrevious(T_WHITESPACE, $start - 1, $target, true);
+
+ if ($nextnonwhitespace === false || $nextnonwhitespace === $target) {
+ foreach (range($target + 1, $start - 1) as $whitespaceptr) {
+ $file->fixer->replaceToken($whitespaceptr, '');
+ }
+ $file->fixer->endChangeset();
+ return;
+ }
+
+ // Otherwise shift existing comment to correct place.
+ $existingboilerplate = [];
+ foreach (range(0, count(self::$comment)) as $lineindex) {
+ $tokenptr = $start + $lineindex;
+
+ $existingboilerplate[] = $tokens[$tokenptr]['content'];
+
+ $file->fixer->replaceToken($tokenptr, '');
+ }
+
+ $file->fixer->addContent($target, implode("", $existingboilerplate) . "\n");
+
+ $file->fixer->endChangeset();
+ }
+
+ private function completeBoilerplate(File $file, $stackptr, int $lineindex): void
+ {
+ $file->fixer->addContent($stackptr, implode("\n", array_slice($this->fullComment(), $lineindex)) . "\n");
+ }
+
+ /**
+ * @param string $line
+ * @return string
+ */
+ private function regexForLine(string $line): string
+ {
+ return str_replace(
+ ['Moodle', 'https\\:'],
+ ['.*', 'https?\\:'],
+ '/^' . preg_quote($line, '/') . '/'
+ );
}
}
diff --git a/vendor/moodlehq/moodle-cs/moodle/Sniffs/PHPUnit/TestCaseCoversSniff.php b/vendor/moodlehq/moodle-cs/moodle/Sniffs/PHPUnit/TestCaseCoversSniff.php
index de5dece5..0d77d5f4 100644
--- a/vendor/moodlehq/moodle-cs/moodle/Sniffs/PHPUnit/TestCaseCoversSniff.php
+++ b/vendor/moodlehq/moodle-cs/moodle/Sniffs/PHPUnit/TestCaseCoversSniff.php
@@ -85,6 +85,7 @@ public function process(File $file, $pointer) {
$class = $file->getDeclarationName($cStart);
$classCovers = false; // To control when the class has a @covers tag.
$classCoversNothing = false; // To control when the class has a @coversNothing tag.
+ $classCoversDefaultClass = []; // To annotate all the existing @coversDefaultClass tags.
// Only if the class is extending something.
// TODO: We could add a list of valid classes once we have a class-map available.
@@ -121,12 +122,30 @@ public function process(File $file, $pointer) {
case '@coversDefaultClass':
// Validate basic syntax (FQCN).
$this->checkCoversTagsSyntax($file, $docPointer, '@coversDefaultClass');
+ $classCoversDefaultClass[] = $docPointer; // Annotated for later checks.
break;
}
}
}
}
+ // If we have found more than one @coversDefaultClass, that's an error.
+ if (count($classCoversDefaultClass) > 1) {
+ // We have to reverse the array to get them in correct order and then
+ // remove the 1st one that is correct/allowed.
+ $classCoversDefaultClass = array_reverse($classCoversDefaultClass);
+ array_shift($classCoversDefaultClass);
+ // Report the remaining ones.
+ foreach ($classCoversDefaultClass as $classCoversDefaultClassPointer) {
+ $file->addError(
+ 'Class %s has more than one @coversDefaultClass tag, only one allowed',
+ $classCoversDefaultClassPointer,
+ 'MultipleDefaultClass',
+ [$class]
+ );
+ }
+ }
+
// Both @covers and @coversNothing, that's a mistake. 2 errors.
if ($classCovers && $classCoversNothing) {
$file->addError(