diff --git a/build/controllers/PhpDocController.php b/build/controllers/PhpDocController.php index 1b12eb3fa9..531dc7c042 100644 --- a/build/controllers/PhpDocController.php +++ b/build/controllers/PhpDocController.php @@ -8,11 +8,17 @@ namespace yii\build\controllers; use Yii; +use yii\base\Model; +use yii\base\Module; use yii\console\Controller; +use yii\db\QueryBuilder; use yii\helpers\Console; use yii\helpers\FileHelper; use yii\helpers\Json; +use yii\log\Dispatcher; +use yii\log\Target; use yii\web\Controller as WebController; +use yii\web\Request as WebRequest; /** * PhpDocController is there to help to maintain PHPDoc annotation in class files. @@ -24,7 +30,7 @@ use yii\web\Controller as WebController; class PhpDocController extends Controller { /** - * Manually added PHPDoc properties that do not need to be removed. + * Manually added PHPDoc properties that do not need to be removed or changed. * * @phpstan-var array */ @@ -34,8 +40,29 @@ class PhpDocController extends Controller 'response', 'view', ], + Model::class => [ + 'errors', + ], + Module::class => [ + 'aliases', + ], + Dispatcher::class => [ + 'flushInterval', + 'logger', + ], + Target::class => [ + 'enabled', + ], + WebRequest::class => [ + 'hostInfo', + ], + QueryBuilder::class => [ + 'conditionClasses', + ], ]; + private const PROPERTIES_ENCLOSURE = " *\n"; + /** * {@inheritdoc} */ @@ -634,6 +661,9 @@ class PhpDocController extends Controller $lines = explode("\n", $doc); $propertyPart = false; $propertyPosition = false; + $lastPropertyName = null; + $hasManuallyAddedProperties = false; + foreach ($lines as $i => $line) { $line = trim($line); if (strncmp($line, '* @property', 11) === 0) { @@ -647,21 +677,44 @@ class PhpDocController extends Controller $propertyPart = false; } if ($propertyPart) { - 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)) { + preg_match( + '/@property(?:-read|-write)?\s+([\\\\\w\|\[\]]+)\s+\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/', + $line, + $matches + ); + + if (isset($matches[2])) { + $lastPropertyName = $matches[2]; + } + + if (in_array($lastPropertyName, $manuallyAddedProperties)) { + $hasManuallyAddedProperties = true; + } else { unset($lines[$i]); } } } + if ($properties === '') { + return implode("\n", $lines); + } + // if no properties or other tags were present add properties at the end if ($propertyPosition === false) { $propertyPosition = \count($lines) - 2; } + // if there are properties that were added manually, remove start enclosure + if ($hasManuallyAddedProperties) { + $properties = substr($properties, strlen(self::PROPERTIES_ENCLOSURE)); + } + $finalDoc = ''; foreach ($lines as $i => $line) { - $finalDoc .= $line . "\n"; + if (!$hasManuallyAddedProperties || $i !== $propertyPosition) { + $finalDoc .= $line . "\n"; + } + if ($i == $propertyPosition) { $finalDoc .= $properties; } @@ -724,16 +777,17 @@ class PhpDocController extends Controller '#\* @param (?[\w\\|\\\\\\[\\]]+) \$\w+(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . '[\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,}(\#\[\\\\*.+\])*[\s\n]{2,}public function [g|s]et(?\w+)\(((?:,? ?\$\w+ ?= ?[^,]+)*|\$\w+(?:, ?\$\w+ ?= ?[^,]+)*)\)#', - $class['content']); - $acrs = array_merge($properties, $gets, $sets); + $acrs = array_merge($gets, $sets); + $manuallyAddedProperties = self::MANUALLY_ADDED_PROPERTIES[$className] ?? []; $props = []; + foreach ($acrs as &$acr) { $acr['name'] = lcfirst($acr['name']); + if (in_array($acr['name'], $manuallyAddedProperties)) { + continue; + } + $acr['comment'] = trim(preg_replace('#(^|\n)\s+\*\s?#', '$1 * ', $acr['comment'])); $props[$acr['name']][$acr['kind']] = [ 'type' => $acr['type'], @@ -747,7 +801,6 @@ class PhpDocController extends Controller ksort($props); - $phpdoc .= " *\n"; foreach ($props as $propName => &$prop) { $docLine = ' * @property'; $note = ''; @@ -777,7 +830,10 @@ class PhpDocController extends Controller $phpdoc .= $docLine; } - $phpdoc .= " *\n"; + } + + if ($phpdoc !== '') { + $phpdoc = self::PROPERTIES_ENCLOSURE . $phpdoc . self::PROPERTIES_ENCLOSURE; } return [$className, $phpdoc]; diff --git a/framework/base/Model.php b/framework/base/Model.php index 6e692ee130..fa5e9730f3 100644 --- a/framework/base/Model.php +++ b/framework/base/Model.php @@ -37,13 +37,25 @@ use yii\validators\Validator; * * For more details and usage information on Model, see the [guide article on models](guide:structure-models). * + * @property-read array $errors Errors for all attributes. This is a two-dimensional + * array of errors for all attributes, similar to the following: + * + * ```php + * [ + * 'username' => [ + * 'Username is required.', + * 'Username must contain only word characters.', + * ], + * 'email' => [ + * 'Email address is invalid.', + * ] + * ] + * ``` + * + * Empty array if no errors. * @property-read \yii\validators\Validator[] $activeValidators The validators applicable to the current * [[scenario]]. * @property array $attributes Attribute values (name => value). - * @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.', ] ] ```. * @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. @@ -585,7 +597,6 @@ class Model extends Component implements StaticInstanceInterface, IteratorAggreg * Returns the errors for all attributes or a single attribute. * @param string|null $attribute attribute name. Use null to retrieve errors for all attributes. * @return array 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 diff --git a/framework/base/Module.php b/framework/base/Module.php index d730d91510..2c00d60733 100644 --- a/framework/base/Module.php +++ b/framework/base/Module.php @@ -381,9 +381,6 @@ class Module extends ServiceLocator * Defines path aliases. * This method calls [[Yii::setAlias()]] to register the path aliases. * This method is provided so that you can define path aliases when configuring a module. - * @property array list of path aliases to be defined. The array keys are alias names - * (must start with `@`) and the array values are the corresponding paths or aliases. - * See [[setAliases()]] for an example. * @param array $aliases list of path aliases to be defined. The array keys are alias names * (must start with `@`) and the array values are the corresponding paths or aliases. * For example, diff --git a/framework/db/QueryBuilder.php b/framework/db/QueryBuilder.php index 95d2c8f6ff..c02f9b87bc 100644 --- a/framework/db/QueryBuilder.php +++ b/framework/db/QueryBuilder.php @@ -23,7 +23,10 @@ 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/log/Dispatcher.php b/framework/log/Dispatcher.php index 11b6b0016a..d21f458d0d 100644 --- a/framework/log/Dispatcher.php +++ b/framework/log/Dispatcher.php @@ -50,8 +50,8 @@ use yii\base\ErrorHandler; * Yii::$app->log->targets['file']->enabled = false; * ``` * - * @property int $flushInterval How many messages should be logged before they are sent to targets. This - * method returns the value of [[Logger::flushInterval]]. + * @property int $flushInterval How many messages should be logged before they are sent to targets. See + * [[getFlushInterval()]] and [[setFlushInterval()]] for details. * @property Logger $logger The logger. If not set, [[Yii::getLogger()]] will be used. Note that the type of * this property differs in getter and setter. See [[getLogger()]] and [[setLogger()]] for details. * @property int $traceLevel How many application call stacks should be logged together with each message. @@ -107,7 +107,6 @@ class Dispatcher extends Component /** * Gets the connected logger. * If not set, [[Yii::getLogger()]] will be used. - * @property Logger the logger. If not set, [[Yii::getLogger()]] will be used. * @return Logger the logger. */ public function getLogger() diff --git a/framework/log/Target.php b/framework/log/Target.php index 8d5d2e3655..f76aba4b35 100644 --- a/framework/log/Target.php +++ b/framework/log/Target.php @@ -381,7 +381,6 @@ abstract class Target extends Component /** * Check whether the log target is enabled. - * @property bool Indicates whether this log target is enabled. Defaults to true. * @return bool A value indicating whether this log target is enabled. */ public function getEnabled() diff --git a/framework/web/Request.php b/framework/web/Request.php index 7b9645d532..cebeb59184 100644 --- a/framework/web/Request.php +++ b/framework/web/Request.php @@ -23,6 +23,9 @@ use yii\validators\IpValidator; * * For more details and usage information on Request, see the [guide article on requests](guide:runtime-requests). * + * @property string|null $hostInfo Schema and hostname part (with port number if needed) of the request URL + * (e.g. `https://www.yiiframework.com`), null if can't be obtained from `$_SERVER` and wasn't set. See + * [[getHostInfo()]] for security related notes on this property. * @property-read string $absoluteUrl The currently requested absolute URL. * @property array $acceptableContentTypes The content types ordered by the quality score. Types with the * highest scores will be returned first. The array keys are the content types, while the array values are the @@ -47,9 +50,6 @@ use yii\validators\IpValidator; * returned if no such header is sent. * @property-read array $eTags The entity tags. * @property-read HeaderCollection $headers The header collection. - * @property string|null $hostInfo Schema and hostname part (with port number if needed) of the request URL - * (e.g. `https://www.yiiframework.com`), null if can't be obtained from `$_SERVER` and wasn't set. See - * [[getHostInfo()]] for security related notes on this property. * @property-read string|null $hostName Hostname part of the request URL (e.g. `www.yiiframework.com`). * @property-read bool $isAjax Whether this is an AJAX (XMLHttpRequest) request. * @property-read bool $isDelete Whether this is a DELETE request. @@ -776,9 +776,6 @@ class Request extends \yii\base\Request * > If you don't have access to the server configuration, you can setup [[\yii\filters\HostControl]] filter at * > application level in order to protect against such kind of attack. * - * @property string|null schema and hostname part (with port number if needed) of the request URL - * (e.g. `https://www.yiiframework.com`), null if can't be obtained from `$_SERVER` and wasn't set. - * See [[getHostInfo()]] for security related notes on this property. * @return string|null schema and hostname part (with port number if needed) of the request URL * (e.g. `https://www.yiiframework.com`), null if can't be obtained from `$_SERVER` and wasn't set. * @see setHostInfo()