php-http / HttplugBundle

Symfony Framework Integration for HTTPlug
http://httplug.io
MIT License
381 stars 50 forks source link

ServiceNotFoundException : The "httplug.message_factory" service or alias has been removed... #241

Closed xsuntel closed 6 years ago

xsuntel commented 6 years ago
Q A
Bug? no yes
New Feature? no yes
Version Specific version or SHA of a commit

Symfony 4.0.2

I am developing my web application using HTTPLUG after Symfony of PHP frameworks was upgraded from version 3 to version 4.0.2

However, I have an issue about "ServiceNotFoundException" that is as below

The "httplug.message_factory" service or alias has been removed or inlined when the container was compiled. You should either make it public, or stop using the container directly and use dependency injection instead.

the source code is that $request = $this->container->get('httplug.message_factory')->createRequest('GET', $url); $response = $this->container->get('httplug.client.twitch')->sendRequest($request);

I have checked some information. could you please give me an advice?

php ./bin/console debug:container

httplug.async_client.default Http\Client\HttpAsyncClient
httplug.auto_discovery.auto_discovered_async alias for "httplug.collector.auto_discovered_async"
httplug.auto_discovery.auto_discovered_client alias for "httplug.collector.auto_discovered_client"
httplug.client Http\Adapter\Guzzle6\Client
httplug.client.amazon Http\Client\Common\PluginClient
httplug.client.app alias for "httplug.client.app.http_methods"
httplug.client.default alias for "httplug.client.app.http_methods"
httplug.client.facebook Http\Client\Common\PluginClient
httplug.client.game Http\Client\Common\PluginClient
httplug.client.game_pc_riotgames_lol Http\Client\Common\PluginClient
httplug.client.instagram Http\Client\Common\PluginClient
httplug.client.kakao Http\Client\Common\PluginClient
httplug.client.linkedin Http\Client\Common\PluginClient
httplug.client.naver Http\Client\Common\PluginClient
httplug.client.twitch Http\Client\Common\PluginClient
httplug.client.youtube Http\Client\Common\PluginClient
httplug.message_factory Http\Message\MessageFactory\GuzzleMessageFactory
httplug.message_factory.default Http\Message\MessageFactory\GuzzleMessageFactory
httplug.plugin.authentication.my_basic Http\Client\Common\Plugin\AuthenticationPlugin
httplug.plugin.authentication.my_basic.auth Http\Message\Authentication\BasicAuth
httplug.plugin.authentication.my_bearer Http\Client\Common\Plugin\AuthenticationPlugin
httplug.plugin.authentication.my_bearer.auth Http\Message\Authentication\Bearer
httplug.plugin.authentication.my_service Http\Client\Common\Plugin\AuthenticationPlugin
httplug.strategy Http\HttplugBundle\Discovery\ConfiguredClientsStrategy
httplug.stream_factory Http\Message\StreamFactory\GuzzleStreamFactory
httplug.stream_factory.default Http\Message\StreamFactory\GuzzleStreamFactory
httplug.uri_factory Http\Message\UriFactory\GuzzleUriFactory
httplug.uri_factory.default Http\Message\UriFactory\GuzzleUriFactory

php ./bin/console debug:container httplug.message_factory

This service is an alias for the service httplug.message_factory.default

Information for Service "httplug.message_factory.default"


Option Value


Service ID httplug.message_factory.default
Class Http\Message\MessageFactory
Tags -
Public yes
Synthetic no
Lazy no
Shared yes
Abstract no
Autowired no
Autoconfigured no
Factory Class Http\Discovery\MessageFactoryDiscovery
Factory Method find


php ./bin/console debug:container httplug.client.twitch

Information for Service "httplug.client.twitch"


Option Value


Service ID httplug.client.twitch
Class Http\Client\Common\PluginClient
Tags -
Public yes
Synthetic no
Lazy no
Shared yes
Abstract no
Autowired no
Autoconfigured no
Factory Service Http\Client\Common\PluginClientFactory
Factory Method createClient


