mmo / sf-utils
Symfony组件的扩展集合
Requires
- php: >=7.2
Requires (Dev)
- ext-intl: *
- aws/aws-sdk-php: ^3.132.4
- doctrine/orm: ^2.8
- lexik/jwt-authentication-bundle: ^2.11
- liip/imagine-bundle: ^2.6
- moneyphp/money: ^3.3
- myclabs/php-enum: ^1.7
- phpunit/phpunit: ^8.5
- ramsey/uuid: ^4.1
- symfony/console: ^4.4
- symfony/form: ^4.4
- symfony/http-kernel: ^4.4
- symfony/security-core: ^4.4
- symfony/serializer: ^4.4
- symfony/validator: ^4.4
Suggests
- ext-intl: *
This package is auto-updated.
Last update: 2024-09-06 18:02:26 UTC
README
验证器
ITIN验证器
ITIN - 个人纳税人识别号
<?php require_once './vendor/autoload.php'; use mmo\sf\Validator\Constraints\Itin; use Symfony\Component\Validator\Validation; $validator = Validation::createValidatorBuilder()->getValidator(); $validator->validate('foo', new Itin()); // NOT VALID $validator->validate('918-97-5273', new Itin()); // VALID
生日
require_once './vendor/autoload.php'; use mmo\sf\Validator\Constraints\Birthday; use Symfony\Component\Validator\Validation; $validator = Validation::createValidatorBuilder()->getValidator(); $validator->validate((new DateTimeImmutable('now'))->modify('-5 years'), new Birthday(['minAge' => 18])); // NOT VALID $validator->validate((new DateTimeImmutable('now'))->modify('-120 years'), new Birthday()); // NOT VALID $validator->validate((new DateTimeImmutable('now'))->modify('-5 years'), new Birthday()); // VALID
银行路由号码
require_once './vendor/autoload.php'; use mmo\sf\Validator\Constraints\BankRoutingNumber; use Symfony\Component\Validator\Validation; $validator = Validation::createValidatorBuilder()->getValidator(); $validator->validate('1234567890', new BankRoutingNumber()); // NOT VALID $validator->validate('275332587', new BankRoutingNumber()); // VALID
Utf8Letters
仅允许UTF-8字母和破折号。
require_once './vendor/autoload.php'; use mmo\sf\Validator\Constraints\Utf8Letters; use Symfony\Component\Validator\Validation; $validator = Validation::createValidatorBuilder()->getValidator(); $validator->validate('foo.bar', new Utf8Letters()); // NOT VALID $validator->validate('Zażółć', new Utf8Letters()); // VALID
Utf8Words
仅允许UTF-8字母、破折号和空格。用于验证个人全名。
require_once './vendor/autoload.php'; use mmo\sf\Validator\Constraints\Utf8Words; use Symfony\Component\Validator\Validation; $validator = Validation::createValidatorBuilder()->getValidator(); $validator->validate('foo.bar', new Utf8Words()); // NOT VALID $validator->validate('Zażółć gęślą', new Utf8Words()); // VALID
OnlyDigits
仅允许数字。
<?php use mmo\sf\Validator\Constraints\OnlyDigits; use Symfony\Component\Validator\Validation; require_once './vendor/autoload.php'; $validator = Validation::createValidatorBuilder()->getValidator(); $violations = $validator->validate('f1234', new OnlyDigits()); // NOT VALID $violations = $validator->validate('1234567', new OnlyDigits()); // VALID
ArrayConstraintValidatorFactory
不遵循命名约束和约束验证器命名约定的验证器,将不会在ConstraintValidatorFactory的默认实现中找到。
类ArrayConstraintValidatorFactory
解决了这个问题,您可以将约束验证器映射到对象。
use Symfony\Component\Validator\Validation; use mmo\sf\Validator\ArrayConstraintValidatorFactory; use Kiczort\PolishValidatorBundle\Validator\Constraints\NipValidator; // .... $validatorFactory = new ArrayConstraintValidatorFactory(['kiczort.validator.nip' => new NipValidator()]); $validator = Validation::createValidatorBuilder() ->setConstraintValidatorFactory($validatorFactory) ->getValidator(); // ...
翻译器
FakeTranslator
FakeTranslator
类可以在单元测试中使用,而不是使用存根。目前只支持id
和locale
参数。
<?php require_once './vendor/autoload.php'; use mmo\sf\Translation\FakeTranslator; $translator = new FakeTranslator('en'); $translator->trans('foo'); // en-foo $translator->trans('foo', [], null, 'us'); // us-foo
安全性
加密器
Encrypter
用于加密字符串值。所有加密值都使用OpenSSL和AES-256-CBC加密算法(默认)进行加密。
<?php use mmo\sf\Util\Encrypter; require_once './vendor/autoload.php'; $encrypter = new Encrypter('my-secret-key'); $secret = $encrypter->encryptString('secret message'); $plaintext = $encrypter->decryptString($secret);
AlwaysTheSameEncoderFactory (Symofny 4.4和5.4)
AlwaysTheSameEncoderFactory
在与UserPasswordEncoder
结合的集成测试中非常有用。无论您传递哪种UserInterface实现,都会始终使用通过构造函数注入的相同密码加密器。
<?php require_once './vendor/autoload.php'; use mmo\sf\Security\Test\AlwaysTheSameEncoderFactory; use Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder; use Symfony\Component\Security\Core\Encoder\UserPasswordEncoder; $factory = new AlwaysTheSameEncoderFactory(new PlaintextPasswordEncoder()); $encoder = new UserPasswordEncoder($factory); // now you can pass $encoder to your service, which expect `UserPasswordEncoderInterface`
MemoryUserProvider (仅限Symfony 4.4和5.4)
对于Symfony 6.4+,请使用内置提供者\Symfony\Component\Security\Core\User\InMemoryUserProvider
。
MemoryUserProvider
是一个简单的非持久用户提供者,用于测试。
此提供程序与InMemoryUserProvider不同,它允许存储任何实现UserInterface接口的用户对象,而不仅仅是内部Symfony User类。
<?php require_once './vendor/autoload.php'; use mmo\sf\Security\Test\MemoryUserProvider; use Symfony\Component\Security\Core\User\User; $provider = new MemoryUserProvider(User::class, []); $provider->createUser(new User('test', 'foo')); $provider->loadUserByUsername('test');
表单
RamseyUuidToStringTransformer
在UUID字符串和UUID对象之间转换。Symfony 5.3包含一个自己的UuidToStringTransformer
转换器,但您还需要使用symfony/uuid组件。此转换器与ramsey/uuid
库一起工作。
PrimaryKeyToEntityTransformer
在主键(不支持复合主键)和实体之间转换。
StringInsteadNullTransformer
此转换器的目的是修复当您有一个带有ChoiceType的表单,并为该字段传递空值时出现的错误。您的实体/DTO仅期望字符串值,您会收到错误“在属性路径 ... 上期望参数类型为 "string","NULL" 已给出”。
Symfony将此空字符串更改为null值(由于ChoiceToValueTransformer)。您可以将StringInsteadNullTransformer作为模型转换器添加,这样null值将被转换为空字符串。
//... use mmo\sf\Form\DataTransformer\StringInsteadNullTransformer; class StateType extends AbstractType { private StatesProviderInterface $statesProvider; public function __construct(StatesProviderInterface $statesProvider) { $this->statesProvider = $statesProvider; } public function buildForm(FormBuilderInterface $builder, array $options): void { $builder->addModelTransformer(new StringInsteadNullTransformer()); } public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults(['choices' => $this->statesProvider->getStates()]); } public function getParent(): string { return ChoiceType::class; } }
ReplaceIfNotSubmittedListener
此事件订阅者的目的是在未发送任何数据时覆盖模型的数据。想象一下这样的场景,您有实体
class FormDto { /** * @var string|null */ public $text; /** * @var PersonDto|null */ public $person; } class PersonDto { /** * @var string|null */ public $firstName; /** * @var string|null */ public $lastName; }
属性 $person 不为空。您希望将该值设置为 null,因为 PersonDto 不能处于“分割状态”。PersonDto 的两个属性都必须设置(不能为空)。当表单提交时,如果方法 submit
的 Symfony 参数 $clearMissing
设置为 false
,则由于此 EventSubscriber,FormDto
的属性 person
将被设置为 null 值。如果没有此 EventSubscriber,将创建一个空的 PersonDto 对象,并将其传递给 FormDto
。
请参阅测试 \mmo\sf\tests\Form\ReplaceIfNotSubmittedFormTest
。
use mmo\sf\Form\ReplaceIfNotSubmittedListener; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; class FormToTestReplaceValueType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void { $builder->add('text', TextType::class); $builder->add('person', PersonType::class, [ 'required' => false, ]); $builder->get('person')->addEventSubscriber(new ReplaceIfNotSubmittedListener(null)); } }
lexik/jwt-authentication-bundle
撤销 JWT 令牌
JWT 是有状态的令牌。我们不需要存储它们。当需要撤销(使无效)令牌时,此属性会引发问题。一些资源:如何将令牌更改为其无效状态? 或 使 JSON Web 令牌无效
在文件 config/packages/cache.yaml
中注册一个新的池 cache.jwt
。在此示例中,我们使用 Redis 作为适配器。
cache.jwt: adapter: cache.adapter.redis
在文件 config/routes.yaml
中添加一个路由器
api_logout: path: /api/sessions controller: 'mmo\sf\JWTAuthenticationBundle\Controller\LogoutAction' methods: DELETE
最后,我们需要在 config/services.yaml
文件中注册服务。我们为接口 JitGeneratorInterface
创建到 RamseyUuid4JitGenerator
的别名,并配置监听器。对于 CheckRevokeListener
,我们需要传递正确的缓存池(我们创建了一个自定义池 - cache.jwt
在 config/packages/cache.yaml
中)和路由名称 api_logout
,我们在文件 config/routes.yaml
中添加了它
mmo\sf\JWTAuthenticationBundle\Controller\LogoutAction: tags: ['controller.service_arguments'] mmo\sf\JWTAuthenticationBundle\JitGenerator\RamseyUuid4JitGenerator: ~ mmo\sf\JWTAuthenticationBundle\JitGenerator\JitGeneratorInterface: alias: mmo\sf\JWTAuthenticationBundle\JitGenerator\RamseyUuid4JitGenerator mmo\sf\JWTAuthenticationBundle\Listener\CheckRevokeListener: arguments: - '@request_stack' - '@cache.jwt' - 'api_logout' - 'key_prefix_in_cache.' tags: - { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_decoded, method: onJWTDecoded } mmo\sf\JWTAuthenticationBundle\Listener\AddJitClaimListener: tags: - { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_created, method: onJWTCreated }
工具
转写器
类 Transliterator
包含一个静态方法 transliterate
,用于返回字符串的转写版本。基于 yii2 Inflector
<?php require_once './vendor/autoload.php'; use mmo\sf\Util\Transliterator; Transliterator::transliterate('¿Español?'); // Espanol? Transliterator::transliterate('Українська: ґанок, європа', Transliterator::TRANSLITERATE_STRICT); // Ukraí̈nsʹka: g̀anok, êvropa
实体测试助手
类 EntityTestHelper
帮助设置私有字段的值,例如 id
。
<?php require_once './vendor/autoload.php'; use mmo\sf\Util\EntityTestHelper; EntityTestHelper::setPrivateProperty($entity, 12); EntityTestHelper::setPrivateProperty($entity, 12, 'fieldName');
对象助手
arrayToObject
此静态方法递归地将数组转换为 stdClass。
<?php require_once './vendor/autoload.php'; use mmo\sf\Util\ObjectHelper; $object = ObjectHelper::arrayToObject(['foo' => 'bar', 'baz' => ['foo' => 'bar']]); // class stdClass#3 (2) { // public $foo => // string(3) "bar" // public $baz => // class stdClass#2 (1) { // public $foo => // string(3) "bar" // } // }
命令
S3CreateBucketCommand
命令 mmo:s3:create-bucket
创建一个 S3 桶。如果启用选项 skip-if-exists
,并且桶已存在,则过程将成功完成。您可以使用选项 --public
使每个人都可以从桶中获取对象。
要使用此命令,您必须注册两个服务。在 config/services.yaml
中注册服务 s3client
和 mmo\sf\Command\S3CreateBucketCommand
。
services: # ... s3client: class: Aws\S3\S3Client arguments: - version: '2006-03-01' # or 'latest' endpoint: '%env(AWS_S3_ENDPOINT)%' use_path_style_endpoint: true region: "us-east-1" # 'eu-central-1' for example credentials: key: '%env(AWS_S3_KEY)%' secret: '%env(AWS_S3_SECRET)%' mmo\sf\Command\S3CreateBucketCommand: arguments: $s3Client: '@s3client'
LiipImagineBundle
ResolverAlwaysStoredDecorator
此解析器无论图像是否已存在都始终返回 true。
liip_imagine: # ... resolvers: offers: flysystem: filesystem_service: oneup_flysystem.images_filesystem root_url: "%env(AWS_S3_URL)%" cache_prefix: miniatures visibility: public cache: always_stored_resolver
services: # ... mmo\sf\ImagineBundle\ResolverAlwaysStoredDecorator: arguments: $resolver: '@liip_imagine.cache.resolver.offers' tags: - { name: "liip_imagine.cache.resolver", resolver: always_stored_resolver }
cyve/json-schema-form-bundle
CyveJsonSchemaMapper
数据映射器的默认实现(PropertyPathMapperTest
)也设置当值为 null 时的数组键。当 JsonSchema 中的字段不是必需的时,这是一个问题。模式验证器不会检查字段值是否已设置。这与 Symfony 验证组件不同。在 Symfony 中,如果字段值为 null 或空字符串,则跳过验证。在 JsonSchemaValidator 事件中,具有值为 null 的可选字段必须匹配验证规则。此外,cyve/json-schema-form-bundle 不支持字段的多种类型。
Doctrine
IgnoreSchemaTablesListener
此监听器期望在比较数据库模式与实体时忽略的表名。当您手动管理某些实体的表的架构时,这很有用。这是与 DoctrineMigrationsBundle 中的手动表类似的一种解决方案。
mmo\sf\Doctrine\IgnoreSchemaTablesListener: arguments: $ignoredTables: - schema._table1_ - schema.table2 tags: - {name: doctrine.event_listener, event: postGenerateSchema }
序列化器
MyCLabsEnumNormalizer
来自包 myclabs/php-enum
的 Enum 类的序列化器和反序列化器。
<?php require_once './vendor/autoload.php'; use mmo\sf\Serializer\Normalizer\MyCLabsEnumNormalizer; use MyCLabs\Enum\Enum; use Symfony\Component\Serializer\Serializer; /** * @method static static DRAFT() * @method static static PUBLISHED() */ class MyEnum extends Enum { private const DRAFT = 'draft'; private const PUBLISHED = 'published'; } $serializer = new Serializer([new MyCLabsEnumNormalizer()]); $serializer->denormalize('draft', MyEnum::class); // return instance of MyEnum
MoneyNormalizer
来自包 moneyphp/money
的 Money 类的规范化器和解规范化器。
<?php require_once './vendor/autoload.php'; use Money\Money; use mmo\sf\Serializer\Normalizer\MoneyNormalizer; use Symfony\Component\Serializer\Serializer; $serializer = new Serializer([new MoneyNormalizer()]); $money = $serializer->denormalize($serializer->normalize(Money::EUR(100)), Money::class);
事件订阅者
性能订阅者
此监听器连接到 HttpKernel 事件(RequestEvent 和 TerminateEvent),通过 LoggerInterface 记录端点的性能。日志条目包括持续时间、HTTP 方法、URL 和 PID。对这些数据的分析可以帮助我们找到最耗时的端点。我们可以有一些嫌疑人,并通过 Xdebug 或 Blackfire 对这些端点的性能进行更深入的分析。
在 config/packages/monolog.yaml
中为 monolog 创建一个新的通道和处理器。
# config/packages/monolog.yaml monolog: channels: ['performance'] handlers: performancelog: type: stream path: php://stderr level: debug channels: [performance]
接下来在 config/services.yaml
中注册监听器。
# config/services.yaml services: mmo\sf\EventSubscriber\PerformanceSubscriber: arguments: $logger: '@monolog.logger.performance'
当我们刷新日志页面(在这个配置中日志发送到 stderr)时,我们应该看到类似以下内容:[2021-08-30 15:15:19] performance.INFO: 请求 "GET /admin/login" 耗时 "1.041289" 秒。{"url":"/admin/login","method":"GET","pid":7,"status_code":200}