zircote / swagger-php

A php swagger annotation and parsing library
http://zircote.github.io/swagger-php/
Apache License 2.0
5.03k stars 933 forks source link

DEPRECATION: Since zircote/swagger-php 4.9: Context detecting is deprecated #1579

Closed dmamchyts closed 2 months ago

dmamchyts commented 2 months ago

Hi, after updating to zircote/swagger-php 4.9.0 many errors appeared:

195x DEPRECATION: Since zircote/swagger-php 4.9: Context detecting is deprecated /var/www/html/vendor/symfony/deprecation-contracts/function.php:25
76x DEPRECATION: Since zircote/swagger-php 4.9: Context detecting is deprecated /var/www/html/src/Common/Controller/ApiLoginController.php:28
38x DEPRECATION: Since zircote/swagger-php 4.9: Context detecting is deprecated /var/www/html/src/Common/Controller/ApiLoginController.php:29
38x DEPRECATION: Since zircote/swagger-php 4.9: Context detecting is deprecated /var/www/html/src/Common/Controller/ApiLoginController.php:26
38x DEPRECATION: Since zircote/swagger-php 4.9: Context detecting is deprecated /var/www/html/src/Common/Controller/ApiLoginController.php:25
76x DEPRECATION: Since zircote/swagger-php 4.9: Context detecting is deprecated /var/www/html/src/Common/Controller/ApiLoginController.php:39
38x DEPRECATION: Since zircote/swagger-php 4.9: Context detecting is deprecated /var/www/html/src/Common/Controller/ApiLoginController.php:40
...

We use symfony 7, nelmio/api-doc-bundle, codeception/codeception

Errors appeared in CLI after running command:

./vendor/bin/codecept run functional --env=test --fail-fast=1

debug backtrace:

docker compose exec -T -e XDEBUG_MODE=off fpm ./vendor/bin/codecept run functional --env=test --fail-fast=1

Codeception PHP Testing Framework v5.1.2 https://stand-with-ukraine.pp.ua

