コンテンツにスキップ
BlackOps1.xは試験的なバージョンです。Production Readyは2.xを予定しています。Releases
BlackOps
Esc
navigateopen⌘Jpreview
このページの内容

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する OperationValueOutcome 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はstringintfloatboolと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.phpdefaultを使います。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は使えませんが、非finalreadonly 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 数値そのものを検証する intfloat 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では利用できません。LengthRangeCountの違いと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で使う

FromBodyFromHeaderFromPathFromQueryの名前を省略するとProperty/Parameter名を使います。一つのValueへ複数の入力元を無秩序に混在させず、Route Contractが読み取れる形にしてください。

FromPathFromQueryFromHeaderはWire文字列を宣言したstringintfloatboolへ厳密にBindします。Booleanは小文字のtruefalseだけ、数値は空白や先頭+のない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を確認してください。