zircote / swagger-php

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

Warning: Skipping unknown #1136

Closed pascalf22 closed 2 years ago

pascalf22 commented 2 years ago

Hello !

I installed swagger-php via composer and i followed your documentation..

Here what i did :

To generate always-up-to-date documentation, i created a new file : api\documentation\api.php

require($_SERVER['DOCUMENT_ROOT']."/api/vendor/autoload.php");
$openapi = \OpenApi\Generator::scan([$_SERVER['DOCUMENT_ROOT']."/api/webapp/v1/Models/"]);
header('Content-Type: application/json');
echo $openapi->toJSON();

in my Models folder : api\webapp\v1\Models\

<?php
/**
 * @OA\Info(title="My First API", version="0.1")
 */
class BlogsModel {
    public $dbcon;
    public $http_method;
    public $config;
    public $userId;

/**
 * @OA\Get(
 *     path="/api/resource.json",
 *     @OA\Response(response="200", description="An example resource")
 * )
 */
    public function getBlogs($pageno=1)
    {
      .....

But when in my browser when i try to see : http://refonte.local/api/documentation/api.php

I get this error :

( ! ) Warning: Skipping unknown \BlogsModel in D:\Users\pascal\www\refonte\api\vendor\zircote\swagger-php\src\Loggers\DefaultLogger.php on line 31
--

1 | 0.0001 | 360552 | {main}( ) | ...\api.php:0
2 | 0.0003 | 367072 | OpenApi\Generator::scan( $sources = [0 => 'D:/Users/pascal/www/refonte/api/webapp/v1/Models/'], $options = ??? ) | ...\api.php:3
3 | 0.0004 | 372992 | OpenApi\Generator->generate( $sources = [0 => 'D:/Users/pascal/www/refonte/api/webapp/v1/Models/'], $analysis = NULL, $validate = TRUE ) | ...\Generator.php:294
4 | 0.0008 | 378360 | OpenApi\Generator->scanSources( $sources = [0 => 'D:/Users/pascal/www/refonte/api/webapp/v1/Models/'], $analysis = class OpenApi\Analysis { public $annotations = class SplObjectStorage { private $storage = [...] }; public $classes = []; public $interfaces = []; public $traits = []; public $enums = []; public $openapi = NULL; public $context = class OpenApi\Context { private $_parent = NULL; public $version = '3.0.0'; public $logger = class OpenApi\Loggers\DefaultLogger { ... } } }, $rootContext = class OpenApi\Context { private $_parent = NULL; public $version = '3.0.0'; public $logger = class OpenApi\Loggers\DefaultLogger { } } ) | ...\Generator.php:342
5 | 0.0018 | 399800 | OpenApi\Generator->scanSources( $sources = class Symfony\Component\Finder\Finder { private int $mode = 1; private array $names = [0 => '*.php']; private array $notNames = []; private array $exclude = []; private array $filters = []; private array $depths = []; private array $sizes = []; private bool $followLinks = TRUE; private bool $reverseSorting = FALSE; private Closure\|int\|false $sort = 1; private int $ignore = 3; private array $dirs = [0 => 'D:\\Users\\pascal\\www\\refonte\\api\\webapp\\v1\\Models']; private array $dates = []; private array $iterators = []; private array $contains = []; private array $notContains = []; private array $paths = []; private array $notPaths = []; private bool $ignoreUnreadableDirs = FALSE }, $analysis = class OpenApi\Analysis { public $annotations = class SplObjectStorage { private $storage = [...] }; public $classes = []; public $interfaces = []; public $traits = []; public $enums = []; public $openapi = NULL; public $context = class OpenApi\Context { private $_parent = NULL; public $version = '3.0.0'; public $logger = class OpenApi\Loggers\DefaultLogger { ... } } }, $rootContext = class OpenApi\Context { private $_parent = NULL; public $version = '3.0.0'; public $logger = class OpenApi\Loggers\DefaultLogger { } } ) | ...\Generator.php:376
6 | 0.0028 | 481672 | OpenApi\Analysers\ReflectionAnalyser->fromFile( $filename = 'D:\\Users\\pascal\\www\\refonte\\api\\webapp\\v1\\Models\\BlogsModel.php', $context = class OpenApi\Context { private $_parent = NULL; public $version = '3.0.0'; public $logger = class OpenApi\Loggers\DefaultLogger { } } ) | ...\Generator.php:378
7 | 0.0056 | 483760 | OpenApi\Analysers\ReflectionAnalyser->analyzeFqdn( $fqdn = '\\BlogsModel', $analysis = class OpenApi\Analysis { public $annotations = class SplObjectStorage { private $storage = [...] }; public $classes = []; public $interfaces = []; public $traits = []; public $enums = []; public $openapi = NULL; public $context = class OpenApi\Context { private $_parent = NULL; public $version = '3.0.0'; public $logger = class OpenApi\Loggers\DefaultLogger { ... } } }, $details = ['uses' => [], 'interfaces' => [], 'traits' => [], 'enums' => [], 'methods' => [0 => 'getBlogs', 1 => 'readArticle'], 'properties' => [0 => 'dbcon', 1 => 'http_method', 2 => 'config', 3 => 'userId']] ) | ...\ReflectionAnalyser.php:53
8 | 0.0056 | 484232 | Psr\Log\AbstractLogger->warning( $message = 'Skipping unknown \\BlogsModel', $context = ??? ) | ...\ReflectionAnalyser.php:79
9 | 0.0056 | 484232 | OpenApi\Loggers\DefaultLogger->log( $level = 'warning', $message = 'Skipping unknown \\BlogsModel', $context = [] ) | ...\LoggerTrait.php:86
10 | 0.0056 | 484232 | trigger_error( $message = 'Skipping unknown \\BlogsModel', $error_level = 512 ) | ...\DefaultLogger.php:31

</font><br class="Apple-interchange-newline"><!--EndFragment-->
</body>
</html>

( ! ) Warning: Skipping unknown \BlogsModel in D:\Users\pascal\www\refonte\api\vendor\zircote\swagger-php\src\Loggers\DefaultLogger.php on line 31
Call Stack
#   Time    Memory  Function    Location
1   0.0001  360552  {main}( )   ...\api.php:0
2   0.0003  367072  OpenApi\Generator::scan( $sources = [0 => 'D:/Users/pascal/www/refonte/api/webapp/v1/Models/'], $options = ??? )    ...\api.php:3
3   0.0004  372992  OpenApi\Generator->generate( $sources = [0 => 'D:/Users/pascal/www/refonte/api/webapp/v1/Models/'], $analysis = NULL, $validate = TRUE )    ...\Generator.php:294
4   0.0008  378360  OpenApi\Generator->scanSources( $sources = [0 => 'D:/Users/pascal/www/refonte/api/webapp/v1/Models/'], $analysis = class OpenApi\Analysis { public $annotations = class SplObjectStorage { private $storage = [...] }; public $classes = []; public $interfaces = []; public $traits = []; public $enums = []; public $openapi = NULL; public $context = class OpenApi\Context { private $_parent = NULL; public $version = '3.0.0'; public $logger = class OpenApi\Loggers\DefaultLogger { ... } } }, $rootContext = class OpenApi\Context { private $_parent = NULL; public $version = '3.0.0'; public $logger = class OpenApi\Loggers\DefaultLogger { } } )  ...\Generator.php:342
5   0.0018  399800  OpenApi\Generator->scanSources( $sources = class Symfony\Component\Finder\Finder { private int $mode = 1; private array $names = [0 => '*.php']; private array $notNames = []; private array $exclude = []; private array $filters = []; private array $depths = []; private array $sizes = []; private bool $followLinks = TRUE; private bool $reverseSorting = FALSE; private Closure|int|false $sort = 1; private int $ignore = 3; private array $dirs = [0 => 'D:\\Users\\pascal\\www\\refonte\\api\\webapp\\v1\\Models']; private array $dates = []; private array $iterators = []; private array $contains = []; private array $notContains = []; private array $paths = []; private array $notPaths = []; private bool $ignoreUnreadableDirs = FALSE }, $analysis = class OpenApi\Analysis { public $annotations = class SplObjectStorage { private $storage = [...] }; public $classes = []; public $interfaces = []; public $traits = []; public $enums = []; public $openapi = NULL; public $context = class OpenApi\Context { private $_parent = NULL; public $version = '3.0.0'; public $logger = class OpenApi\Loggers\DefaultLogger { ... } } }, $rootContext = class OpenApi\Context { private $_parent = NULL; public $version = '3.0.0'; public $logger = class OpenApi\Loggers\DefaultLogger { } } )  ...\Generator.php:376
6   0.0028  481672  OpenApi\Analysers\ReflectionAnalyser->fromFile( $filename = 'D:\\Users\\pascal\\www\\refonte\\api\\webapp\\v1\\Models\\BlogsModel.php', $context = class OpenApi\Context { private $_parent = NULL; public $version = '3.0.0'; public $logger = class OpenApi\Loggers\DefaultLogger { } } ) ...\Generator.php:378
7   0.0056  483760  OpenApi\Analysers\ReflectionAnalyser->analyzeFqdn( $fqdn = '\\BlogsModel', $analysis = class OpenApi\Analysis { public $annotations = class SplObjectStorage { private $storage = [...] }; public $classes = []; public $interfaces = []; public $traits = []; public $enums = []; public $openapi = NULL; public $context = class OpenApi\Context { private $_parent = NULL; public $version = '3.0.0'; public $logger = class OpenApi\Loggers\DefaultLogger { ... } } }, $details = ['uses' => [], 'interfaces' => [], 'traits' => [], 'enums' => [], 'methods' => [0 => 'getBlogs', 1 => 'readArticle'], 'properties' => [0 => 'dbcon', 1 => 'http_method', 2 => 'config', 3 => 'userId']] ) ...\ReflectionAnalyser.php:53
8   0.0056  484232  Psr\Log\AbstractLogger->warning( $message = 'Skipping unknown \\BlogsModel', $context = ??? )   ...\ReflectionAnalyser.php:79
9   0.0056  484232  OpenApi\Loggers\DefaultLogger->log( $level = 'warning', $message = 'Skipping unknown \\BlogsModel', $context = [] ) ...\LoggerTrait.php:86
10  0.0056  484232  [trigger_error](http://www.php.net/function.trigger-error)( $message = 'Skipping unknown \\BlogsModel', $error_level = 512 )    ...\DefaultLogger.php:31

( ! ) Warning: Required @OA\Info() not found in D:\Users\pascal\www\refonte\api\vendor\zircote\swagger-php\src\Loggers\DefaultLogger.php on line 31
Call Stack
#   Time    Memory  Function    Location
1   0.0001  360552  {main}( )   ...\api.php:0
2   0.0003  367072  OpenApi\Generator::scan( $sources = [0 => 'D:/Users/pascal/www/refonte/api/webapp/v1/Models/'], $options = ??? )    ...\api.php:3
3   0.0004  372992  OpenApi\Generator->generate( $sources = [0 => 'D:/Users/pascal/www/refonte/api/webapp/v1/Models/'], $analysis = NULL, $validate = TRUE )    ...\Generator.php:294
4   0.0114  496360  OpenApi\Analysis->validate( )   ...\Generator.php:353
5   0.0114  496360  OpenApi\Annotations\OpenApi->validate( $parents = ???, $skip = ???, $ref = ??? )    ...\Analysis.php:438
6   0.0114  496360  OpenApi\Annotations\AbstractAnnotation->validate( $parents = [], $skip = [], $ref = '#' )   ...\OpenApi.php:145
7   0.0115  496416  Psr\Log\AbstractLogger->warning( $message = 'Required @OA\\Info() not found', $context = ??? )  ...\AbstractAnnotation.php:465
8   0.0115  496416  OpenApi\Loggers\DefaultLogger->log( $level = 'warning', $message = 'Required @OA\\Info() not found', $context = [] )    ...\LoggerTrait.php:86
9   0.0115  496416  [trigger_error](http://www.php.net/function.trigger-error)( $message = 'Required @OA\\Info() not found', $error_level = 512 )   ...\DefaultLogger.php:31

( ! ) Warning: Required @OA\PathItem() not found in D:\Users\pascal\www\refonte\api\vendor\zircote\swagger-php\src\Loggers\DefaultLogger.php on line 31
Call Stack
#   Time    Memory  Function    Location
1   0.0001  360552  {main}( )   ...\api.php:0
2   0.0003  367072  OpenApi\Generator::scan( $sources = [0 => 'D:/Users/pascal/www/refonte/api/webapp/v1/Models/'], $options = ??? )    ...\api.php:3
3   0.0004  372992  OpenApi\Generator->generate( $sources = [0 => 'D:/Users/pascal/www/refonte/api/webapp/v1/Models/'], $analysis = NULL, $validate = TRUE )    ...\Generator.php:294
4   0.0114  496360  OpenApi\Analysis->validate( )   ...\Generator.php:353
5   0.0114  496360  OpenApi\Annotations\OpenApi->validate( $parents = ???, $skip = ???, $ref = ??? )    ...\Analysis.php:438
6   0.0114  496360  OpenApi\Annotations\AbstractAnnotation->validate( $parents = [], $skip = [], $ref = '#' )   ...\OpenApi.php:145
7   0.0142  496424  Psr\Log\AbstractLogger->warning( $message = 'Required @OA\\PathItem() not found', $context = ??? )  ...\AbstractAnnotation.php:465
8   0.0142  496424  OpenApi\Loggers\DefaultLogger->log( $level = 'warning', $message = 'Required @OA\\PathItem() not found', $context = [] )    ...\LoggerTrait.php:86
9   0.0142  496424  [trigger_error](http://www.php.net/function.trigger-error)( $message = 'Required @OA\\PathItem() not found', $error_level = 512 )   ...\DefaultLogger.php:31

( ! ) Warning: Cannot modify [header](http://www.php.net/function.header) information - headers already sent by (output started at D:\Users\pascal\www\refonte\api\vendor\zircote\swagger-php\src\Loggers\DefaultLogger.php:31) in D:\Users\pascal\www\refonte\api\documentation\api.php on line 4
Call Stack
#   Time    Memory  Function    Location
1   0.0001  360552  {main}( )   ...\api.php:0
2   0.0167  495528  header( $header = 'Content-Type: application/json' )    ...\api.php:4

What i'm doing wrong ?

thank you so much !

Have a great day !

DerManoMann commented 2 years ago

The code is trying to use reflection to collect details about the PHP code and cannot find the class \BlogsModel. I would guess that there is a namespace missing somewhere. Does the API endpoint actually work or at least load?

pascalf22 commented 2 years ago

Hello ! Thank you for your reply ! Yes, my class (end point) works perfectly !

In a browser, if i try to reach my end point to get blog article : http://refonte.local/api/webapp/v1/blogs/1 it works perfectly and i get blog articles titles...

here my api/webapp/v1/Models/BlogsModel.php :

<?php
/**
 * @OA\Info(title="My First API", version="0.1")
 */
class BlogsModel {
    public $dbcon;
    public $http_method;
    public $config;
    public $userId;

/**
 * @OA\Get(
 *     path="/api/resource.json",
 *     @OA\Response(response="200", description="An example resource")
 * )
 */
    public function getBlogs($pageno=1)
    {
        ...
    }
    public function readArticle($id)
    {
      ...

    }
}

?>

I suppose my class is OK ? but it's working when i do : http://refonte.local/api/webapp/v1/blogs/1 or http://refonte.local/api/webapp/v1/blog/267

thanks a lot !

pascalf22 commented 2 years ago

Hello !

Ok it is working now... now i get :

{
    "openapi": "3.0.0",
    "info": {
        "title": "My First API",
        "version": "0.1"
    },
    "paths": {
        "/api/resource.json": {
            "get": {
                "operationId": "82c2269a247163a711afe70f585c6866",
                "responses": {
                    "200": {
                        "description": "An example resource"
                    }
                }
            }
        }
    }
}

but i had to add this in my /api/documentation/api.php :

spl_autoload_register('autoloader');
function autoloader(string $name) {

    if (file_exists('../webapp/v1/Controllers/'.$name.'.php')){
        require_once '../webapp/v1/Controllers/'.$name.'.php';
    }
    else if (file_exists('../webapp/v1/Models/'.$name.'.php')){
        require_once '../webapp/v1/Models/'.$name.'.php';
    }
}

The final /api/documentation/api.php file is :


<?php
spl_autoload_register('autoloader');
function autoloader(string $name) {

    if (file_exists('../webapp/v1/Controllers/'.$name.'.php')){
        require_once '../webapp/v1/Controllers/'.$name.'.php';
    }
    else if (file_exists('../webapp/v1/Models/'.$name.'.php')){
        require_once '../webapp/v1/Models/'.$name.'.php';
    }
}
require($_SERVER['DOCUMENT_ROOT']."/api/vendor/autoload.php");
$openapi = \OpenApi\Generator::scan([$_SERVER['DOCUMENT_ROOT'].'/api/webapp/v1/Models']);
header('Content-Type: application/json');
echo $openapi->toJSON();
?>

Is that normal i had to add this ?

Thanks so much !

DerManoMann commented 2 years ago

EDIT: Ah, I should have read all of your comment. ..

The code fails at this line:

        if (!class_exists($fqdn) && !interface_exists($fqdn) && !trait_exists($fqdn) && (!function_exists('enum_exists') || !enum_exists($fqdn))) {
            $analysis->context->logger->warning('Skipping unknown ' . $fqdn);

with $fqdn being \BlogModel. That means class_exists() cannot resolve it. That, in turn, means the composer classloader cannot find/resolve the class name. There isn't a lot I can do to help from here I am afraid.

Your additions, of course, fix that.

My best guess at this point is that you are using a framework that magically uses aliases instead of proper namespaces. swagger-php relies just on the composer autoloader, not any other features outside of that.

You might be able to get around this by creating an optimized autoloader dump; something like

composer dump-autoload --optimize
pascalf22 commented 2 years ago

Hello ! thanks :)

I just want to know if you read my last message.. because i think we submit a message at the same time ;)

thanks

DerManoMann commented 2 years ago

Yes, that is why I edited my last message again :)

pascalf22 commented 2 years ago

Thank you so much ! I reallya appreicated your help !

Have a great day ! Pascal

rsplsk commented 1 year ago

Thanks a lot for this code.

spl_autoload_register('autoloader'); function autoloader(string $name) {

if (file_exists('../webapp/v1/Controllers/'.$name.'.php')){
    require_once '../webapp/v1/Controllers/'.$name.'.php';
}
else if (file_exists('../../models/'.$name.'.php')){
    require_once '../../models/'.$name.'.php';
}

}

RodrigoPimienta commented 1 year ago

Hello friends, I have the same issue, but fortunately I could resolve with the explication, but I want to add some comments for the future people resolved more quickly and easy.

I use this distribution or files:

image

api\v1\documentation\api.php <?php spl_autoload_register('autoloader'); function autoloader(string $name) {

 if (file_exists('../Models/'.$name.'.php')){
    require_once '../Models/'.$name.'.php';
}

} require($_SERVER['DOCUMENT_ROOT']."/api/vendor/autoload.php"); $openapi = \OpenApi\Generator::scan(['../Models']); echo $openapi->toJSON(); ?>

api\v1\documentation\index.php

<!DOCTYPE html>

Swagger UI

This file I get of this repo: swagger-api/swagger-ui: Swagger UI is a collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API. (github.com)

I just copy the index.html on the folder dist and copy and page the css, png and js that I need into my folder swagger

And I was need rename my models as the same name has the class: api/v1/Models/CatAreas.php <?php /**

public function getAreas(): string { … } }

compose.json { "require": { "zircote/swagger-php": "^4.7" } }

zebraf1 commented 2 weeks ago

I ran into a similar issue and it turned out to be a naming/autoloading issue for the underlying class.

Basicly class_exists('Namespace/To/Class') will be false if the class isn't autoloaded. This is what ReflectionAnalyzer->analyzeFqdn() checks.

My file name and the class name inside didn't match, therefore it was not autoloaded for some reason.