......Array
(
    [0] => Array
        (
            [file] => /var/www/html/vendor/zircote/swagger-php/src/Annotations/AbstractAnnotation.php
            [line] => 103
            [function] => detect
            [class] => OpenApi\Context
            [type] => ::
        )

    [1] => Array
        (
            [file] => /var/www/html/vendor/zircote/swagger-php/src/Attributes/Tag.php
            [line] => 26
            [function] => __construct
            [class] => OpenApi\Annotations\AbstractAnnotation
            [type] => ->
        )

    [2] => Array
        (
            [file] => /var/www/html/src/Common/Controller/ApiLoginController.php
            [line] => 21
            [function] => __construct
            [class] => OpenApi\Attributes\Tag
            [type] => ->
        )

    [3] => Array
        (
            [file] => /var/www/html/vendor/symfony/http-kernel/Event/ControllerEvent.php
            [line] => 107
            [function] => newInstance
            [class] => ReflectionAttribute
            [type] => ->
        )

    [4] => Array
        (
            [file] => /var/www/html/vendor/symfony/http-kernel/Event/ControllerArgumentsEvent.php
            [line] => 107
            [function] => getAttributes
            [class] => Symfony\Component\HttpKernel\Event\ControllerEvent
            [type] => ->
        )

    [5] => Array
        (
            [file] => /var/www/html/vendor/symfony/security-http/EventListener/IsGrantedAttributeListener.php
            [line] => 42
            [function] => getAttributes
            [class] => Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent
            [type] => ->
        )

    [6] => Array
        (
            [file] => /var/www/html/vendor/symfony/event-dispatcher/Debug/WrappedListener.php
            [line] => 116
            [function] => onKernelControllerArguments
            [class] => Symfony\Component\Security\Http\EventListener\IsGrantedAttributeListener
            [type] => ->
        )

    [7] => Array
        (
            [file] => /var/www/html/vendor/symfony/event-dispatcher/EventDispatcher.php
            [line] => 206
            [function] => __invoke
            [class] => Symfony\Component\EventDispatcher\Debug\WrappedListener
            [type] => ->
        )

    [8] => Array
        (
            [file] => /var/www/html/vendor/symfony/event-dispatcher/EventDispatcher.php
            [line] => 56
            [function] => callListeners
            [class] => Symfony\Component\EventDispatcher\EventDispatcher
            [type] => ->
        )

    [9] => Array
        (
            [file] => /var/www/html/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php
            [line] => 127
            [function] => dispatch
            [class] => Symfony\Component\EventDispatcher\EventDispatcher
            [type] => ->
        )

    [10] => Array
        (
            [file] => /var/www/html/vendor/symfony/http-kernel/HttpKernel.php
            [line] => 173
            [function] => dispatch
            [class] => Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher
            [type] => ->
        )

    [11] => Array
        (
            [file] => /var/www/html/vendor/symfony/http-kernel/HttpKernel.php
            [line] => 76
            [function] => handleRaw
            [class] => Symfony\Component\HttpKernel\HttpKernel
            [type] => ->
        )

    [12] => Array
        (
            [file] => /var/www/html/vendor/symfony/http-kernel/Kernel.php
            [line] => 185
            [function] => handle
            [class] => Symfony\Component\HttpKernel\HttpKernel
            [type] => ->
        )

    [13] => Array
        (
            [file] => /var/www/html/vendor/symfony/http-kernel/HttpKernelBrowser.php
            [line] => 61
            [function] => handle
            [class] => Symfony\Component\HttpKernel\Kernel
            [type] => ->
        )

    [14] => Array
        (
            [file] => /var/www/html/vendor/codeception/module-symfony/src/Codeception/Lib/Connector/Symfony.php
            [line] => 55
            [function] => doRequest
            [class] => Symfony\Component\HttpKernel\HttpKernelBrowser
            [type] => ->
        )

    [15] => Array
        (
            [file] => /var/www/html/vendor/symfony/browser-kit/AbstractBrowser.php
            [line] => 369
            [function] => doRequest
            [class] => Codeception\Lib\Connector\Symfony
            [type] => ->
        )

    [16] => Array
        (
            [file] => /var/www/html/vendor/codeception/lib-innerbrowser/src/Codeception/Lib/InnerBrowser.php
            [line] => 243
            [function] => request
            [class] => Symfony\Component\BrowserKit\AbstractBrowser
            [type] => ->
        )

    [17] => Array
        (
            [file] => /var/www/html/vendor/codeception/lib-innerbrowser/src/Codeception/Lib/InnerBrowser.php
            [line] => 161
            [function] => clientRequest
            [class] => Codeception\Lib\InnerBrowser
            [type] => ->
        )

    [18] => Array
        (
            [file] => /var/www/html/vendor/codeception/module-rest/src/Codeception/Module/REST.php
            [line] => 701
            [function] => _request
            [class] => Codeception\Lib\InnerBrowser
            [type] => ->
        )

    [19] => Array
        (
            [file] => /var/www/html/vendor/codeception/module-rest/src/Codeception/Module/REST.php
            [line] => 475
            [function] => execute
            [class] => Codeception\Module\REST
            [type] => ->
        )

    [20] => Array
        (
            [function] => sendPost
            [class] => Codeception\Module\REST
            [type] => ->
        )

    [21] => Array
        (
            [file] => /var/www/html/vendor/codeception/codeception/src/Codeception/Step.php
            [line] => 292
            [function] => call_user_func_array
        )

    [22] => Array
        (
            [file] => /var/www/html/vendor/codeception/codeception/src/Codeception/Scenario.php
            [line] => 76
            [function] => run
            [class] => Codeception\Step
            [type] => ->
        )

    [23] => Array
        (
            [file] => /var/www/html/tests/_support/_generated/FunctionalTesterActions.php
            [line] => 4252
            [function] => runStep
            [class] => Codeception\Scenario
            [type] => ->
        )

    [24] => Array
        (
            [file] => /var/www/html/tests/_support/FunctionalTester.php
            [line] => 49
            [function] => sendPost
            [class] => App\Tests\FunctionalTester
            [type] => ->
        )

    [25] => Array
        (
            [file] => /var/www/html/tests/functional/Common/AdminAjaxControllerCest.php
            [line] => 15
            [function] => apiLogin
            [class] => App\Tests\FunctionalTester
            [type] => ->
        )

    [26] => Array
        (
            [function] => evaluateRule
            [class] => App\Tests\Functional\Common\AdminAjaxControllerCest
            [type] => ->
        )

    [27] => Array
        (
            [file] => /var/www/html/vendor/codeception/codeception/src/Codeception/Lib/Di.php
            [line] => 130
            [function] => invokeArgs
            [class] => ReflectionMethod
            [type] => ->
        )

    [28] => Array
        (
            [file] => /var/www/html/vendor/codeception/codeception/src/Codeception/Test/Cest.php
            [line] => 184
            [function] => injectDependencies
            [class] => Codeception\Lib\Di
            [type] => ->
        )

    [29] => Array
        (
            [file] => /var/www/html/vendor/codeception/codeception/src/Codeception/Test/Cest.php
            [line] => 200
            [function] => invoke
            [class] => Codeception\Test\Cest
            [type] => ->
        )

    [30] => Array
        (
            [file] => /var/www/html/vendor/codeception/codeception/src/Codeception/Test/Cest.php
            [line] => 129
            [function] => executeTestMethod
            [class] => Codeception\Test\Cest
            [type] => ->
        )

    [31] => Array
        (
            [file] => /var/www/html/vendor/codeception/codeception/src/Codeception/Test/Test.php
            [line] => 168
            [function] => test
            [class] => Codeception\Test\Cest
            [type] => ->
        )

    [32] => Array
        (
            [file] => /var/www/html/vendor/codeception/codeception/src/Codeception/Suite.php
            [line] => 130
            [function] => realRun
            [class] => Codeception\Test\Test
            [type] => ->
        )

    [33] => Array
        (
            [file] => /var/www/html/vendor/codeception/codeception/src/Codeception/SuiteManager.php
            [line] => 151
            [function] => run
            [class] => Codeception\Suite
            [type] => ->
        )

    [34] => Array
        (
            [file] => /var/www/html/vendor/codeception/codeception/src/Codeception/Codecept.php
            [line] => 260
            [function] => run
            [class] => Codeception\SuiteManager
            [type] => ->
        )

    [35] => Array
        (
            [file] => /var/www/html/vendor/codeception/codeception/src/Codeception/Codecept.php
            [line] => 216
            [function] => runSuite
            [class] => Codeception\Codecept
            [type] => ->
        )

    [36] => Array
        (
            [file] => /var/www/html/vendor/codeception/codeception/src/Codeception/Command/Run.php
            [line] => 646
            [function] => run
            [class] => Codeception\Codecept
            [type] => ->
        )

    [37] => Array
        (
            [file] => /var/www/html/vendor/codeception/codeception/src/Codeception/Command/Run.php
            [line] => 467
            [function] => runSuites
            [class] => Codeception\Command\Run
            [type] => ->
        )

    [38] => Array
        (
            [file] => /var/www/html/vendor/symfony/console/Command/Command.php
            [line] => 279
            [function] => execute
            [class] => Codeception\Command\Run
            [type] => ->
        )

    [39] => Array
        (
            [file] => /var/www/html/vendor/symfony/console/Application.php
            [line] => 1031
            [function] => run
            [class] => Symfony\Component\Console\Command\Command
            [type] => ->
        )

    [40] => Array
        (
            [file] => /var/www/html/vendor/symfony/console/Application.php
            [line] => 318
            [function] => doRunCommand
            [class] => Symfony\Component\Console\Application
            [type] => ->
        )

    [41] => Array
        (
            [file] => /var/www/html/vendor/symfony/console/Application.php
            [line] => 169
            [function] => doRun
            [class] => Symfony\Component\Console\Application
            [type] => ->
        )

    [42] => Array
        (
            [file] => /var/www/html/vendor/codeception/codeception/src/Codeception/Application.php
            [line] => 112
            [function] => run
            [class] => Symfony\Component\Console\Application
            [type] => ->
        )

    [43] => Array
        (
            [file] => /var/www/html/vendor/codeception/codeception/app.php
            [line] => 45
            [function] => run
            [class] => Codeception\Application
            [type] => ->
        )

    [44] => Array
        (
            [file] => /var/www/html/vendor/codeception/codeception/app.php
            [line] => 46
            [function] => {closure}
        )

    [45] => Array
        (
            [file] => /var/www/html/vendor/codeception/codeception/codecept
            [line] => 7
            [args] => Array
                (
                    [0] => /var/www/html/vendor/codeception/codeception/app.php
                )

            [function] => require
        )

    [46] => Array
        (
            [file] => /var/www/html/vendor/bin/codecept
            [line] => 119
            [args] => Array
                (
                    [0] => /var/www/html/vendor/codeception/codeception/codecept
                )

            [function] => include
        )

)

