252 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Plaintext
		
	
	
		
		
			
		
	
	
			252 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Plaintext
		
	
	
| 
								 | 
							
								#!/usr/bin/env php
							 | 
						||
| 
								 | 
							
								<?php
							 | 
						||
| 
								 | 
							
								use OpenApi\Logger;
							 | 
						||
| 
								 | 
							
								use OpenApi\Analysis;
							 | 
						||
| 
								 | 
							
								use const OpenApi\UNDEFINED;
							 | 
						||
| 
								 | 
							
								use const OpenApi\COLOR_RED;
							 | 
						||
| 
								 | 
							
								use const OpenApi\COLOR_YELLOW;
							 | 
						||
| 
								 | 
							
								use const OpenApi\COLOR_STOP;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								error_reporting(E_ALL);
							 | 
						||
| 
								 | 
							
								// Possible options and their default values.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								$options = [
							 | 
						||
| 
								 | 
							
								    'output' => false,
							 | 
						||
| 
								 | 
							
								    'format' => 'auto',
							 | 
						||
| 
								 | 
							
								    'exclude' => [],
							 | 
						||
| 
								 | 
							
								    'pattern' => '*.php',
							 | 
						||
| 
								 | 
							
								    'bootstrap' => false,
							 | 
						||
| 
								 | 
							
								    'help' => false,
							 | 
						||
| 
								 | 
							
								    'debug' => false,
							 | 
						||
| 
								 | 
							
								    'processor' => [],
							 | 
						||
| 
								 | 
							
								];
							 | 
						||
| 
								 | 
							
								$aliases = [
							 | 
						||
| 
								 | 
							
								    'o' => 'output',
							 | 
						||
| 
								 | 
							
								    'e' => 'exclude',
							 | 
						||
| 
								 | 
							
								    'n' => 'pattern',
							 | 
						||
| 
								 | 
							
								    'b' => 'bootstrap',
							 | 
						||
| 
								 | 
							
								    'h' => 'help',
							 | 
						||
| 
								 | 
							
								    'd' => 'debug',
							 | 
						||
| 
								 | 
							
								    'p' => 'processor',
							 | 
						||
| 
								 | 
							
								    'f' => 'format'
							 | 
						||
| 
								 | 
							
								];
							 | 
						||
| 
								 | 
							
								$needsArgument = [
							 | 
						||
| 
								 | 
							
								    'output',
							 | 
						||
| 
								 | 
							
								    'format',
							 | 
						||
| 
								 | 
							
								    'exclude',
							 | 
						||
| 
								 | 
							
								    'pattern',
							 | 
						||
| 
								 | 
							
								    'bootstrap',
							 | 
						||
| 
								 | 
							
								    'processor',
							 | 
						||
| 
								 | 
							
								];
							 | 
						||
| 
								 | 
							
								$paths = [];
							 | 
						||
| 
								 | 
							
								$error = false;
							 | 
						||
| 
								 | 
							
								define('OpenApi\COLOR_RED', "\033[31m");
							 | 
						||
| 
								 | 
							
								define('OpenApi\COLOR_YELLOW', "\033[33m");
							 | 
						||
