Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

5.0 | Proposal: add return types to select methods #391

Open
jrfnl opened this issue Mar 13, 2024 · 3 comments
Open

5.0 | Proposal: add return types to select methods #391

jrfnl opened this issue Mar 13, 2024 · 3 comments

Comments

@jrfnl
Copy link
Member

jrfnl commented Mar 13, 2024

Setting the scene

At this time, it has not yet been decided what the minimum supported PHP version will be for PHPCS 5.0, though it will be PHP 7.2 or higher (as PHP 7.2 is the new minimum supported PHP version for PHCPS 4.0).

PHP 7.2 allows for:

  • Adding parameter type declarations, with the exception of union/intersection/DNF/mixed types.
  • Adding return type declarations, with the exception of union/intersection/DNF/mixed types.

PHP 7.2 does not support:

  • Property type declarations (PHP 7.4+)
  • Constant type declarations (PHP 8.3+)
  • Union/intersection/DNF types (PHP 8.0, 8.1, 8.2 respectively).
  • Mixed type (PHP 8.0)
  • Null/true/false as in union types or as stand-alone types (PHP 8.0/8/2)

Parameter type declarations in PHP are contravariant, which means that overloaded methods in child classes can widen the type.
Return type declarations in PHP are covariant, which means that overloaded methods in child classes can make the type more narrow.

Full support for contravariance and covariance was only added in PHP 7.4.

Proposal

I propose to add return type declarations to the public/protected methods in PHPCS 5.0 as per the list below - providing the minimum PHP version will allow for it (to be decided and discussed in a separate issue).

If any of the signature changes can not be made yet due to the minimum PHP version not allowing for it (yet), those changes (and only those) will move to the first major release which has the required minimum PHP version.

Detailed proposal

Notes about the detailed proposal:

  • Signatures of private methods methods are excluded from the below list as they don't affect the public API, but these will be updated too.
  • Methods for which no legal return type can be added, have been left out of the list.
  • Methods which will need to be updated by extension and do not deviate from the change to the "parent" method, are not listed repetitively.
  • The return types have largely been based on the currently documented types, which may not be correct (in which case the type will be changed to the real type).
  • The list is based on the current state of the codebase and is subject to change.

In PHP_CodeSniffer\Config:

  • public function __get(...): mixed (* PHP 8.0+)
  • public function __set(...): void
  • public function __isset(...): bool
  • public function __unset(...): void
  • public function getSettings(): array
  • public function setSettings(...): void
  • public function setCommandLineValues(...): void
  • public function restoreDefaults(): void
  • public function processShortArgument(...): void
  • public function processLongArgument(...): void
  • public function processUnknownArgument(...): void
  • public function processFilePath(...): void
  • public function printUsage(): void
  • public function printPHPCSUsage(): void
  • public function printPHPCBFUsage(): void
  • public static function getConfigData(...): ?string
  • public static function getExecutablePath(...): ?string
  • public function setConfigData(...): bool
  • public static function getAllConfigData(): array
  • public function printConfigData(...): void

