From 06858c10899e23c3c8e6717636c038b34f0b63ac Mon Sep 17 00:00:00 2001 From: Maksim Spirkov <63721828+max-s-lab@users.noreply.github.com> Date: Tue, 2 Sep 2025 10:10:56 +0500 Subject: [PATCH] Fix #20508: Fix PHPDoc, add PHPStan/Psalm annotations for `yii\web\CookieCollection::getIterator`. Add missing `@property` annotation in `yii\base\Model` --- build/controllers/PhpDocController.php | 33 +++++++++++++++++++++----- framework/CHANGELOG.md | 1 + framework/base/Action.php | 8 ++++--- framework/base/Model.php | 3 ++- framework/console/Controller.php | 4 ++-- framework/db/QueryBuilder.php | 2 +- framework/web/CookieCollection.php | 5 +++- 7 files changed, 42 insertions(+), 14 deletions(-) diff --git a/build/controllers/PhpDocController.php b/build/controllers/PhpDocController.php index 6d14d2d3a5..1b12eb3fa9 100644 --- a/build/controllers/PhpDocController.php +++ b/build/controllers/PhpDocController.php @@ -12,6 +12,7 @@ use yii\console\Controller; use yii\helpers\Console; use yii\helpers\FileHelper; use yii\helpers\Json; +use yii\web\Controller as WebController; /** * PhpDocController is there to help to maintain PHPDoc annotation in class files. @@ -22,6 +23,19 @@ use yii\helpers\Json; */ class PhpDocController extends Controller { + /** + * Manually added PHPDoc properties that do not need to be removed. + * + * @phpstan-var array + */ + private const MANUALLY_ADDED_PROPERTIES = [ + WebController::class => [ + 'request', + 'response', + 'view', + ], + ]; + /** * {@inheritdoc} */ @@ -535,7 +549,7 @@ class PhpDocController extends Controller } $oldDoc = $ref->getDocComment(); - $newDoc = $this->cleanDocComment($this->updateDocComment($oldDoc, $propertyDoc)); + $newDoc = $this->cleanDocComment($this->updateDocComment($oldDoc, $propertyDoc, $className)); $seenSince = false; $seenAuthor = false; @@ -614,8 +628,9 @@ class PhpDocController extends Controller * @param $properties * @return string */ - protected function updateDocComment($doc, $properties) + protected function updateDocComment($doc, $properties, $className) { + $manuallyAddedProperties = self::MANUALLY_ADDED_PROPERTIES[$className] ?? []; $lines = explode("\n", $doc); $propertyPart = false; $propertyPosition = false; @@ -632,7 +647,10 @@ class PhpDocController extends Controller $propertyPart = false; } if ($propertyPart) { - unset($lines[$i]); + preg_match('/@property\s+\w+\s+\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/', $line, $matches); + if (!isset($matches[1]) || !in_array($matches[1], $manuallyAddedProperties)) { + unset($lines[$i]); + } } } @@ -700,16 +718,16 @@ class PhpDocController extends Controller $gets = $this->match( '#\* @return (?[\w\\|\\\\\\[\\]]+)(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . - '[\s\n]{2,}public function (?get)(?\w+)\((?:,? ?\$\w+ ?= ?[^,]+)*\)#', + '[\s\n]{2,}(\#\[\\\\*.+\])*[\s\n]{2,}public function (?get)(?\w+)\((?:,? ?\$\w+ ?= ?[^,]+)*\)#', $class['content'], true); $sets = $this->match( '#\* @param (?[\w\\|\\\\\\[\\]]+) \$\w+(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . - '[\s\n]{2,}public function (?set)(?\w+)\(\$\w+(?:, ?\$\w+ ?= ?[^,]+)*\)#', + '[\s\n]{2,}(\#\[\\\\*.+\])*[\s\n]{2,}public function (?set)(?\w+)\(\$\w+(?:, ?\$\w+ ?= ?[^,]+)*\)#', $class['content'], true); // check for @property annotations in getter and setter $properties = $this->match( '#\* @(?property) (?[\w\\|\\\\\\[\\]]+)(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . - '[\s\n]{2,}public function [g|s]et(?\w+)\(((?:,? ?\$\w+ ?= ?[^,]+)*|\$\w+(?:, ?\$\w+ ?= ?[^,]+)*)\)#', + '[\s\n]{2,}(\#\[\\\\*.+\])*[\s\n]{2,}public function [g|s]et(?\w+)\(((?:,? ?\$\w+ ?= ?[^,]+)*|\$\w+(?:, ?\$\w+ ?= ?[^,]+)*)\)#', $class['content']); $acrs = array_merge($properties, $gets, $sets); @@ -791,6 +809,9 @@ class PhpDocController extends Controller protected function fixSentence($str) { + $str = rtrim($str, '*'); + $str = rtrim($str); + // TODO fix word wrap if ($str == '') { return ''; diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 123d161a05..9f4bc6d652 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -23,6 +23,7 @@ Yii Framework 2 Change Log - Bug #20494: Fix `PHPdoc`, add `PHPStan/Psalm` annotations for `authMethods` property in `CompositeAuth` class (terabytesoftw) - Bug #20485: Fix error `Cannot unset string offsets` in `yii\di\Instance:ensure(['__class' => ...], 'some\class\name')` (max-s-lab) - Enh #20505: `ArrayDataProvider` key handling with flexible path support (fetus-hina) +- Bug #20508: Fix PHPDoc, add PHPStan/Psalm annotations for `yii\web\CookieCollection::getIterator`. Add missing `@property` annotation in `yii\base\Model` (max-s-lab) 2.0.53 June 27, 2025 -------------------- diff --git a/framework/base/Action.php b/framework/base/Action.php index b6322e1805..3aa20608cd 100644 --- a/framework/base/Action.php +++ b/framework/base/Action.php @@ -30,13 +30,15 @@ use Yii; * * For more details and usage information on Action, see the [guide article on actions](guide:structure-controllers). * - * @template T of Controller * @property-read string $uniqueId The unique ID of this action among the whole application. - * @phpstan-property T $controller - * @psalm-property T $controller * * @author Qiang Xue * @since 2.0 + * + * @template T of Controller + * + * @phpstan-property T $controller + * @psalm-property T $controller */ class Action extends Component { diff --git a/framework/base/Model.php b/framework/base/Model.php index 01510c1acf..6e692ee130 100644 --- a/framework/base/Model.php +++ b/framework/base/Model.php @@ -43,9 +43,10 @@ use yii\validators\Validator; * @property-read array $errors Errors for all attributes or the specified attribute. Empty array is returned * if no error. See [[getErrors()]] for detailed description. Note that when returning errors for all attributes, * the result is a two-dimensional array, like the following: ```php [ 'username' => [ 'Username is required.', - * 'Username must contain only word characters.', ], 'email' => [ 'Email address is invalid.', ] ] ``` . + * 'Username must contain only word characters.', ], 'email' => [ 'Email address is invalid.', ] ] ```. * @property-read array $firstErrors The first errors. The array keys are the attribute names, and the array * values are the corresponding error messages. An empty array will be returned if there is no error. + * @property-read ArrayIterator $iterator An iterator for traversing the items in the list. * @property string $scenario The scenario that this model is in. Defaults to [[SCENARIO_DEFAULT]]. * @property-read ArrayObject|\yii\validators\Validator[] $validators All the validators declared in the * model. diff --git a/framework/console/Controller.php b/framework/console/Controller.php index 721124b10c..4c96f9e614 100644 --- a/framework/console/Controller.php +++ b/framework/console/Controller.php @@ -520,7 +520,7 @@ class Controller extends \yii\base\Controller * You may override this method to return customized summary. * The default implementation returns first line from the PHPDoc comment. * - * @return string + * @return string the one-line short summary describing this controller. */ public function getHelpSummary() { @@ -532,7 +532,7 @@ class Controller extends \yii\base\Controller * * You may override this method to return customized help. * The default implementation returns help information retrieved from the PHPDoc comment. - * @return string + * @return string the help information for this controller. */ public function getHelp() { diff --git a/framework/db/QueryBuilder.php b/framework/db/QueryBuilder.php index 45b67b1bb3..95d2c8f6ff 100644 --- a/framework/db/QueryBuilder.php +++ b/framework/db/QueryBuilder.php @@ -23,7 +23,7 @@ use yii\helpers\StringHelper; * For more details and usage information on QueryBuilder, see the [guide article on query builders](guide:db-query-builder). * * @property-write string[] $conditionClasses Map of condition aliases to condition classes. For example: - * ```php ['LIKE' => yii\db\condition\LikeCondition::class] ``` . + * ```php ['LIKE' => yii\db\condition\LikeCondition::class] ```. * @property-write string[] $expressionBuilders Array of builders that should be merged with the pre-defined * ones in [[expressionBuilders]] property. * diff --git a/framework/web/CookieCollection.php b/framework/web/CookieCollection.php index 3791b54532..8b7db87c5e 100644 --- a/framework/web/CookieCollection.php +++ b/framework/web/CookieCollection.php @@ -52,7 +52,10 @@ class CookieCollection extends BaseObject implements \IteratorAggregate, \ArrayA * Returns an iterator for traversing the cookies in the collection. * This method is required by the SPL interface [[\IteratorAggregate]]. * It will be implicitly called when you use `foreach` to traverse the collection. - * @return ArrayIterator an iterator for traversing the cookies in the collection. + * @return ArrayIterator an iterator for traversing the cookies in the collection. + * + * @phpstan-return ArrayIterator + * @psalm-return ArrayIterator */ #[\ReturnTypeWillChange] public function getIterator()