Example of OpenApi\Attributes usage: image

dmamchyts commented 2 months ago

cc @DerManoMann

deluxetom commented 2 months ago

I'm having the same issue, I'm not sure how to define the context in NelmioApiDocBundle to fix this

D3strukt0r commented 2 months ago

For more context for everyone, came from this merge: https://github.com/zircote/swagger-php/pull/1563

D3strukt0r commented 2 months ago

@DerManoMann What does one need to change when using https://github.com/nelmio/NelmioApiDocBundle do avoid this deprecation? I mean there must be a reason why you activated that deprecation, it's just very unclear how to not trigger this.

DerManoMann commented 2 months ago

This is related to how the bundle instantiates annotations internally without passing in a context. There has been work to fix that, but obviously not enough. Admittedly, this is a part of swagger-php that is not structured very well.

brpauwels commented 2 months ago

We're also getting the deprecations in a Symfony project without NelmioApiDocBundle. However, they only occur on controllers where we use attributes instead of annotations to configure OA.

lhausammann commented 2 months ago

Some Nelmio Testcases just need to add a context - that should be fixable with some effort. However, there remains a special case: When using an attribute with TARGET_PARAMETER in Symfony in a controller, the framework instantiates the attribute. Therefore, theres no context available. The effect can be seen in that test Controller: https://github.com/nelmio/NelmioApiDocBundle/blob/master/tests/Functional/Controller/ApiController81.php#L462

