Skip to content

Better DX for new strong PHP types #1870

@bdsl

Description

@bdsl

Is your feature request related to a problem? Please describe.

I just upgrade from version 15 to 17, and I very much appreciate the new type safety from #1837 .

However the types as they are now coded are difficult to work with, since they are many hundreds of characters of docblock-defined types, often repeating the same or similar information across multiple functions - for instance in the arguments to Customer::create (

* @param null|array{address?: null|array{city?: string, country?: string, line1?: string, line2?: string, postal_code?: string, state?: string}, balance?: int, cash_balance?: array{settings?: array{reconciliation_mode?: string}}, description?: string, email?: string, expand?: string[], invoice_prefix?: string, invoice_settings?: array{custom_fields?: null|array{name: string, value: string}[], default_payment_method?: string, footer?: string, rendering_options?: null|array{amount_tax_display?: null|string, template?: string}}, metadata?: null|StripeObject, name?: string, next_invoice_sequence?: int, payment_method?: string, phone?: string, preferred_locales?: string[], shipping?: null|array{address: array{city?: string, country?: string, line1?: string, line2?: string, postal_code?: string, state?: string}, name: string, phone?: string}, source?: string, tax?: array{ip_address?: null|string, validate_location?: string}, tax_exempt?: null|string, tax_id_data?: array{type: string, value: string}[], test_clock?: string, validate?: bool} $params
) and Customer::update().

We check our code with Psalm and PHPStan. The complex types mean we get very long error messages and it's hard to see whats wrong when we aren't passing correct arguments, especially with Psalm.

As we wrap the stripe library in our own class, and have other classes that return arrays suitable for passing to stripe we tend to need to refer to these types across our application code. We can reduce this effort a bit by making supertypes with just the properties we actually use, but it's still quite tricky and I ended up suppressing errors in some places.

My feature request is to adjust this API to make the types much easier to understand and work with. To avoid unnecessary BC breaks it could be done by providing new functions alongside the existing ones for users to migrate to, or having the functions accept a union of what they do now and something better.

Describe the solution you'd like

IMHO the better options in my order of preference would be:

  1. Accept PHP value objects using a combination of the native PHP type system, smaller value objects inside the big ones and docblocks on individual properties to ensure all required properties are present and correct.

  2. Instead of repeating docblock based array-shape types many times in the API use type aliases, with
    PHPStan local type alias and/or psalm-types . Reference these type aliases (and unions & intersections of them) in function docblocks to avoid writing any complex repetitive types.

As an example of option 1, perhaps the usage of \Stripe\Service\CustomerService::create could change like so:

// current
        $stripe = new \Stripe\StripeClient('api_key');
        $stripe->customers->create([
            'name' => 'Jenny Rosen',
            'email' => 'jennyrosen@example.com',
        ]);

// new
        $stripe = new \Stripe\StripeClient('api_key');
        $stripe->customers->create(new \Stripe\CreateCustomerRequest(
            name: 'Jenny Rosen',
            email: 'jennyrosen@example.com',
        ));

In a transitional period to allow users to migrate the create function could accept a union of the existing array type and the new CreateCustomerRequest, or there could be a new function to use instead for people wanting to opt-in to the new class based API.

Potentially the name and email properties could themselves be value objects instead of simple strings, which might save stripe some processing power by having checks for sensible values run in those constructors on customers PHP servers as part of the CreateCustomerRequest value object creation.

Describe alternatives you've considered

No response

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions