Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions contributing/internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,31 @@ afraid to use it when it's needed and can help things.
- Start simple, refactor as necessary to achieve clean separation of
code, but don't overdo it.

## Deprecations

Deprecations happen when code no longer fits its purpose or is superseded by better solutions.
In such cases, it's important that deprecations are documented clearly to guide developers during
their upgrade process.

- Add `@deprecated` tags in the doc block of deprecated functions, methods, classes, etc. Include
the version number where the deprecation was introduced, a description of why it's deprecated,
and recommended replacement(s), if any. If there are multiple alternative approaches, list all of them.

- Do not add `@deprecated` tags to a function/method if you only mean to deprecate its parameter(s).
This will cause the whole function/method to be marked as deprecated by IDEs. Instead, trigger
a deprecation inside the function/method body if those parameters are passed. Ensure to include
the version number since when the deprecation occurred. Optionally, but encouraged, add a `@todo`
comment so that it can be found later on.

- User-facing deprecation warnings should also be triggered via the framework's deprecation handling
mechanism (e.g., `@trigger_error()` or error log entries) to alert end users.

- Do not add new tests for deprecated code paths. Instead, use tools and static analysis to ensure
that no code within the framework or official packages is using the deprecated functionality.

- Document all deprecations in the changelog file for that release, under a "Deprecations"
section, so users are informed when upgrading.

## Testing

Any new packages submitted to the framework must be accompanied by unit
Expand Down
2 changes: 1 addition & 1 deletion system/Language/en/Images.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@
'invalidDirection' => 'Flip direction can be only "vertical" or "horizontal". Given: "{0}"',
'exifNotSupported' => 'Reading EXIF data is not supported by this PHP installation.',

// @deprecated
// @deprecated 4.7.0
'libPathInvalid' => 'The path to your image library is not correct. Please set the correct path in your image preferences. "{0}"',
];
91 changes: 89 additions & 2 deletions tests/system/AutoReview/FrameworkCodeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,22 @@
final class FrameworkCodeTest extends TestCase
{
/**
* Cache of discovered test class names.
* Cache of source filenames.
*
* @var list<non-empty-string>
*/
private static array $sourceFiles = [];

/**
* Cache of test class names.
*
* @var list<class-string>
*/
private static array $testClasses = [];

/**
* @var list<string>
*/
private static array $recognizedGroupAttributeNames = [
'AutoReview',
'CacheLive',
Expand All @@ -42,6 +54,42 @@ final class FrameworkCodeTest extends TestCase
'SeparateProcess',
];

public function testDeprecationsAreProperlyVersioned(): void
{
$deprecationsWithoutVersion = [];

foreach ($this->getSourceFiles() as $file) {
$lines = file($file, FILE_IGNORE_NEW_LINES);

if ($lines === false) {
continue;
}

foreach ($lines as $number => $line) {
if (! str_contains($line, '@deprecated')) {
continue;
}

if (preg_match('/((?:\/\*)?\*|\/\/)\s+@deprecated\s+(?P<text>.+?)(?:\s*\*\s*)?$/', $line, $matches) === 1) {
$deprecationText = trim($matches['text']);

if (preg_match('/^v?\d+\.\d+/', $deprecationText) !== 1) {
$deprecationsWithoutVersion[] = sprintf('%s:%d', $file, ++$number);
}
}
}
}

$this->assertCount(
0,
$deprecationsWithoutVersion,
sprintf(
"The following lines contain @deprecated annotations without a version number:\n%s",
implode("\n", array_map(static fn (string $location): string => " * {$location}", $deprecationsWithoutVersion)),
),
);
}

/**
* @param class-string $class
*/
Expand Down Expand Up @@ -87,14 +135,16 @@ public static function provideEachTestClassHasCorrectGroupAttributeName(): itera
}
}

/**
* @return list<class-string>
*/
private static function getTestClasses(): array
{
if (self::$testClasses !== []) {
return self::$testClasses;
}

helper('filesystem');

$directory = set_realpath(dirname(__DIR__), true);

$iterator = new RecursiveIteratorIterator(
Expand Down Expand Up @@ -145,4 +195,41 @@ static function (SplFileInfo $file) use ($directory): string {

return $testClasses;
}

/**
* @return list<string>
*/
private function getSourceFiles(): array
{
if (self::$sourceFiles !== []) {
return self::$sourceFiles;
}

helper('filesystem');
$phpFiles = [];
$basePath = dirname(__DIR__, 3);

foreach (['system', 'app', 'tests'] as $dir) {
$directory = set_realpath($basePath . DIRECTORY_SEPARATOR . $dir, true);

$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(
$directory,
FilesystemIterator::SKIP_DOTS,
),
RecursiveIteratorIterator::CHILD_FIRST,
);

/** @var SplFileInfo $file */
foreach ($iterator as $file) {
if ($file->isFile() && str_ends_with($file->getPathname(), '.php')) {
$phpFiles[] = $file->getRealPath();
}
}
}

self::$sourceFiles = $phpFiles;

return $phpFiles;
}
}
2 changes: 1 addition & 1 deletion utils/phpstan-baseline/loader.neon
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# total 2115 errors
# total 2112 errors

includes:
- argument.type.neon
Expand Down
17 changes: 1 addition & 16 deletions utils/phpstan-baseline/missingType.iterableValue.neon
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# total 1258 errors
# total 1255 errors

parameters:
ignoreErrors:
Expand Down Expand Up @@ -4767,26 +4767,11 @@ parameters:
count: 1
path: ../../tests/system/AutoReview/ComposerJsonTest.php

-
message: '#^Method CodeIgniter\\AutoReview\\FrameworkCodeTest\:\:getTestClasses\(\) return type has no value type specified in iterable type array\.$#'
count: 1
path: ../../tests/system/AutoReview/FrameworkCodeTest.php

-
message: '#^Method CodeIgniter\\AutoReview\\FrameworkCodeTest\:\:provideEachTestClassHasCorrectGroupAttributeName\(\) return type has no value type specified in iterable type iterable\.$#'
count: 1
path: ../../tests/system/AutoReview/FrameworkCodeTest.php

-
message: '#^Property CodeIgniter\\AutoReview\\FrameworkCodeTest\:\:\$recognizedGroupAttributeNames type has no value type specified in iterable type array\.$#'
count: 1
path: ../../tests/system/AutoReview/FrameworkCodeTest.php

-
message: '#^Property CodeIgniter\\AutoReview\\FrameworkCodeTest\:\:\$testClasses type has no value type specified in iterable type array\.$#'
count: 1
path: ../../tests/system/AutoReview/FrameworkCodeTest.php

-
message: '#^Method CodeIgniter\\CLI\\CLITest\:\:provideTable\(\) return type has no value type specified in iterable type iterable\.$#'
count: 1
Expand Down
Loading