which triggers in turn the Symfony arguments resolver which creates the metadata: https://github.com/symfony/symfony/blob/7.1/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php#L30

Then the attribute instantiation triggers the deprecation because of the missing context.

(at least thats my understanding).

In my opinion, it should be allowed to instantiate an annotation direclty (for such cases), but i am really not sure whats the best way to achieve that. An easy fix would be to set up a context in ParameterTrait if none is set and it doesnt exist in Generator::$context. But i dont know what the goal is with the $context in the future (and don't understand it thoroughly).

Trace:

array(5) {
  [0]=>
  array(6) {
    ["file"]=>
    string(70) "/app/vendor/zircote/swagger-php/src/Annotations/AbstractAnnotation.php"
    ["line"]=>
    int(103)
    ["function"]=>
    string(6) "detect"
    ["class"]=>
    string(15) "OpenApi\Context"
    ["type"]=>
    string(2) "::"
    ["args"]=>
    array(1) {
      [0]=>
      int(1)
    }
  }
  [1]=>
  array(6) {
    ["file"]=>
    string(65) "/app/vendor/zircote/swagger-php/src/Attributes/ParameterTrait.php"
    ["line"]=>
    int(65)
    ["function"]=>
    string(11) "__construct"
    ["class"]=>
    string(38) "OpenApi\Annotations\AbstractAnnotation"
    ["type"]=>
    string(2) "->"
    ["args"]=>
    array(1) {
      [0]=>
      array(16) {
        ["parameter"]=>
        string(28) "@OA\Generator::UNDEFINED🙈"
        ["name"]=>
        string(28) "@OA\Generator::UNDEFINED🙈"
        ["description"]=>
        string(28) "@OA\Generator::UNDEFINED🙈"
        ["in"]=>
        string(4) "path"
        ["required"]=>
        string(28) "@OA\Generator::UNDEFINED🙈"
        ["deprecated"]=>
        string(28) "@OA\Generator::UNDEFINED🙈"
        ["allowEmptyValue"]=>
        string(28) "@OA\Generator::UNDEFINED🙈"
        ["ref"]=>
        string(28) "@OA\Generator::UNDEFINED🙈"
        ["example"]=>
        string(28) "@OA\Generator::UNDEFINED🙈"
        ["style"]=>
        string(28) "@OA\Generator::UNDEFINED🙈"
        ["explode"]=>
        string(28) "@OA\Generator::UNDEFINED🙈"
        ["allowReserved"]=>
        string(28) "@OA\Generator::UNDEFINED🙈"
        ["spaceDelimited"]=>
        string(28) "@OA\Generator::UNDEFINED🙈"
        ["pipeDelimited"]=>
        string(28) "@OA\Generator::UNDEFINED🙈"
        ["x"]=>
        string(28) "@OA\Generator::UNDEFINED🙈"
        ["value"]=>
        array(0) {
        }
      }
    }
  }
  [2]=>
  array(6) {
    ["file"]=>
    string(52) "/app/tests/Functional/Controller/ApiController81.php"
    ["line"]=>
    int(462)
    ["function"]=>
    string(11) "__construct"
    ["class"]=>
    string(28) "OpenApi\Attributes\Parameter"
    ["type"]=>
    string(2) "->"
    ["args"]=>
    array(0) {
    }
  }
  [3]=>
  array(6) {
    ["file"]=>
    string(78) "/app/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactory.php"
    ["line"]=>
    int(30)
    ["function"]=>
    string(11) "newInstance"
    ["class"]=>
    string(19) "ReflectionAttribute"
    ["type"]=>
    string(2) "->"
    ["args"]=>
    array(0) {
    }
  }
  [4]=>
  array(6) {
    ["file"]=>
    string(50) "/app/src/RouteDescriber/RouteArgumentDescriber.php"
    ["line"]=>
    int(42)
    ["function"]=>
    string(22) "createArgumentMetadata"
    ["class"]=>
    string(71) "Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory"
    ["type"]=>
    string(2) "->"
    ["args"]=>
    array(2) {
      [0]=>
      string(83) "Nelmio\ApiDocBundle\Tests\Functional\Controller\ApiController::inlinePathParameters"
      [1]=>
      object(ReflectionMethod)#429 (2) {
        ["name"]=>
        string(20) "inlinePathParameters"
        ["class"]=>
        string(63) "Nelmio\ApiDocBundle\Tests\Functional\Controller\ApiController81"
      }
    }
  }
}
lhausammann commented 2 months ago

it seems that its not symfony itself, but the Routedescriber which just uses the ArgumentMetafactory: so its probably possible to save the generator context there (in NelmioApiDoc), replace it for the describer step and set the context back there.

DerManoMann commented 2 months ago

@lhausammann - thanks for the investigation and summary.

I think you are correct in that it should be possible to instantiate attributes on their own without sideeffects... ~got some ideas~.

We could just use new Context() here or just not trigger the deprecation. However, the fact is that having a context tree with a shared root context is a fundamental assumption in swagger-php.

So, detecting these cases is perhaps important as it might mean some annotations use the wrong openapi version during serialization or fail to use the correct logger.

Not sure why code would instantiate random attributes, tbh, but I suspect I do not have all the context required.

It would be nice to just use new Context() and log a warning, but the logger is taken from the context and defaults to the NullLogger... :/

lhausammann commented 2 months ago

NelmioApiBundle is probably the wrong place to fix that: I am able to reproduce that on a fresh symfony installation (without Nelmio Api Doc, just swagger-php):

image

Its maybe(?) unfortunate that Symfony has to instantiate every attribute in order to make it available for controller methods for further processing(https://github.com/symfony/http-kernel/blob/7.0/Event/ControllerEvent.php#L107).

So it seems to me now that the behaviour should be fixed here in the swagger-php bundle.

Maybe a possible solution could be to create a context on the fly, but "mark" it somehow for further processing (and handle it separately if that is needed)? Im also not sure about the "warning" approach - in a symfony application that would generate and fill logs quickly.

DerManoMann commented 2 months ago

Thanks for the details, some good inspiration in there.