In PHP_CodeSniffer\Fixer:

  • public function startFile(...): void
  • public function fixFile(): bool
  • public function generateDiff(...): string
  • public function getFixCount(): int
  • public function getContents(): string
  • public function getTokenContent(...): string
  • public function endChangeset(): ? (under discussion, see Ensure that Fixer::endChangeset() returns the real outcome #375)
  • public function rollbackChangeset(): void
  • public function replaceToken(...): bool
  • public function revertToken(...): bool
  • public function substrToken(...): bool
  • public function addNewline(...): bool
  • public function addNewlineBefore(...): bool
  • public function addContent(...): bool
  • public function addContentBefore(...): bool
  • public function changeCodeBlockIndent(...): void

In PHP_CodeSniffer\Reporter:

  • public function printReports(): bool
  • public function printReport(...): void
  • public function cacheFileReport(...): void
  • public function prepareFileReport(...): array

In PHP_CodeSniffer\Ruleset:

  • public function explain(): void
  • public function hasSniffDeprecations(): bool
  • public function showSniffDeprecations(): void
  • public function processRuleset(...): array
  • public function registerSniffs(...): void
  • public function populateTokenListeners(): void
  • public function setSniffProperty(...): void
  • public function getIgnorePatterns(...): array
  • public function getIncludePatterns(...): array

In PHP_CodeSniffer\Runner:

  • public function runPHPCS(): int
  • public function runPHPCBF(): int
  • public function checkRequirements(): void
  • public function init(): void
  • public function handleErrors(...): bool
  • public function processFile(...): void
  • public function printProgress(...): void

In PHP_CodeSniffer\Files\File:

  • public function setContent(): void
  • public function reloadContent(): void
  • public function disableCaching(): void
  • public function process(): void
  • public function parse(): void
  • public function getTokens(): array
  • public function cleanUp(): void
  • public function addError(...): bool
  • public function addWarning(...): bool
  • public function addErrorOnLine(...): bool
  • public function addWarningOnLine(...): bool
  • public function addFixableError(...): bool
  • public function addFixableWarning(...): bool
  • protected function addMessage(...): bool
  • public function recordMetric(...): bool
  • public function getErrorCount(): int
  • public function getWarningCount(): int
  • public function getFixableCount(): int
  • public function getFixedCount(): int
  • public function getIgnoredLines(): array
  • public function getErrors(): array
  • public function getWarnings(): array
  • public function getMetrics(): array
  • public function getListenerTimes(): array
  • public function getFilename(): string
  • public function getDeclarationName(...): ?string
  • public function getMethodParameters(...): array
  • public function getMethodProperties(...): array
  • public function getMemberProperties(...): array
  • public function getClassProperties(...): array
  • public function isReference(...): bool
  • public function getTokensAsString(...): string
  • public function findPrevious(...): int|false (* PHP 8.0+)
  • public function findNext(...): int|false (* PHP 8.0+)
  • public function findStartOfStatement(...): int
  • public function findEndOfStatement(...): int
  • public function findFirstOnLine(...): int|false (* PHP 8.0+)
  • public function hasCondition(...): bool
  • public function getCondition(...): int|false (* PHP 8.0+)
  • public function findExtendedClassName(...): string|false (* PHP 8.0+)
  • public function findImplementedInterfaceNames(...): array|false (* PHP 8.0+)

In PHP_CodeSniffer\Files\DummyFile:

  • public function setErrorCounts(...): void

In PHP_CodeSniffer\Files\FileList:

  • public function addFile(...): void
  • public function rewind(): void
  • public function current(): \PHP_CodeSniffer\Files\File
  • public function key(): ?string
  • public function next(): void
  • public function valid(): bool
  • public function count(): int

In PHP_CodeSniffer\Filters\Filter:

  • public function accept(): bool
  • public function getChildren(): \RecursiveIterator
  • protected function shouldProcessFile(...): bool
  • protected function shouldIgnorePath(...): bool

In PHP_CodeSniffer\Filters\ExactMatch:

  • abstract protected function getDisallowedFiles(): array
  • abstract protected function getAllowedFiles(): array

In PHP_CodeSniffer\Filters\Git*:

  • protected function exec(...): array

In PHP_CodeSniffer\Generators\Generator:

  • protected function getTitle(...): string
  • public function generate(): void
  • abstract protected function processSniff(...): void

In PHP_CodeSniffer\Generators\*:

All methods will get the void return type.

In PHP_CodeSniffer\Reports\Report:

  • public function generateFileReport(...): bool
  • public function generate(...): void

In PHP_CodeSniffer\Sniffs\Sniff:

  • public function register(): array

Note: the public function process(...) method will not get a return type as int|void is an illegal type, even in PHP 8.0+ and ?int is incompatible with a child class having a void return type.
Classes implementing the interface can still add a return type if the type returned from their implementation is consistently void or int.

In PHP_CodeSniffer\Sniffs\AbstractArraySniff:

  • public function process(...): void
  • abstract protected function processSingleLineArray(...): void
  • abstract protected function processMultiLineArray(...): void

In PHP_CodeSniffer\Sniffs\AbstractPatternSniff:

  • final public function process(...): void
  • protected function processPattern(...): array|false (* PHP 8.0+)
  • protected function prepareError(...): string
  • abstract protected function getPatterns(): array
  • protected function registerSupplementary(): array
  • protected function processSupplementary(...): void

In PHP_CodeSniffer\Sniffs\AbstractScopeSniff:

  • abstract protected function processTokenWithinScope(...): ?int
  • abstract protected function processTokenOutsideScope(...): ?int

In PHP_CodeSniffer\Sniffs\DeprecatedSniff:

  • public function getDeprecationVersion(): string
  • public function getRemovalVersion(): string
  • public function getDeprecationMessage(): string

In PHP_CodeSniffer\Tokenizers\Tokenizer:

  • protected function isMinifiedContent(...): bool
  • public function getTokens(): array
  • abstract protected function tokenize(...): array
  • abstract protected function processAdditional(): void
  • public function replaceTabsInToken(...): void

In PHP_CodeSniffer\Tokenizers\Comment:

  • public function tokenizeString(...): array

In PHP_CodeSniffer\Tokenizers\PHP:

  • public static function standardiseToken(...): array
  • public static function resolveSimpleToken(...): array

In PHP_CodeSniffer\Util\Cache:

  • public static function load(...): void
  • public static function save(): void
  • public static function get(...): mixed (* PHP 8.0+)
  • public static function set(...): void
  • public static function getSize(): int

In PHP_CodeSniffer\Util\Common:

  • public static function isPharFile(...): bool
  • public static function isReadable(...): bool
  • public static function realpath(...): string|false (* PHP 8.0+)
  • public static function stripBasepath(...): string
  • public static function detectLineEndings(...): string
  • public static function isStdinATTY(): bool
  • public static function escapeshellcmd(...): string
  • public static function prepareForOutput(...): string
  • public static function isCamelCaps(...): bool
  • public static function isUnderscoreName(...): bool
  • public static function suggestType(...): string
  • public static function getSniffCode(...): string
  • public static function cleanSniffClass(...): string

In PHP_CodeSniffer\Util\Standards:

  • public static function getInstalledStandardPaths(): array
  • public static function getInstalledStandardDetails(...): array
  • public static function getInstalledStandards(...): array
  • public static function isInstalledStandard(...): bool
  • public static function getInstalledStandardPath(...): ?string
  • public static function printInstalledStandards(): void

In PHP_CodeSniffer\Util\Timing:

  • public static function startTiming(): void
  • public static function getDuration(): float
  • public static function getHumanReadableDuration(...): string
  • public static function printRunTime(...): void

In PHP_CodeSniffer\Util\Tokens:

  • public static function tokenName(...): string
  • public static function getHighestWeightedToken(...): int|false (* PHP 8.0+)

In PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest:

  • protected function getTestFiles(...): array
  • protected function shouldSkipTest(): bool
  • public function setCliValues(...): void
  • protected function getErrorList(): array
  • protected function getWarningList(): array

[*] The above list may still receive some updates over time. If/when updates are made, a comment will be left in the thread detailing the updates which were made.

New methods

Any new methods being introduced to PHPCS during the lifetime of the 4.x major should be considered outside the scope of this issue and can get a return type declaration at their introduction, providing the minimum PHP version allows for it.

New methods which need a type not yet supported via the current minimum PHP version should be added to the list above.

BC considerations

As return types are covariant, adding these type declarations in PHPCS 5.0 constitutes a backward compatibility break as the method signature of overloaded/implemented methods MUST be updated to include a compatible return type declaration.

Suggested Migration Path for External Standards

During the lifetime of PHPCS 4.x, external standards are encouraged to:

  • Add parameter type declarations to their code, in line with the proposal in 4.0 | Proposal: add parameter types to methods in PHPCS #390.
  • Add return type declarations to their code, in line with the above proposal
  • Add strict_types=1 declarations to their files.
  • Use tooling like PHPStan and/or Psalm to find further type juggling issues.

None of this can/will be enforced, but external standards using the lifetime of PHPCS 4.x to action the above will be better prepared for supporting PHPCS 5.x when the time comes.

Opinions ?

/cc @asispts @dingo-d @fredden @GaryJones @greg0ire @kukulich @michalbundyra @Ocramius @sirbrillig @stronk7 @weierophinney @wimg

@greg0ire
Copy link

This sounds great! You mentioned private methods, what about final classes and methods? Should they benefit from return type declarations on 3.x already?

@jrfnl
Copy link
Member Author

jrfnl commented Mar 13, 2024

This sounds great! You mentioned private methods, what about final classes and methods? Should they benefit from return type declarations on 3.x already?

@greg0ire 3.x has a PHP 5.4 minimum, so that's a no-go. As for 4.x, I'd have to see, though there's enough to do for 4.0 already, so I think it might be better to leave it till 5.0.

@sirbrillig
Copy link

I think the more types the better, so I'd vote yes for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants