phpswaggeropenapiswagger-php

zircote/swagger-php - Skipping unknown La\Product


Problem

I'm trying to use zircote/swagger-php to generate an OpenApi file from PHP-code. For some reason I keep getting the following error:

Warning: Skipping unknown La\Product
Warning: Required @OA\PathItem() not found
Warning: Required @OA\Info() not found

Setup

I'm using a docker container to have it reproducible. My setup is as follows:

.
├── Dockerfile
├── project
│   └── myTest.php
└── swagger.php

and here is the contents of the files:

Dockerfile

FROM composer:latest
COPY . /usr/src/myapp
WORKDIR /usr/src/myapp
RUN ["composer", "require", "zircote/swagger-php"]
RUN ["composer", "require", "doctrine/annotations"]
CMD ["sh"]

Inside the container I run: ./vendor/bin/openapi project/

project/myTest.php

<?php

namespace La;
use OpenApi\Annotations as OA;

/**
 * @OA\Info(title="My First API", version="0.1")
 * @OA\Get(
 *    path="/",
 *    @OA\Response(response="200", description="Some description.")
 * )
 */
#[SomeAttribute]
class Product {
    /**
     * @OA\Get(
     *     path="/api/data.json",
     *     @OA\Response(
     *         response="200",
     *         description="The data"
     *     )
     * )
     */
    public function getResource()
    {
        // ...
    }
    public $la = 12;
}

swagger.php

<?php

require("vendor/autoload.php");
$openapi = \OpenApi\Generator::scan(['project']);
header('Content-Type: application/x-yaml');
echo $openapi->toJson();

Yields same error as running a command-line command above.

Have tried

I have tried several tutorials and examples, with the same output, just different namespaces and class-names being skipped.

I have seen here that it all comes down to autoloading and reflection, but it didn't give me any practical hints what to do.

I also saw in some comments on github that downgrading to php 8.1 helps, but that didn't work for me. After a lot of fiddeling with the Dockerfile, it came down to the exact same error.

Also this question on SO didn't help. Seems to be very Laravel specific.

The suggestion here to have just one dummy class, yields pretty much the same error, tho there is no class skipped.

Just to make sure that I'm not having something really stupid, I verified that reflection is working with this code:

<?php

require './project/myTest.php';

$reflector = new ReflectionClass('La\Product');

$t = new La\Product();
echo $t->la . "\n";

var_dump($reflector->getProperties());
echo "\n";

print_r(array_map(fn($att) => $att->getName(), $reflector->getAttributes()));

var_dump( $reflector->getDocComment());

Which gave the expected output.


Solution

  • I think you are running into this issue: https://zircote.github.io/swagger-php/guide/faq.html#skipping-unknown-someclass

    You've done pretty much everything correct, except for autoloading. In fact, in your manual example you've actually explicitely require'd the test class file for the same reason - the class needs to be loaded for reflection to work.

    There are two ways to get this basic test to work

    a) Add myTest.php as bootstrap option to the CL This will do the require for you, just like in your test code.

    ./vendor/bin/openapi -b project/myTest.php project/
    

    b) Set up composer autoloading. Seems there is no composer.json in your setup. I suppose something like

    {
        "require": {
            "zircote/swagger-php": "^4.8",
            "doctrine/annotations": "^2.0"
        },
       "autoload": {
            "psr-4": {
                "La\\": "project/"
            }
        }
    }
    

    should work. Also means you can change your Dockerfile to a single composer command:

    RUN ["composer", "install"]
    

    It should be clear that option (b) is the more scalable :)

    Final thing about autoloading is that PSR-4 requires the filename to match the classname, so you'll need to rename myTest.php to Product.php.