/config/packages/httplug.yaml

httplug: main_alias: client: httplug.client.default message_factory: httplug.message_factory.default uri_factory: httplug.uri_factory.default stream_factory: httplug.stream_factory.default classes: client: ~ message_factory: ~ uri_factory: ~ stream_factory: ~ profiling: enabled: true formatter: null captured_body_length: 1000 discovery: client: 'auto' async_client: 'auto' clients: twitch: factory: 'httplug.factory.guzzle6' flexible_client: false http_methods_client: false config: verify: false timeout: 2

"require": {
    "php": "^7.1.3",
    "doctrine/doctrine-fixtures-bundle": "^3.0",
    "hwi/oauth-bundle": "^0.6.0",
    "php-http/guzzle6-adapter": "^1.1",
    "php-http/httplug-bundle": "^1.8",
    "php-http/httplug-pack": "^1.0",
    "sensio/framework-extra-bundle": "^5.0",
    "sensiolabs/security-checker": "^4.1",
    "symfony/asset": "^4.0",
    "symfony/console": "^4.0",
    "symfony/expression-language": "^4.0",
    "symfony/finder": "^4.0",
    "symfony/flex": "^1.0",
    "symfony/form": "^4.0",
    "symfony/framework-bundle": "^4.0",
    "symfony/lts": "^4@dev",
    "symfony/monolog-bundle": "^3.1",
    "symfony/orm-pack": "^1.0",
    "symfony/polyfill-apcu": "^1.5",
    "symfony/requirements-checker": "^1.0",
    "symfony/security-bundle": "^4.0",
    "symfony/swiftmailer-bundle": "^3.1",
    "symfony/translation": "^4.0",
    "symfony/validator": "^4.0",
    "symfony/webpack-encore-pack": "^1.0",
    "symfony/yaml": "^4.0",
    "twig/extensions": "^1.5",
    "twig/twig": "^2.4"
},
"require-dev": {
    "symfony/dotenv": "^4.0",
    "symfony/browser-kit": "^4.0",
    "symfony/css-selector": "^4.0",
    "symfony/debug-bundle": "^4.0",
    "symfony/phpunit-bridge": "^4.0",
    "symfony/profiler-pack": "^1.0",
    "friendsofphp/php-cs-fixer": "^2.7"
},
dbu commented 6 years ago

the message factory is currently at the default visibility, which changes from public to private in symfony 4: https://github.com/php-http/HttplugBundle/blob/583dbc5e3ff68a71fee528597549d6cd075ecf3b/Resources/config/services.xml#L22

the idea is that you would use dependency injection to get the factory. with symfony 4 you have autowiring, so specifying the constructor argument (or, if you are in a controller, specifying the factory as argument to the controller method) should be enough for symfony.

@php-http/httplug wdyt, should we make the factories and the client public services? there is also #213 which might be the same problem. while DI is all cool and nice, i feel like getting a factory from the container is not the end of the world...

Nyholm commented 6 years ago

Sure, lets!

There is no harm in that.

xsuntel commented 6 years ago

Thank you for your infomation

xsuntel commented 6 years ago

I have tested the issue about "ServiceNotFoundException" using HttplugBundle after some file was edited on Symfony 4. as a result, It is normal. but I have questions about adding 'httplug.client.????'. If I add 'httplug.client' every time, then I have to define it as service at 'service.yaml'.

In my opinion, I hope that the factories and the client are automatically to be public services when the package was installed.

Could you please give me an advice?

pacth : /config/services.yaml

httplug.message_factory:
    class: Http\Message\MessageFactory\GuzzleMessageFactory
    public: true
httplug.uri_factory:
    class: Http\Message\UriFactory\GuzzleUriFactory
    public: true
httplug.stream_factory:
    class: Http\Message\StreamFactory\GuzzleStreamFactory
    public: true
httplug.client:
    class: Http\Adapter\Guzzle6\Client
    public: true

pacth : /config/packages/httplug.yaml

