Skip to content

Improve psalm annotations to make it easier to use in userland #253

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

Merged
merged 7 commits into from
Feb 24, 2025
Merged
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
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@
"rector/rector": "^2.0.9",
"roave/infection-static-analysis-plugin": "^1.35",
"spatie/phpunit-watcher": "^1.24",
"vimeo/psalm": "^5.26.1 || ^6.7.1",
"vimeo/psalm": "^5.26.1 || ^6.8.6",
"yiisoft/di": "^1.3",
"yiisoft/event-dispatcher": "^1.1",
"yiisoft/log": "^2.1",
"yiisoft/router-fastroute": "^3.1.0",
"yiisoft/test-support": "^3.0.1",
"yiisoft/test-support": "^3.0.2",
"yiisoft/translator-message-php": "^1.1.1"
},
"extra": {
Expand Down
1 change: 0 additions & 1 deletion psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,5 @@
<PropertyNotSetInConstructor errorLevel="suppress" />
<RedundantPropertyInitializationCheck errorLevel="suppress" />
<RiskyTruthyFalsyComparison errorLevel="suppress" />
<MissingOverrideAttribute errorLevel="suppress" />
</issueHandlers>
</psalm>
14 changes: 5 additions & 9 deletions src/Column/ActionButton.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@

/**
* `ActionButton` represents a button in an action column of a grid.
*
* @psalm-type TContentClosure = Closure(array|object $data, DataContext $context): (string|Stringable)
* @psalm-type TUrlClosure = Closure(array|object $data, DataContext $context): string
* @psalm-type TAttributesClosure = Closure(array|object $data, DataContext $context): array
* @psalm-type TClassClosure = Closure(array|object $data, DataContext $context): (array<array-key, string|null>|string|null)
*/
final class ActionButton
{
Expand All @@ -30,10 +25,11 @@ final class ActionButton
* @param string|null $title Button title attribute.
* @param bool $overrideAttributes Whether to override default attributes with custom ones instead of merging.
*
* @psalm-param TContentClosure|string|Stringable $content
* @psalm-param TUrlClosure|string|null $url
* @psalm-param TAttributesClosure|array|null $attributes
* @psalm-param TClassClosure|array<array-key,string|null>|false|string|null $class
* @template TData as array|object
* @psalm-param (Closure(TData, DataContext): string)|string|null $url
* @psalm-param (Closure(TData, DataContext): array)|array|null $attributes
* @psalm-param (Closure(TData, DataContext): (array<array-key, string|null>|string|null))|array<array-key,string|null>|false|string|null $class
* @psalm-param (Closure(TData, DataContext): (string|Stringable))|string|Stringable $content
*/
public function __construct(
public readonly Closure|string|Stringable $content = '',
Expand Down
50 changes: 35 additions & 15 deletions src/Column/ActionColumnRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,25 +158,45 @@ private function renderButton(ActionButton|callable $button, string $name, DataC
return $button($url);
}

/** @var string|Stringable $content */
$content = $button->content instanceof Closure
? ($button->content)($context->data, $context)
: $button->content;

$url = $button->url instanceof Closure
? ($button->url)($context->data, $context)
: ($button->url ?? $this->createUrl($name, $context));

$attributes = $button->attributes instanceof Closure
? ($button->attributes)($context->data, $context)
: ($button->attributes ?? []);
if ($button->content instanceof Closure) {
/**
* @psalm-suppress InvalidArgument
* @var string|Stringable $content
*/
$content = ($button->content)($context->data, $context);
} else {
$content = $button->content;
}

if ($button->url instanceof Closure) {
/**
* @psalm-suppress InvalidArgument
*/
$url = ($button->url)($context->data, $context);
} else {
$url = $button->url ?? $this->createUrl($name, $context);
}

if ($button->attributes instanceof Closure) {
/**
* @psalm-suppress InvalidArgument
*/
$attributes = ($button->attributes)($context->data, $context);
} else {
$attributes = $button->attributes ?? [];
}
if (!$button->overrideAttributes && !empty($this->buttonAttributes)) {
$attributes = array_merge($this->buttonAttributes, $attributes);
}

$class = $button->class instanceof Closure
? ($button->class)($context->data, $context)
: $button->class;
if ($button->class instanceof Closure) {
/**
* @psalm-suppress InvalidArgument
*/
$class = ($button->class)($context->data, $context);
} else {
$class = $button->class;
}

if ($class === false) {
Html::addCssClass($attributes, $this->buttonClass);
Expand Down
28 changes: 17 additions & 11 deletions src/GridView.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,20 @@
*
* The look and feel of a grid view can be customized using many properties.
*
* @template TData as array|object
* @psalm-import-type UrlCreator from BaseListView
* @psalm-type TBodyRowAttributes = array|(Closure(array|object, BodyRowContext): array)|(array<array-key, Closure(array|object, BodyRowContext): mixed>)
* @psalm-type TBeforeAfterRowClosure = Closure(array|object, array-key, int, GridView): (Tr|null)
*/
final class GridView extends BaseListView
{
/**
* @var Closure|null Callback executed after rendering each data row. The Result is appended after the row.
* @psalm-var TBeforeAfterRowClosure|null
* @psalm-var (Closure(TData, array-key, int, GridView): (Tr|null))|null
*/
private Closure|null $afterRowCallback = null;

/**
* @var Closure|null Callback executed before rendering each data row. The Result is prepended before the row.
* @psalm-var TBeforeAfterRowClosure|null
* @psalm-var (Closure(TData, array-key, int, GridView): (Tr|null))|null
*/
private Closure|null $beforeRowCallback = null;

Expand Down Expand Up @@ -105,7 +104,7 @@ final class GridView extends BaseListView

/**
* @var array|Closure HTML attributes for body rows.
* @psalm-var TBodyRowAttributes
* @psalm-var array|(Closure(TData, BodyRowContext): array)|(array<array-key, Closure(TData, BodyRowContext): mixed>)
*/
private Closure|array $bodyRowAttributes = [];

Expand Down Expand Up @@ -325,13 +324,12 @@ public function keepPageOnSort(bool $enabled = true): self
*
* @return self New instance with the after row callback.
*
* @psalm-param TBeforeAfterRowClosure|null $callback
* @psalm-param (Closure(TData, array-key, int, GridView): (Tr|null))|null $callback
*/
public function afterRow(Closure|null $callback): self
{
$new = clone $this;
$new->afterRowCallback = $callback;

return $new;
}

Expand All @@ -355,13 +353,12 @@ public function afterRow(Closure|null $callback): self
*
* @return self New instance with the before row callback.
*
* @psalm-param TBeforeAfterRowClosure|null $callback
* @psalm-param (Closure(TData, array-key, int, GridView): (Tr|null))|null $callback
*/
public function beforeRow(Closure|null $callback): self
{
$new = clone $this;
$new->beforeRowCallback = $callback;

return $new;
}

Expand Down Expand Up @@ -510,7 +507,7 @@ public function headerRowAttributes(array $attributes): self
* function (array|object $data, BodyRowContext $context): mixed
* ```
*
* @psalm-param TBodyRowAttributes $attributes
* @psalm-param array|(Closure(TData, BodyRowContext): array)|(array<array-key, Closure(TData, BodyRowContext): mixed>) $attributes
*
* @return self New instance with the body row attributes.
*/
Expand Down Expand Up @@ -887,6 +884,9 @@ protected function renderItems(
$index = 0;
foreach ($items as $key => $value) {
if ($this->beforeRowCallback !== null) {
/**
* @psalm-suppress InvalidArgument
*/
$row = ($this->beforeRowCallback)($value, $key, $index, $this);
if (!empty($row)) {
$rows[] = $row;
Expand All @@ -911,6 +911,9 @@ protected function renderItems(
$rows[] = Html::tr($bodyRowAttributes)->cells(...$tags);

if ($this->afterRowCallback !== null) {
/**
* @psalm-suppress InvalidArgument
*/
$row = ($this->afterRowCallback)($value, $key, $index, $this);
if (!empty($row)) {
$rows[] = $row;
Expand Down Expand Up @@ -1002,11 +1005,14 @@ protected function getOrderProperties(): array
*
* @return array The prepared attributes.
*
* @psalm-param TBodyRowAttributes $attributes
* @psalm-param array|(Closure(TData, BodyRowContext): array)|(array<array-key, Closure(TData, BodyRowContext): mixed>) $attributes
*/
private function prepareBodyRowAttributes(array|Closure $attributes, BodyRowContext $context): array
{
if (is_callable($attributes)) {
/**
* @psalm-suppress InvalidArgument
*/
return $attributes($context->data, $context);
}

Expand Down
Loading