kynx / laminas-form-shape
为 Laminas 表单生成 Psalm 类型
Requires
- php: ~8.2 || ~8.3
- composer-runtime-api: ^2.2
- laminas/laminas-cli: ^1.10
- laminas/laminas-form: ^3.19
- nette/php-generator: ^4.1
Requires (Dev)
- cuyz/valinor: ^1.8
- laminas/laminas-coding-standard: ^2.5
- laminas/laminas-i18n: ^2.26
- laminas/laminas-servicemanager: ^3.22
- laminas/laminas-uri: ^2.11
- phpunit/phpunit: ^10.5
- psalm/plugin-phpunit: ^0.18.4
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- roave/security-advisories: dev-master
- squizlabs/php_codesniffer: ^3.8
- vimeo/psalm: ^5.20
README
为 Psalm 生成 Laminas 表单 类型
安装
使用 Composer 将此包安装为开发依赖项
composer require --dev kynx/laminas-form-shape vimeo/psalm
如果您想使用 Psalm Phar,则要求使用它
composer require --dev kynx/laminas-form-shape psalm/phar
用法
vendor/bin/laminas form:psalm-type src/Forms/Artist.php
...将为您的 Artist 表单添加一个类似 数组形状
use Laminas\Form\Element\Text; use Laminas\Form\Form; +/** + * @psalm-import-type TAlbumData from Album + * @psalm-type TArtistData = array{ + * id: int|null, + * name: non-empty-string, + * albums: array<array-key, TAlbumData>, + * } + * @extends Form<TArtistData> + */ final class Artist extends Form { /**
查看完整选项列表
vendor/bin/laminas form:psalm-type --help
为什么?
Laminas Form 的 3.17.0 版本引入了用于描述 Form::getData() 返回的数组的 Psalm 模板。这是一个很大的改进,并且应该能够发现任何使用 Psalm 的人的潜在错误。然而,为表单生成数组形状是一项繁琐的任务,很容易出错。您是否真的知道由每个 required、allow_empty 和 continue_if_empty 组合产生的 Psalm 类型?还有所有附加到 InputFilter 的可能过滤器和服务程序呢?
此命令将分析您的表单输入过滤器,并生成最具体的数组形状。
配置
所有配置都存储在 laminas-form-shape 配置键下。以下示例假设您正在使用返回数组的 PHP 配置文件:
return [ 'laminas-form-shape' => [ // custom config here ], ];
输出格式化
有三个顶级设置用于控制数组形状的格式:
indent- 用于在格式化数组形状时缩进的字符串。默认为四个空格。max-string-length- 在输出字面字符串时包含的最大字符数。默认为 Psalm 配置的值(通常为 1000 个字符)。literal-limit- 要输出的最大字面值数量。如果您的AllowList过滤器或InArray验证器包含大量项目,您可能希望限制此值。默认为 100。
更改最后两个设置会使数组形状不那么精确,但更易读。
return [ 'laminas-form-shape' => [ 'indent' => "\t", // use tab for indenting 'max-string-length' => 50, // don't output long literal strings 'literal-limit' => 20, // don't output too many literals ], ];
文件元素
文件元素用于处理 [表单上传]。FileInput 验证了 Laminas\Http\PhpEnvironment\Request 使用的数组表示法以及 Mezzio 等应用程序使用的 PSR-7 UploadFileInterface。如果您的控制器/处理程序仅使用其中之一,那么在配置中指定它。
return [ 'laminas-form-shape' => [ 'input' => [ 'file' => [ // Laminas MVC applications 'laminas' => true, 'psr-7' => false, // Mezzio applications // 'laminas' => false, // 'psr-7' => true, ], ], ], ];
过滤器
您表单 InputFilter 中定义的每个过滤器都将由多个 访问者 处理。每个访问者都接受先前的类型列表(通常从 null|string 开始)并根据过滤器实际执行的操作添加或删除类型。
大多数过滤器访问者不需要配置,对于需要配置的访问者,我们提供了合理的默认值。但您也可以自由调整以下内容:
允许列表
允许列表 过滤器允许您配置一个列表,其中包含过滤器允许的术语,如果没有匹配项,则为 null。我们将此转换为类似 'first'|'second'|1|2|null 的字面值列表。
如果列表中没有术语,我们将传递前一位访客输出的任何类型。这样做是为了防止动态填充列表的代码(例如,从数据库查询)崩溃。
您可以通过配置来更改这两种行为。
return [ 'laminas-form-shape' => [ 'filter' => [ 'allow-list' => [ 'allow-empty-list' => false, // empty lists will produce "Cannot get type" error ], ], ], ];
回调
我们检查任何回调 回调过滤器 的返回类型。传递给下一个过滤器的类型是那里发现的类型。
如果您进行的操作比 intval() 更复杂,我强烈建议您将这些重构为 FilterInterface 的实现,并编写一个自定义访客以提供它返回的具体类型:这将使您的测试更干净,类型更可靠。
如果回调甚至没有返回类型,它们将被忽略。修复它们。
验证器
您表单的 InputFilter 中定义的每个验证器都将由多个 访客 处理。所有过滤器产生的最终类型列表被输送到第一个,然后将其输出输送到下一个,依此类推。验证器通常缩小最终类型。例如,Digits 验证器的访客将 string 类型转换为 numeric-string。
大多数过滤器访问者不需要配置,对于需要配置的访问者,我们提供了合理的默认值。但您也可以自由调整以下内容:
回调
回调验证器 被忽略。与回调过滤器不同,没有可靠的方法可以确定它们的作用。如果您关心类型(并且如果您在这里,您确实关心),将它们转换为具体的 ValidatorInterface 实现并编写一个自定义访客来描述它们。您的代码——以及您的生活——将会更好。
文件
各种 文件 验证器接受来自 $_FILES 超全局或 UploadedFileInterface 的数组。我们有一个访客来处理所有这些。
如果您有自定义文件验证器接受相同的内容,请将其添加到访客处理的验证器列表中。
use MyApp\Validator\MyCustomFileValidator; return [ 'laminas-form-shape' => [ 'validator' => [ 'file' => [ 'validators' => [ MyCustomFileValidator::class, ], ], ], ], ];
InArray
与 AllowList 过滤器类似,InArray 验证器接受一个值列表——或 haystack——并验证输入是否为其中之一。访客可以返回一个字面类型('first'|'second'|1|2)。默认情况下,它忽略空的 haystack。
您可以更改默认值。
return [ 'laminas-form-shape' => [ 'validator' => [ 'in-array' => [ 'allow-empty-haystack' => false, // empty haystacks will produce "Cannot get type" error ], ], ], ];
Regex
Regex 验证器拒绝不匹配其正则表达式的输入。如果我是天才并且有时间,我可能能够编写一个可以从表达式中确定类型的正则表达式解析器。但我不,我也没有。
相反,我们提供了一个由标准表单元素使用的已知正则表达式列表。如果您有自己的正则表达式,您希望将它们添加到列表中。
配置是通过正则表达式字符串键入的,并包含一个用于缩小类型联合的 Psalm 类型 列表。例如,Number 元素将验证 float、int 或 numeric-string。其配置如下所示
use Psalm\Type\Atomic\TFloat; use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TString; return [ 'laminas-form-shape' => [ 'validator' => [ 'regex' => [ '(^-?\d*(\.\d+)?$)' => [TFloat::class, TInt::class, TNumericString::class], ], ], ], ];
字符串
有许多验证器可以执行各种操作来验证字符串的格式,从 Barcode 到 Uuid。但它们关于类型的所有信息只是将 string 更改为 non-empty-string。
如果您有一个执行相同功能的自定义验证器,请将其添加到列表中
use MyApp\Validator\MyCustomStringValidator; return [ 'laminas-form-shape' => [ 'validator' => [ 'non-empty-string' => [ 'validators' => [ MyCustomStringValidator::class, ], ], ], ], ];
小心
如果您的代码在运行时改变了表单结构 - 添加和删除元素,改变 required 属性或填充 <select> 选项 - 这项工具将无法知道。它生成的数组形状可以作为良好的起点,但需要手动调整。
该工具旨在覆盖您在 composer require laminas/laminas-form 时安装的所有过滤器或验证器。如果它遇到了它不知道的,它将默默地忽略它。请参阅自定义部分以获取处理这些内容的提示。
无法获取 'foo' 的类型
您可以使用组合的过滤器和验证器构建一个永远不会产生结果的输入过滤器。例如,一个将 casting 选项设置为 true 的 Boolean 过滤器将仅输出 bool 类型。如果您随后使用一个 Barcode 验证器,该元素将永远无法验证。当命令遇到这种情况时,它将报告“无法获取类型”错误。
如果在解析运行多年且一直表现良好的现有表单时看到此错误,您遇到了一个错误。请提交一个带有复现错误的小型示例表单的问题。或者,更好的是,创建一个带有失败测试的PR 😃
自定义过滤器和验证器
待定,一旦事情稳定下来...