httplug:
    main_alias:
        client: httplug.client.default
        message_factory: httplug.message_factory.default
        uri_factory: httplug.uri_factory.default
        stream_factory: httplug.stream_factory.default
    classes:
        client: Http\Adapter\Guzzle6\Client
        message_factory: Http\Message\MessageFactory\GuzzleMessageFactory
        uri_factory: Http\Message\UriFactory\GuzzleUriFactory
        stream_factory: Http\Message\StreamFactory\GuzzleStreamFactory
Nyholm commented 6 years ago

Can you elaborate why you need the service to be public?

xsuntel commented 6 years ago

No Problem, I had considered why It is the service to be public. I try to connect and analyze many data from Open API using the HTTP Plug Bundle of Symfony 4. so, I just think that The more connecting API is tried, The more HTTP client is needed. as you know, We can use so many API online. If my application should use many 'httpplug.client.xxx' over 50. I will register for it as service every time. Honestly, I might not need it because we have to consider various situations such as performance and security. but, I just want to know that it is possible.

Nyholm commented 6 years ago

I use HTTPlug bundle the same way as you do. But why do you need the clients to be public?

Do you use your clients in controllers or commands? https://symfony.com/doc/current/service_container/alias_private.html

xsuntel commented 6 years ago

Thank you for your information. I will use private service aliases.

Nyholm commented 6 years ago

Yeah, You can still use private services in the service declaration. But you cannot access private services like $this->container->get('foo');.

Since best practice in Symfony 4 is to use controllers/commands as services there will never be a need for accessing services directly from the container.

But as Davis says, it is also about DX.

gigi commented 6 years ago

Try to continue topic. Im writing bundle for Symfony 4 and trying to test functional that relies on httplug. Is it any documented ways to mock MessageFactory during testing?

public function testClient()
{
    $client = static::createClient();
    /** @var Client $httpClientMock */
    $httpClientMock = $client->getContainer()->get('httplug.client.mock'); // error here
    ...
}

There is no default message factory on testing so error occured Http\Discovery\NotFoundException : No message factories found. To use Guzzle, Diactoros or Slim Framework factories install php-http/message and the chosen message implementation.

httplug:
    clients:
      my_service:
        factory: 'httplug.factory.mock'
dbu commented 6 years ago

i would use a "real" message factory and add that to your require-dev section. the messages are value object, i don't think its a good idea to mock them. could that work for your use case?

gigi commented 6 years ago

So to test the whole functional you propose to add one of implementations. I thought the only right way is to add own psr7 mock implementation. Maybe I'm wrong :) composer require guzzlehttp/psr7 --dev seems ok for now... Thank you!

dbu commented 6 years ago

i like this answer on the topic: https://softwareengineering.stackexchange.com/questions/205731/should-we-mock-entities-and-value-objects-when-doing-ddd/205737#205737

gigi commented 6 years ago

One more try Now I have problem to fetch message_factory at all

    "require": {
        "php" : "^7.1",
        "symfony/http-kernel": "^4.0",
        "php-http/httplug-bundle": "^1.9"
    },
    "require-dev": {
        "php-http/guzzle6-adapter": "^1.1",
        "symfony/phpunit-bridge": "^4.0",
        "symfony/framework-bundle": "^4.0",
        "symfony/yaml": "^4.0",
        "symfony/browser-kit": "^4.0",
        "php-http/mock-client": "^1.1"
    },
httplug:
    clients:
        acme:
          factory: 'httplug.factory.guzzle6'
public function testClient()
    {
        $client = static::createClient();
        $container = $client->getContainer();

        $request = $container->get('httplug.message_factory')->createRequest('GET', 'http://example.com'); // error here
        ...
}
Error : Cannot instantiate interface Http\Message\MessageFactory
.../vendor/symfony/dependency-injection/Container.php:239

Can you help please...

gigi commented 6 years ago

Its my fault... Have replaced message factories in test kernel... Sorry)

dbu commented 6 years ago

uff, glad you found the problem!