Attributes
全Public Attributeの用途、付与対象、Typed Self-handled標準形での必要性を確認する。
BlackOpsはOperation、Transaction、Value Validation、HTTP Binding、Observed Journal ProjectionのMetadataをPHP Attributeで宣言します。このPageは利用者向けPublic Attribute 24件をSourceと照合しています。PublicApi marker自身はFrameworkが公開境界を管理するためのMetadataであり、Application Authoringには使いません。
Operation Attributes
| Attribute | 用途 | 付与対象 | 最小例 | Typed Self-handled標準形 |
|---|---|---|---|---|
BlackOps\Core\Attribute\OperationType |
永続的なdot-separated Operation Type IDを宣言する | Operation Class | #[OperationType('order.place')] |
必須 |
BlackOps\Core\Attribute\Deferred |
OperationをDeferred実行へ切り替える | Operation Class | #[Deferred] |
Deferred時だけ付ける。省略時はInline |
BlackOps\Core\Attribute\ExecuteWith |
Execution Strategyを明示するCompatibility Attribute | Operation Class | #[ExecuteWith(Deferred::class)] |
Canonical Deferredは引数なし#[Deferred]。InlineはAttributeを省略する |
BlackOps\Core\Attribute\Authorize |
Operationへ認可Policyを結び付ける | Operation Class | #[Authorize(PlaceOrderPolicy::class)] |
認可が必要なOperationへ一度だけ付ける |
BlackOps\Core\Attribute\HandledBy |
Separate Handler Classを指定する | Operation Class | #[HandledBy(PlaceOrderHandler::class)] |
不要。Separate Handler互換形だけで使う |
BlackOps\Core\Attribute\Accepts |
Accepted OperationValueを明示する |
Operation Class | #[Accepts(PlaceOrderValue::class)] |
不要。第一引数から推論する |
BlackOps\Core\Attribute\Returns |
Outcome Classを明示する |
Operation Class | #[Returns(OrderPlaced::class)] |
不要。Return Typeから推論する |
BlackOps\Core\Attribute\Sensitive |
Observed ProjectionでPropertyをOmit/Mask/Hashする | OperationValue/Outcome Property |
#[Sensitive(SensitiveMode::Mask)] |
Sensitive Propertyだけで使う |
BlackOps\Core\Attribute\ListOf |
OutcomeのTyped DTO ListでElement Classを宣言する | 非Nullable array Outcome Property |
#[ListOf(PostSummary::class)] |
Structured Outcome Listだけで使う |
BlackOps\Core\Attribute\ConsoleCommand |
OperationをBlackOps CLIへ明示公開する | Operation Class | #[ConsoleCommand('order:create', 'Create an order.')] |
Console入口が必要なOperationへ一度だけ付ける |
Typed Self-handled標準形では、handle(ConcreteValue $value): ConcreteOutcomeのNative Signatureを正本にします。#[Accepts]/#[Returns]を併記した場合は推論型との完全一致が必要です。新しい単純なOperationへは追加しないでください。
#[ConsoleCommand]を付けたOperationだけがCLIへ現れます。Command名はsegment:segment形式のCanonical Nameを使い、Alias、空Segment、空白、Control Character、|を含めません。Console入力はScalarなpublic constructor-promoted PropertyだけをNamed Optionへ変換し、#[Sensitive]を含むValue/OutcomeはBuild時に拒否します。
Structured Outcome
Nested ResponseはJSON文字列へ変換せず、OutcomeDataと#[ListOf]で型を保ったまま返せます。
use BlackOps\Core\Attribute\ListOf;
use BlackOps\Core\Outcome;
use BlackOps\Core\OutcomeData;
final readonly class PostSummary implements OutcomeData
{
public function __construct(
public string $id,
public string $title,
) {}
}
final readonly class ListPostsOutcome implements Outcome
{
/** @param list<PostSummary> $posts */
public function __construct(
#[ListOf(PostSummary::class)]
public array $posts,
public ?PostSummary $featured,
public int $total,
) {}
}
Scalarはstring、int、float、boolとNullable、Nested型は具象final readonly OutcomeData、Listは非Nullable array+#[ListOf]だけを使用できます。Map、Scalar List、Enum、DateTime、Union、任意Object、Element型のないArrayは対応しません。Nested DTOのFieldはすべてPublic Constructor-promoted Propertyにしてください。
この契約はOutput専用です。OutcomeDataや#[ListOf]を追加しても、OperationValueのNested Object/Array HTTP Input BindingやArray Validationは有効になりません。
#[HandledBy]はDecorator、複数実装切替等でOperation DefinitionとHandlerを分けるCompatibility形に限って使います。Typed Self-handled handle()と同時に指定するとBuildがAmbiguousとして拒否します。
#[Authorize]はAuthorizationPolicyを実装するClassを一つ指定します。複数条件は複数Attributeではなく、一つのApplication Policy内で組み合わせてください。BuildはPolicy Contractを検証し、PolicyをCompiled ContainerへAutowired登録します。Service Providerで同じPolicyを登録した場合はApplication側のBindingを優先します。
Sensitive Mode
BlackOps\Core\Attribute\SensitiveModeはAttributeではなく、#[Sensitive]のModeを選ぶPublic enumです。
use BlackOps\Core\Attribute\Sensitive;
use BlackOps\Core\Attribute\SensitiveMode;
final readonly class InviteMemberValue implements OperationValue
{
public function __construct(
#[Sensitive(SensitiveMode::Mask)]
public string $inviteeEmail,
) {}
}
OmitはFieldを除外し、Maskは[masked]へ置換し、Hashは一方向Digestへ置換します。どのModeも認証、認可、暗号化、Access Control、Retentionを代替しません。
Transaction Attributes
| Attribute | 用途 | 付与対象 | 最小例 |
|---|---|---|---|
BlackOps\Database\Attribute\Transactional |
Operation固定LifecycleまたはDI管理ServiceのDatabase Transaction境界を宣言する | 非final ClassまたはPublicな非final Instance Method |
#[Transactional]、#[Transactional(connection: 'analytics')] |
BlackOps\Database\Attribute\AfterCommit |
Transactionで呼ばれた処理をCommit後まで遅延する | Publicな非final Instance Method |
#[AfterCommit] public function send(OrderId $id): void |
TransactionalのConnectionを省略するとconfig/database.phpのdefaultを使います。Named ConnectionはBuild時にConfiguration Snapshotと照合しますが、Databaseへは接続しません。空のNameと未定義Nameはphp blackops build:compileで拒否されます。Method-levelの指定はClass-levelのConnectionを上書きできます。
Operation Definitionまたは自己処理handle()へ付けたTransactionalは、Authorization後から成功Terminal Journal/OutcomeまでをFramework固定Lifecycleで包みます。同一Connectionなら業務更新と成功Terminalを同じCommitへ含めます。Separate Handler側だけへ付けた場合は一般Service Semanticsとなり、Handler Method Return時にCommitします。
<?php
declare(strict_types=1);
namespace App\Feature\Order;
use BlackOps\Database\Attribute\Transactional;
#[Transactional]
readonly class CreateOrderCommand
{
public function __construct(private OrderRepository $orders) {}
public function execute(CreateOrderInput $input): OrderId
{
return $this->orders->create($input);
}
}
AOP ProxyはBuild時にvar/build/aop/へ生成され、Compiled Containerから解決したInstanceだけをInterceptします。new CreateOrderCommand(...)で直接作ったInstance、StaticまたはPrivate Methodは対象ではありません。対象ClassとMethodにfinalは使えませんが、非finalのreadonly classは使えます。無効な付与対象は黙って無視せずBuild Errorにします。
Operation Proxy上のTransaction Interceptorは意図的にPass-throughです。実行時はManifestの解決済みConnection Metadataを固定Lifecycleが読み、AOPと二重にTransactionを開始しません。
AfterCommit Methodは明示的なvoid Return Typeが必要で、Static、final、Generator、Reference Return、Reference Parameterは使えません。Transaction内の呼出は最外Commit後までQueueされ、Rollbackでは破棄されます。Transaction外では通常のMethod Callとして即時実行されます。Nested、Manual Transaction、失敗時の保証はTransactionを確認してください。
Value Validation Attributes
| Attribute | 用途 | 付与対象 | 最小例 |
|---|---|---|---|
BlackOps\Core\Validation\Attribute\NotBlank |
空文字や空相当を拒否する | OperationValue Property |
#[NotBlank] |
BlackOps\Core\Validation\Attribute\Length |
Stringの文字数を検証する | string Property |
#[Length(min: 3, max: 80)] |
BlackOps\Core\Validation\Attribute\Range |
数値そのものを検証する | int/float Property |
#[Range(min: 1, max: 100)] |
BlackOps\Core\Validation\Attribute\Email |
Email形式を検証する | string Property |
#[Email] |
BlackOps\Core\Validation\Attribute\Regex |
PCRE Patternとの一致を検証する | string Property |
#[Regex('/^[A-Z]+$/')] |
BlackOps\Core\Validation\Attribute\Count |
Collectionの要素数を検証する | array等のProperty |
#[Count(min: 1, max: 20)]。現行HTTP BinderはArray Input非対応 |
BlackOps\Core\Validation\Attribute\Choice |
許可したScalarへ厳密一致させる | Scalar Property | #[Choice(['JPY', 'USD'])] |
Validation BackendはSymfony Validatorですが、ApplicationはBlackOps Namespaceの7 AttributeだけをContractとして使います。Binding後、Inline/Deferred Strategyを選ぶ前に全Violationを集約します。CountのValidatorは実装済みですが、現行HTTP BinderはNon-scalar Inputをbinding.typeとして拒否するためHTTP Valueでは利用できません。Length、Range、Countの違いとRejected LifecycleはValue and Validationを参照してください。
HTTP Attributes
| Attribute | 用途 | 付与対象 | 最小例 | Typed Self-handled標準形 |
|---|---|---|---|---|
BlackOps\Http\Attribute\Route |
HTTP MethodとPathをOperationへ結び付ける | Operation Class | #[Route(method: 'POST', path: '/orders')] |
HTTP公開時に必要 |
BlackOps\Http\Attribute\FromBody |
JSON Body FieldをValue Constructor引数/PropertyへBindする | ParameterまたはProperty | #[FromBody('customerId')] |
Body Field名とProperty名が異なる場合に指定。nullで同名 |
BlackOps\Http\Attribute\FromHeader |
HTTP HeaderをValueへBindする | ParameterまたはProperty | #[FromHeader('Idempotency-Key')] |
Header Inputで使う |
BlackOps\Http\Attribute\FromPath |
Route Path ParameterをValueへBindする | ParameterまたはProperty | #[FromPath('orderId')] |
Path Parameterで使う |
BlackOps\Http\Attribute\FromQuery |
Query ParameterをValueへBindする | ParameterまたはProperty | #[FromQuery('page')] |
Query Parameterで使う |
FromBody、FromHeader、FromPath、FromQueryの名前を省略するとProperty/Parameter名を使います。一つのValueへ複数の入力元を無秩序に混在させず、Route Contractが読み取れる形にしてください。
FromPath、FromQuery、FromHeaderはWire文字列を宣言したstring、int、float、boolへ厳密にBindします。Booleanは小文字のtrue/falseだけ、数値は空白や先頭+のないCanonical形式だけを受理します。FromBodyはJSON Decoderが返すNative型を検査し、Body文字列を別のScalar型へ変換しません。詳細はBinding Failureは422を参照してください。
Typed標準形の全体例
use BlackOps\Core\Attribute\OperationType;
use BlackOps\Core\Attribute\Authorize;
use BlackOps\Core\Operation;
use BlackOps\Http\Attribute\Route;
#[Route(method: 'POST', path: '/orders')]
#[OperationType('order.place')]
#[Authorize(PlaceOrderPolicy::class)]
readonly class PlaceOrder implements Operation
{
public function __construct(private OrderRepository $orders) {}
public function handle(PlaceOrderValue $value): OrderPlaced
{
$order = $this->orders->place($value->customerId, $value->productCode, $value->quantity);
return new OrderPlaced($order->id());
}
}
この標準形には#[Accepts]、#[Returns]、#[HandledBy]がありません。BuildがSignatureからValue、Outcome、Handlerを確定します。Authoringの詳細はAuthoring、Security境界はSecurityを確認してください。