| 
								 | 
							
								define('OpenApi\COLOR_STOP', "\033[0m");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								try {
							 | 
						||
| 
								 | 
							
								    // Parse cli arguments
							 | 
						||
| 
								 | 
							
								    for ($i = 1; $i < $argc; $i++) {
							 | 
						||
| 
								 | 
							
								        $arg = $argv[$i];
							 | 
						||
| 
								 | 
							
								        if (substr($arg, 0, 2) === '--') { // longopt
							 | 
						||
| 
								 | 
							
								            $option = substr($arg, 2);
							 | 
						||
| 
								 | 
							
								        } elseif ($arg[0] === '-') { // shortopt
							 | 
						||
| 
								 | 
							
								            if (array_key_exists(substr($arg, 1), $aliases)) {
							 | 
						||
| 
								 | 
							
								                $option = $aliases[$arg[1]];
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                throw new Exception('Unknown option: "' . $arg . '"');
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            $paths[] = $arg;
							 | 
						||
| 
								 | 
							
								            continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (array_key_exists($option, $options) === false) {
							 | 
						||
| 
								 | 
							
								            throw new Exception('Unknown option: "' . $arg . '"');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (in_array($option, $needsArgument)) {
							 | 
						||
| 
								 | 
							
								            if (empty($argv[$i + 1]) || $argv[$i + 1][0] === '-') {
							 | 
						||
| 
								 | 
							
								                throw new Exception('Missing argument for "' . $arg . '"');
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (is_array($options[$option])) {
							 | 
						||
| 
								 | 
							
								                $options[$option][] = $argv[$i + 1];
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                $options[$option] = $argv[$i + 1];
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            $i++;
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            $options[$option] = true;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								} catch (Exception $e) {
							 | 
						||
| 
								 | 
							
								    $error = $e->getMessage();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if (!$error && $options['bootstrap']) {
							 | 
						||
| 
								 | 
							
								    if (is_readable($options['bootstrap']) === false) {
							 | 
						||
| 
								 | 
							
								        $error = 'Invalid `--bootstrap` value: "'.$options['bootstrap'].'"';
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        require_once($options['bootstrap']);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								if (count($paths) === 0) {
							 | 
						||
| 
								 | 
							
								    $error = 'Specify at least one path.';
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								if ($options['help'] === false && $error) {
							 | 
						||
| 
								 | 
							
								    error_log('');
							 | 
						||
| 
								 | 
							
								    error_log(COLOR_RED.'Error: '.$error.COLOR_STOP);
							 | 
						||
| 
								 | 
							
								    $options['help'] = true; // Show help
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								if ($options['help']) {
							 | 
						||
| 
								 | 
							
								    $help = <<<EOF
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Usage: openapi [--option value] [/path/to/project ...]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Options:
							 | 
						||
| 
								 | 
							
								  --output (-o)     Path to store the generated documentation.
							 | 
						||
| 
								 | 
							
								                    ex: --output openapi.yaml
							 | 
						||
| 
								 | 
							
								  --exclude (-e)    Exclude path(s).
							 | 
						||
| 
								 | 
							
								                    ex: --exclude vendor,library/Zend
							 | 
						||
| 
								 | 
							
								  --pattern (-n)    Pattern of files to scan.
							 | 
						||
| 
								 | 
							
								                    ex: --pattern "*.php" or --pattern "/\.(phps|php)$/"
							 | 
						||
| 
								 | 
							
								  --bootstrap (-b)  Bootstrap a php file for defining constants, etc.
							 | 
						||
| 
								 | 
							
								                    ex: --bootstrap config/constants.php
							 | 
						||
| 
								 | 
							
								  --processor       Register an additional processor.
							 | 
						||
| 
								 | 
							
								  --format          Force yaml or json.
							 | 
						||
| 
								 | 
							
								  --debug           Show additional error information.
							 | 
						||
| 
								 | 
							
								  --help (-h)       Display this help message.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								EOF;
							 | 
						||
| 
								 | 
							
								    error_log($help);
							 | 
						||
| 
								 | 
							
								    exit(1);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if (class_exists(Logger::class) === false) {
							 | 
						||
| 
								 | 
							
								    if (file_exists(__DIR__.'/../vendor/autoload.php')) {  // cloned / dev environment?
							 | 
						||
| 
								 | 
							
								        require_once(__DIR__.'/../vendor/autoload.php');
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        require_once(realpath(__DIR__.'/../../../').'/autoload.php');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								$errorTypes = [
							 | 
						||
| 
								 | 
							
								    E_ERROR => 'Error',
							 | 
						||
| 
								 | 
							
								    E_WARNING => 'Warning',
							 | 
						||
| 
								 | 
							
								    E_PARSE => 'Parser error',
							 | 
						||
| 
								 | 
							
								    E_NOTICE => 'Notice',
							 | 
						||
| 
								 | 
							
								    E_STRICT => 'Strict',
							 | 
						||
| 
								 | 
							
								    E_DEPRECATED => 'Deprecated',
							 | 
						||
| 
								 | 
							
								    E_CORE_ERROR => 'Error(Core)',
							 | 
						||
| 
								 | 
							
								    E_CORE_WARNING => 'Warning(Core)',
							 | 
						||
| 
								 | 
							
								    E_COMPILE_ERROR => 'Error(compile)',
							 | 
						||
| 
								 | 
							
								    E_COMPILE_WARNING => 'Warning(Compile)',
							 | 
						||
| 
								 | 
							
								    E_RECOVERABLE_ERROR => 'Error(Recoverable)',
							 | 
						||
| 
								 | 
							
								    E_USER_ERROR => 'Error',
							 | 
						||
| 
								 | 
							
								    E_USER_WARNING => 'Warning',
							 | 
						||
| 
								 | 
							
								    E_USER_NOTICE => 'Notice',
							 | 
						||
| 
								 | 
							
								    E_USER_DEPRECATED => 'Deprecated',
							 | 
						||
| 
								 | 
							
								];
							 | 
						||
| 
								 | 
							
								set_error_handler(function ($errno, $errstr, $file, $line) use ($errorTypes, $options) {
							 | 
						||
| 
								 | 
							
								    if (!(error_reporting() & $errno)) {
							 | 
						||
| 
								 | 
							
								        return; // This error code is not included in error_reporting
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    $type = array_key_exists($errno, $errorTypes) ? $errorTypes[$errno] : 'Error';
							 | 
						||
| 
								 | 
							
								    $color = (substr($type, 0, 5) === 'Error') ? COLOR_RED: COLOR_YELLOW;
							 | 
						||
| 
								 | 
							
								    error_log(COLOR_RED.$type. ': '.$errstr.COLOR_STOP);
							 | 
						||
| 
								 | 
							
								    if ($options['debug']) {
							 | 
						||
| 
								 | 
							
								        error_log(' in '.$file.' on line '.$line);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (substr($type, 0, 5) === 'Error') {
							 | 
						||
| 
								 | 
							
								        exit($errno);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								set_exception_handler(function ($exception) use ($options) {
							 | 
						||
| 
								 | 
							
								    if ($options['debug']) {
							 | 
						||
| 
								 | 
							
								        error_log($exception);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        error_log(COLOR_RED.'Exception: '.$exception->getMessage().COLOR_STOP);
							 | 
						||
| 
								 | 
							
								        // if ($options['debug']) {
							 | 
						||
| 
								 | 
							
								        //     error_log(' in '.$exception->getFile().' on line '.$exception->getLine());
							 | 
						||
| 
								 | 
							
								        // }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    exit($exception->getCode() ?: 1);
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								$exit = 0;
							 | 
						||
| 
								 | 
							
								Logger::getInstance()->log = function ($entry, $type) use ($options, &$exit) {
							 | 
						||
| 
								 | 
							
								    $exit = 1;
							 | 
						||
| 
								 | 
							
								    if ($type === E_USER_NOTICE) {
							 | 
						||
| 
								 | 
							
								        $type = '';
							 | 
						||
| 
								 | 
							
								        $color = COLOR_YELLOW;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        $type = 'Warning: ';
							 | 
						||
| 
								 | 
							
								        $color = COLOR_RED;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if ($entry instanceof Exception) {
							 | 
						||
| 
								 | 
							
								        error_log(COLOR_RED."Error: " . $entry->getMessage().COLOR_STOP);
							 | 
						||
| 
								 | 
							
								        if ($options['debug']) {
							 | 
						||
| 
								 | 
							
								            error_log('Stack trace:'.PHP_EOL.$entry->getTraceAsString());
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        error_log($color. $type . $entry.COLOR_STOP);
							 | 
						||
| 
								 | 
							
								        if ($options['debug']) {
							 | 
						||
| 
								 | 
							
								            // Show backtrace in debug mode
							 | 
						||
| 
								 | 
							
								            $e = (string)(new Exception('trace'));
							 | 
						||
| 
								 | 
							
								            $trace = explode("\n", substr($e, strpos($e, 'Stack trace:')));
							 | 
						||
| 
								 | 
							
								            foreach ($trace as $i => $entry) {
							 | 
						||
| 
								 | 
							
								                if ($i === 0) {
							 | 
						||
| 
								 | 
							
								                    error_log($entry);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                if ($i <= 3) {
							 | 
						||
| 
								 | 
							
								                    continue;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                preg_match('/#([0-9]+) (.*)$/', $entry, $match);
							 | 
						||
| 
								 | 
							
								                error_log('#' .($match[1] - 2).' '.$match[2]);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								$exclude = null;
							 | 
						||
| 
								 | 
							
								if ($options['exclude']) {
							 | 
						||
| 
								 | 
							
								    $exclude = $options['exclude'];
							 | 
						||
| 
								 | 
							
								    if (strpos($exclude[0], ',') !== false) {
							 | 
						||
| 
								 | 
							
								        $exploded = explode(',', $exclude[0]);
							 | 
						||
| 
								 | 
							
								        error_log(COLOR_RED.'Comma-separated exclude paths are deprecated, use multiple --exclude statements: --exclude '.$exploded[0].' --exclude '.$exploded[1]).COLOR_STOP;
							 | 
						||
| 
								 | 
							
								        $exclude[0] = array_shift($exploded);
							 | 
						||
| 
								 | 
							
								        $exclude = array_merge($exclude, $exploded);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								$pattern = "*.php";
							 | 
						||
| 
								 | 
							
								if ($options['pattern']) {
							 | 
						||
| 
								 | 
							
								    $pattern = $options['pattern'];
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								foreach ($options["processor"] as $processor) {
							 | 
						||
| 
								 | 
							
								    $class = '\OpenApi\Processors\\'.$processor;
							 | 
						||
| 
								 | 
							
								    if (class_exists($class)) {
							 | 
						||
| 
								 | 
							
								        $processor = new $class();
							 | 
						||
| 
								 | 
							
								    } elseif (class_exists($processor)) {
							 | 
						||
| 
								 | 
							
								        $processor = new $processor();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    Analysis::registerProcessor($processor);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								$openapi = OpenApi\Generator::scan(OpenApi\Util::finder($paths, $exclude, $pattern));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if ($exit !== 0) {
							 | 
						||
| 
								 | 
							
								    error_log('');
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								if ($options['output'] === false) {
							 | 
						||
| 
								 | 
							
								    if (strtolower($options['format']) === 'json') {
							 | 
						||
| 
								 | 
							
								        echo $openapi->toJson();
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        echo $openapi->toYaml();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    echo "\n";
							 | 
						||
| 
								 | 
							
								} else {
							 | 
						||
| 
								 | 
							
								    if (is_dir($options['output'])) {
							 | 
						||
| 
								 | 
							
								        $options['output'] .= '/openapi.yaml';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    $openapi->saveAs($options['output'], $options['format']);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								exit($exit);
							 |