🌐 AI搜索 & 代理 主页
Skip to content

Type Hinting in App::config for http_client using extra option #62512

@gobalopper

Description

@gobalopper

Symfony version(s) affected

7.4.0-RC3

Description

In #60320 I found an issue with the older fluent config builder but since that was deprecated I updated to 7.4.0-RC3 and am running into a similar issue with the new approach.

I am using curl constants in the definition of the extra section but it is expecting a list which is a sequential array and not another array with curl as the key.

The error from PHPStan:

 ------ ------------------------------------------------------------------------------------------------------------------------ 
  Line   config/packages/framework.php                                                                                           
 ------ ------------------------------------------------------------------------------------------------------------------------ 
  5      Parameter #1 $config of static method Symfony\Component\DependencyInjection\Loader\Configurator\App::config() expects   
         array{imports?: list<array{resource: string, type?: string|null, ignore_errors?: bool}|string>, parameters?: array<str  
         ing, array<array<mixed>|bool|float|int|string|UnitEnum|null>|bool|float|int|string|UnitEnum|null>, services?: array{_d  
         efaults?: array{public?: bool, tags?: mixed, resource_tags?: mixed, autowire?: bool, autoconfigure?: bool, bind?:       
         array<string, mixed>}, _instanceof?: array{shared?: bool, lazy?: bool|string, public?: bool, properties?: array<string  
         , mixed>, configurator?: array{string|Symfony\Component\DependencyInjection\Loader\Configurator\ReferenceConfigurator,  
          string}|Closure|string|Symfony\Component\DependencyInjection\Loader\Configurator\ExpressionConfigurator|Symfony\Compo  
         nent\DependencyInjection\Loader\Configurator\ReferenceConfigurator, calls?: list<array<0|1|2|string, array<int<0,       
         max>|string, mixed>|bool|string>>, tags?: mixed, resource_tags?: mixed, ...}}, framework?: array{secret?:               
         bool|float|int|string|null, http_method_override?: bool, allowed_http_method_override?: list<string>|null, trust_x_sen  
         dfile_type_header?: bool|float|int|string|null, ide?: bool|float|int|string|null, test?: bool, default_locale?:         
         bool|float|int|string|null, set_locale_from_accept_language?: bool, ...}, doctrine?: array{dbal?:                       
         array{default_connection?: bool|float|int|string|null, types?: array<string, array{class: bool|float|int|string|null}|  
         string>, driver_schemes?: array<string, bool|float|int|string|null>, connections?: array<string, array{url?: bool|floa  
         t|int|string|null, dbname?: bool|float|int|string|null, host?: bool|float|int|string|null, port?:                       
         bool|float|int|string|null, user?: bool|float|int|string|null, password?: bool|float|int|string|null, dbname_suffix?:   
         bool|float|int|string|null, application_name?: bool|float|int|string|null, ...}>}, orm?: array{default_entity_manager?  
         : bool|float|int|string|null, enable_native_lazy_objects?: bool, controller_resolver?: array{enabled?: bool,            
         auto_mapping?: bool, evict_cache?: bool}|bool, entity_managers?: array<string, array{query_cache_driver?: array{type?:  
         bool|float|int|string|null, id?: bool|float|int|string|null, pool?: bool|float|int|string|null}|string,                 
         metadata_cache_driver?: array{type?: bool|float|int|string|null, id?: bool|float|int|string|null, pool?:                
         bool|float|int|string|null}|string, result_cache_driver?: array{type?: bool|float|int|string|null, id?:                 
         bool|float|int|string|null, pool?: bool|float|int|string|null}|string, entity_listeners?: array{entities?:              
         array<string, array{listeners?: array<string, array{events?: list<array{type?: bool|float|int|string|null, method?: bo  
         ol|float|int|string|null}>}>}>}, connection?: bool|float|int|string|null, class_metadata_factory_name?: bool|float|int  
         |string|null, default_repository_class?: bool|float|int|string|null, auto_mapping?: bool|float|int|string|null, ...}>,  
         resolve_target_entities?: array<string, bool|float|int|string|null>}}, doctrine_migrations?: array{enable_service_migr  
         ations?: bool, migrations_paths?: array<string, bool|float|int|string|null>, services?: array<string, bool|float|int|s  
         tring|null>, factories?: array<string, bool|float|int|string|null>, storage?: array{table_storage?: array{table_name?:  
          bool|float|int|string|null, version_column_name?: bool|float|int|string|null, version_column_length?:                  
         bool|float|int|string|null, executed_at_column_name?: bool|float|int|string|null, execution_time_column_name?:          
         bool|float|int|string|null}}, migrations?: list<bool|float|int|string|null>, connection?: bool|float|int|string|null,   
         em?: bool|float|int|string|null, ...}, twig?: array{form_themes?: list<bool|float|int|string|null>, globals?: array<st  
         ring, array{id?: bool|float|int|string|null, type?: bool|float|int|string|null, value?: mixed}>, autoescape_service?:   
         bool|float|int|string|null, autoescape_service_method?: bool|float|int|string|null, base_template_class?:               
         bool|float|int|string|null, cache?: bool|float|int|string|null, charset?: bool|float|int|string|null, debug?: bool,     
         ...}, stimulus?: array{controller_paths?: list<bool|float|int|string|null>, controllers_json?: bool|float|int|string|n  
         ull}, ...}, array{framework: array{http_client: array{scoped_clients: array{'account.server.client': array{base_uri:    
         '%env(ACCOUNT_SERVER…', passphrase: '%env(ACCOUNT_SERVER…', extra: array{curl: array{40291: '%env(base64:ACCOUNT…',     
         40292: '%env(base64:ACCOUNT…'}}}}}}} given.                                                                             
         🪪  argument.type                                                                                                       
         💡  Offset 'framework' (array{secret?: bool|float|int|string|null, http_method_override?: bool,                         
         allowed_http_method_override?: list<string>|null, trust_x_sendfile_type_header?: bool|float|int|string|null, ide?: boo  
         l|float|int|string|null, test?: bool, default_locale?: bool|float|int|string|null, set_locale_from_accept_language?:    
         bool, ...}) does not accept type array{http_client: array{scoped_clients: array{'account.server.client':                
         array{base_uri: '%env(ACCOUNT_SERVER…', passphrase: '%env(ACCOUNT_SERVER…', extra: array{curl: array{40291:             
         '%env(base64:ACCOUNT…', 40292: '%env(base64:ACCOUNT…'}}}}}}: Offset 'http_client' (array{enabled?: bool,                
         max_host_connections?: int, default_options?: array{headers?: array<string, mixed>, vars?: list<mixed>, max_redirects?  
         : int, http_version?: bool|float|int|string|null, resolve?: array<string, bool|float|int|string|null>, proxy?: bool|fl  
         oat|int|string|null, no_proxy?: bool|float|int|string|null, timeout?: float, ...}, mock_response_factory?:              
         bool|float|int|string|null, scoped_clients?: array<string, array{scope?: bool|float|int|string|null, base_uri?:         
         bool|float|int|string|null, auth_basic?: bool|float|int|string|null, auth_bearer?: bool|float|int|string|null,          
         auth_ntlm?: bool|float|int|string|null, query?: array<string, bool|float|int|string|null>, headers?: array<string, mix  
         ed>, max_redirects?: int, ...}|string>}|bool) does not accept type array{scoped_clients: array{'account.server.client'  
         : array{base_uri: '%env(ACCOUNT_SERVER…', passphrase: '%env(ACCOUNT_SERVER…', extra: array{curl: array{40291:           
         '%env(base64:ACCOUNT…', 40292: '%env(base64:ACCOUNT…'}}}}}: Type #1 from the union: Offset 'scoped_clients'             
         (array<string, array{scope?: bool|float|int|string|null, base_uri?: bool|float|int|string|null, auth_basic?:            
         bool|float|int|string|null, auth_bearer?: bool|float|int|string|null, auth_ntlm?: bool|float|int|string|null, query?:   
         array<string, bool|float|int|string|null>, headers?: array<string, mixed>, max_redirects?: int, ...}|string>) does not  
          accept type array{'account.server.client': array{base_uri: '%env(ACCOUNT_SERVER…', passphrase:                         
         '%env(ACCOUNT_SERVER…', extra: array{curl: array{40291: '%env(base64:ACCOUNT…', 40292: '%env(base64:ACCOUNT…'}}}}:      
         Type #1 from the union: Offset 'extra' (list<mixed>) does not accept type array{curl: array{40291: '%env(base64:ACCOUN  
         T…', 40292: '%env(base64:ACCOUNT…'}}: array{curl: array{40291: '%env(base64:ACCOUNT…', 40292: '%env(base64:ACCOUNT…'}}  
         is not a list. 

Which is a lot of text to say that extra is not a list.

How to reproduce

Using this code for framework.php in the config/packages folder with PHPStan will detect the issue:

// config/packages/framework.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

return App::config([
    'framework' => [
        'http_client' => [
            'scoped_clients' => [
                'account.server.client' => [
                    'base_uri' => '%env(ACCOUNT_SERVER_BASEURI)%',
                    'passphrase' => '%env(ACCOUNT_SERVER_CLIENT_PASSPHRASE)%',
                    'extra' => [
                        'curl' => [
                            CURLOPT_SSLCERT_BLOB => '%env(base64:ACCOUNT_SERVER_CLIENT_SSLCERT)%',
                            CURLOPT_SSLKEY_BLOB => '%env(base64:ACCOUNT_SERVER_CLIENT_SSLCERT)%',
                        ],
                    ],
                ],
            ],
        ],
    ],
]);

Possible Solution

I think to fix this the type hint would need to be array<mixed> instead of list<mixed>.

All of the examples in the HTTP Client docs use this same approach of an array with key => value inside. ie 'extra' => ['my-key' => 'my-value'] or 'extra' => ['trace_content' => false],.

I don't know if there is a complete list of all the possible extra options so doing array instead of a more complex definition is probably best.

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