Started the rewrite which will be compatible with Swagger v2.0 Spec
This commit is contained in:
commit
6c7cc4ab5a
|
@ -0,0 +1,2 @@
|
|||
/vendor
|
||||
/composer.lock
|
|
@ -0,0 +1,16 @@
|
|||
language: php
|
||||
|
||||
before_script:
|
||||
- composer install
|
||||
|
||||
php:
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- hhvm
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: hhvm
|
||||
|
||||
script: phpunit
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace PetstoreSimple;
|
||||
|
||||
class PetResource {
|
||||
|
||||
/**
|
||||
* @SWG\Get(
|
||||
* path="/pets",
|
||||
* description="Returns all pets from the system that the user has access to"
|
||||
* )
|
||||
*/
|
||||
function findPets() {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,28 @@
|
|||
# swagger-php
|
||||
|
||||
- [![2.* Development Build Status](https://api.travis-ci.org/zircote/swagger-php.png?branch=2.0)](http://travis-ci.org/zircote/swagger-php) `2.*@dev`
|
||||
|
||||
Generate interactive [Swagger](http://swagger.io) documentation for your RESTful API using [doctrine annotations](http://doctrine-common.readthedocs.org/en/latest/reference/annotations.html).
|
||||
|
||||
## Installation (with [Composer](http://composer.org))
|
||||
|
||||
```sh
|
||||
composer require zircote/swagger-php
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Compatible with the Swagger 2.0 specification
|
||||
-
|
||||
|
||||
## More on Swagger
|
||||
|
||||
* http://swagger.io/
|
||||
* https://github.com/swagger-api/swagger-spec/
|
||||
|
||||
## Contributing
|
||||
|
||||
Please feel free to submit [Github Issues](https://github.com/zircote/swagger-php/issues) or pull requests.
|
||||
The documentation website resides within the `gh-pages` branch.
|
||||
|
||||
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/zircote/swagger-php/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "zircote/swagger-php",
|
||||
"type": "library",
|
||||
"license": "Apache2",
|
||||
"bin": ["bin/swagger"],
|
||||
"description": "Swagger-PHP - Generate interactive documentation for your RESTful API using phpdoc annotations",
|
||||
"keywords": ["json", "api", "service discovery"],
|
||||
"homepage": "https://github.com/zircote/swagger-php/",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Robert Allen",
|
||||
"email": "zircote@gmail.com",
|
||||
"homepage": "http://www.zircote.com"
|
||||
},
|
||||
{
|
||||
"name": "Bob Fanger",
|
||||
"email": "bfanger@gmail.com",
|
||||
"homepage": "http://bfanger.nl"
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
"bin-dir": "bin"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4.0",
|
||||
"doctrine/annotations": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Swagger\\": "src"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<phpunit bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="true">
|
||||
<testsuite name="Swagger-PHP TestSuite">
|
||||
<directory>tests/</directory>
|
||||
</testsuite>
|
||||
</phpunit>
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace Swagger\Annotations;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
class Get extends Operation {
|
||||
public $method = 'get';
|
||||
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace Swagger\Annotations;
|
||||
|
||||
/**
|
||||
* Base class for the @SWG\Get(), @SWG\Post(), @SWG\Put(), @SWG\Delete()
|
||||
*
|
||||
* A Swagger "Operation Object": https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#operationObject
|
||||
*/
|
||||
abstract class Operation extends SwaggerAnnotation {
|
||||
|
||||
/**
|
||||
*
|
||||
* @var string key in the Swagger "Paths Object" for this operation
|
||||
*/
|
||||
public $path;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var string key in the Swagger "Path Item Object" for this operation
|
||||
*/
|
||||
public $method;
|
||||
|
||||
/**
|
||||
* @var [string] A list of tags for API documentation control. Tags can be used for logical grouping of operations by resources or any other qualifier.
|
||||
*/
|
||||
public $tags;
|
||||
|
||||
/**
|
||||
* @var string A short summary of what the operation does. For maximum readability in the swagger-ui, this field SHOULD be less than 120 characters.
|
||||
*/
|
||||
public $summary;
|
||||
|
||||
/**
|
||||
* @var string A verbose explanation of the operation behavior. GFM syntax can be used for rich text representation.
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* @var External Documentation Object Additional external documentation for this operation.
|
||||
*/
|
||||
public $externalDocs;
|
||||
|
||||
/**
|
||||
* @var string A friendly name for the operation. The id MUST be unique among all operations described in the API. Tools and libraries MAY use the operation id to uniquely identify an operation.
|
||||
*/
|
||||
public $operationId;
|
||||
|
||||
/**
|
||||
* @var [string] A list of MIME types the operation can consume. This overrides the [consumes](#swaggerConsumes) definition at the Swagger Object. An empty value MAY be used to clear the global definition. Value MUST be as described under Mime Types.
|
||||
*/
|
||||
public $consumes;
|
||||
|
||||
/**
|
||||
* @var [string] A list of MIME types the operation can produce. This overrides the [produces](#swaggerProduces) definition at the Swagger Object. An empty value MAY be used to clear the global definition. Value MUST be as described under Mime Types.
|
||||
*/
|
||||
public $produces;
|
||||
|
||||
/**
|
||||
* @var [Parameter Object | Reference Object] A list of parameters that are applicable for this operation. If a parameter is already defined at the Path Item, the new definition will override it, but can never remove it. The list MUST NOT include duplicated parameters. A unique parameter is defined by a combination of a name and location. The list can use the Reference Object to link to parameters that are defined at the Swagger Object's parameters. There can be one "body" parameter at most.
|
||||
*/
|
||||
public $parameters;
|
||||
|
||||
/**
|
||||
* @var Responses Object Required. The list of possible responses as they are returned from executing this operation.
|
||||
*/
|
||||
public $responses;
|
||||
|
||||
/**
|
||||
* @var [string] The transfer protocol for the operation. Values MUST be from the list: "http", "https", "ws", "wss". The value overrides the Swagger Object schemes definition.
|
||||
*/
|
||||
public $schemes;
|
||||
|
||||
/**
|
||||
* @var boolean Declares this operation to be deprecated. Usage of the declared operation should be refrained. Default value is false.
|
||||
*/
|
||||
public $deprecated;
|
||||
|
||||
/**
|
||||
* @var [Security Requirement Object] A declaration of which security schemes are applied for this operation. The list of values describes alternative security schemes that can be used (that is, there is a logical OR between the security requirements). This definition overrides any declared top-level security. To remove a top-level security declaration, an empty array can be used.
|
||||
*/
|
||||
public $security;
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace Swagger\Annotations;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
abstract class SwaggerAnnotation extends Annotation {
|
||||
//put your code here
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
<?php
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace Swagger;
|
||||
|
||||
/**
|
||||
* Context
|
||||
*
|
||||
* The context in which the annotation is parsed.
|
||||
* It includes useful metadata which the Processors can use to augment the annotations.
|
||||
*
|
||||
* Context hierarchy:
|
||||
* - parseContext
|
||||
* |- docBlockContext
|
||||
* |- classContext
|
||||
* |- docBlockContext
|
||||
* |- propertyContext
|
||||
* |- methodContext
|
||||
*
|
||||
* @property string $comment The PHP DocComment
|
||||
* @property string $filename
|
||||
* @property int $line
|
||||
* @property int $character
|
||||
*
|
||||
* @property string $class
|
||||
* @property string $extends
|
||||
* @property string $method
|
||||
* @property string $property
|
||||
*/
|
||||
class Context {
|
||||
|
||||
/**
|
||||
* Prototypical inheritance for properties.
|
||||
* @var Context
|
||||
*/
|
||||
private $_parent;
|
||||
|
||||
/**
|
||||
* @param array $properties new properties for this context.
|
||||
* @param Context $parent The parent context
|
||||
*/
|
||||
public function __construct($properties = array(), $parent = null) {
|
||||
foreach ($properties as $property => $value) {
|
||||
$this->$property = $value;
|
||||
}
|
||||
$this->_parent = $parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a property is set directly on this context and not its parent context.
|
||||
*
|
||||
* @param string $type Example: $c->is('method') or $c->is('class')
|
||||
* @return bool
|
||||
*/
|
||||
public function is($type) {
|
||||
return property_exists($this, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the context containing the specified property.
|
||||
*
|
||||
* @param string $property
|
||||
* @return boolean|\Swagger\Context
|
||||
*/
|
||||
public function with($property) {
|
||||
if (property_exists($this, $property)) {
|
||||
return $this;
|
||||
}
|
||||
if ($this->_parent) {
|
||||
return $this->_parent->with($property);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Swagger\Context
|
||||
*/
|
||||
public function getRootContext() {
|
||||
if ($this->_parent) {
|
||||
return $this->_parent->getRootContext();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export location for debugging.
|
||||
*
|
||||
* @return string Example: "file1.php on line 12"
|
||||
*/
|
||||
public function getDebugLocation() {
|
||||
$location = '';
|
||||
if ($this->class && ($this->method || $this->property)) {
|
||||
$location .= $this->class;
|
||||
if ($this->method) {
|
||||
$location .= ($this->static ? '::' : '->') . $this->method . '()';
|
||||
} elseif ($this->property) {
|
||||
$location .= ($this->static ? '::$' : '->') . $this->property;
|
||||
}
|
||||
}
|
||||
if ($this->filename) {
|
||||
if ($location !== '') {
|
||||
$location .= ' in ';
|
||||
}
|
||||
$location .= $this->filename;
|
||||
}
|
||||
if ($this->line) {
|
||||
if ($location !== '') {
|
||||
$location .= ' on';
|
||||
}
|
||||
$location .= ' line ' . $this->line;
|
||||
if ($this->character) {
|
||||
$location .= ':' . $this->character;
|
||||
}
|
||||
}
|
||||
return $location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse the context tree to get the property value.
|
||||
*
|
||||
* @param string $property
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($property) {
|
||||
if ($this->_parent) {
|
||||
return $this->_parent->$property;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
return $this->getDebugLocation();
|
||||
}
|
||||
|
||||
public function __debugInfo() {
|
||||
return $this->getDebugLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function extractDescription() {
|
||||
$lines = explode("\n", $this->comment);
|
||||
unset($lines[0]);
|
||||
$description = '';
|
||||
foreach ($lines as $line) {
|
||||
$line = ltrim($line, "\t *");
|
||||
if (substr($line, 0, 1) === '@') {
|
||||
break;
|
||||
}
|
||||
$description .= $line . ' ';
|
||||
}
|
||||
$description = trim($description);
|
||||
if ($description === '') {
|
||||
return null;
|
||||
}
|
||||
if (stripos($description, 'license')) {
|
||||
return null; // Don't use the GPL/MIT license text as the description.
|
||||
}
|
||||
return $description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Context based on the debug_backtrace
|
||||
* @param int $index
|
||||
* @return \Swagger\Context
|
||||
*/
|
||||
static public function detect($index = 0) {
|
||||
$context = new Context();
|
||||
$backtrace = debug_backtrace();
|
||||
$call = $backtrace[$index];
|
||||
if (isset($call['function'])) {
|
||||
$context->method = $call['function'];
|
||||
if (isset($call['type']) && $call['type'] === '::') {
|
||||
$context->static = true;
|
||||
}
|
||||
}
|
||||
if (isset($call['class'])) {
|
||||
$context->class = $call['class'];
|
||||
}
|
||||
if (isset($call['file'])) {
|
||||
$context->filename = $call['file'];
|
||||
}
|
||||
if (isset($call['line'])) {
|
||||
$context->line = $call['line'];
|
||||
}
|
||||
return $context;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace Swagger;
|
||||
|
||||
class Logger {
|
||||
|
||||
/**
|
||||
* Singleton
|
||||
* @var Logger
|
||||
*/
|
||||
public static $instance;
|
||||
|
||||
/**
|
||||
* @var Closure
|
||||
*/
|
||||
public $log;
|
||||
|
||||
protected function __construct() {
|
||||
/**
|
||||
* @param \Exception|string $entry
|
||||
* @param int Error type
|
||||
*/
|
||||
$this->log = function ($entry, $type) {
|
||||
if ($entry instanceof \Exception) {
|
||||
$entry = $entry->getMessage();
|
||||
}
|
||||
trigger_error($entry, $type);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Logger
|
||||
*/
|
||||
public static function getInstance() {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new Logger();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a Swagger warning.
|
||||
* @param \Exception|string $entry
|
||||
*/
|
||||
public static function warning($entry) {
|
||||
call_user_func(self::getInstance()->log, $entry, E_USER_WARNING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a Swagger notice.
|
||||
* @param \Exception|string $entry
|
||||
*/
|
||||
public static function notice($entry) {
|
||||
call_user_func(self::getInstance()->log, $entry, E_USER_NOTICE);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,345 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace Swagger;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationRegistry;
|
||||
use Doctrine\Common\Annotations\DocParser;
|
||||
use Annotations\SwaggerAnnotation;
|
||||
|
||||
//
|
||||
AnnotationRegistry::registerLoader(function ($class) {
|
||||
if (substr($class, 0, 20) === 'Swagger\\Annotations\\') {
|
||||
return class_exists($class);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @category Swagger
|
||||
* @package Swagger
|
||||
*/
|
||||
class Parser {
|
||||
|
||||
/**
|
||||
* Allows Annotation classes to know the context of the annotation that is being processed.
|
||||
* @var Context
|
||||
*/
|
||||
public static $context;
|
||||
|
||||
/**
|
||||
* @var DocParser
|
||||
*/
|
||||
private $docParser;
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
*/
|
||||
public function __construct($filename = null) {
|
||||
if ($filename !== null) {
|
||||
$this->parseFile($filename);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and process all doc-comments from a file.
|
||||
*
|
||||
* @param string $filename Path to a php file.
|
||||
* @return SwaggerAnnotation[]
|
||||
*/
|
||||
public function parseFile($filename) {
|
||||
$tokens = token_get_all(file_get_contents($filename));
|
||||
return $this->parseTokens($tokens, new Context(array('filename' => $filename)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and process all doc-comments from the contents.
|
||||
*
|
||||
* @param string $contents PHP code.
|
||||
* @param Context $context The original location of the contents.
|
||||
* @return SwaggerAnnotation[]
|
||||
*/
|
||||
public function parseContents($contents, $context) {
|
||||
$tokens = token_get_all($contents);
|
||||
return $this->parseTokens($tokens, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared implementation for parseFile() & parseContents().
|
||||
*
|
||||
* @param array $tokens The result of a token_get_all()
|
||||
* @return SwaggerAnnotation[]
|
||||
*/
|
||||
protected function parseTokens($tokens, $parseContext) {
|
||||
$this->docParser = new DocParser();
|
||||
$this->docParser->setIgnoreNotImportedAnnotations(true);
|
||||
|
||||
$annotations = array();
|
||||
reset($tokens);
|
||||
$token = '';
|
||||
$namespace = '';
|
||||
$imports = array(
|
||||
'swg' => 'Swagger\Annotations' // Use @SWG\* for swagger annotations (unless overwrittemn by a use statement)
|
||||
);
|
||||
$this->docParser->setImports($imports);
|
||||
$uses = array();
|
||||
$classContext = $parseContext; // Use the parseContext until a classContext is created.
|
||||
$comment = false;
|
||||
$line = 0;
|
||||
while ($token !== false) {
|
||||
$previousToken = $token;
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
if (is_array($token) === false) { // Ignore tokens like "{", "}", etc
|
||||
continue;
|
||||
}
|
||||
if ($token[0] === T_DOC_COMMENT) {
|
||||
if ($comment) { // 2 Doc-comments in succession?
|
||||
$this->parseContext(new Context(array('comment' => $comment, 'line' => $line), $classContext), $annotations);
|
||||
}
|
||||
$comment = $token[1];
|
||||
$line = $token[2];
|
||||
continue;
|
||||
}
|
||||
if ($token[0] === T_ABSTRACT) {
|
||||
$token = $this->nextToken($tokens, $parseContext); // Skip "abstract" keyword
|
||||
}
|
||||
if ($token[0] === T_CLASS) { // Doc-comment before a class?
|
||||
if (is_array($previousToken) && $previousToken[0] === T_DOUBLE_COLON) {
|
||||
//php 5.5 class name resolution (i.e. ClassName::class)
|
||||
continue;
|
||||
}
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
$classContext = new Context(array(
|
||||
'class' => $namespace ? $namespace . '\\' . $token[1] : $token[1],
|
||||
'line' => $token[2],
|
||||
), $parseContext);
|
||||
// @todo detect end-of-class and reset $class
|
||||
$extends = null;
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
if ($token[0] === T_EXTENDS) {
|
||||
$classContext->extends = $this->prefixNamespace($namespace, $this->parseNamespace($tokens, $token, $parseContext), $uses);
|
||||
}
|
||||
if ($comment) {
|
||||
$classContext->comment = $comment;
|
||||
$classContext->line = $line;
|
||||
$this->parseContext($classContext, $annotations);
|
||||
$comment = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($comment) {
|
||||
if ($token[0] == T_STATIC) {
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
if ($token[0] === T_VARIABLE) { // static property
|
||||
$this->parseContext(new Context(array(
|
||||
'property' => substr($token[1], 1),
|
||||
'static' => true,
|
||||
'comment' => $comment,
|
||||
'line' => $line
|
||||
), $classContext), $annotations);
|
||||
$comment = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (in_array($token[0], array(T_PRIVATE, T_PROTECTED, T_PUBLIC, T_VAR))) { // Scope
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
if ($token[0] == T_STATIC) {
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
}
|
||||
if ($token[0] === T_VARIABLE) { // instance property
|
||||
$this->parseContext(new Context(array(
|
||||
'property' => substr($token[1], 1),
|
||||
'comment' => $comment,
|
||||
'line' => $line
|
||||
), $classContext), $annotations);
|
||||
$comment = false;
|
||||
} elseif ($token[0] === T_FUNCTION) {
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
if ($token[0] === T_STRING) {
|
||||
$this->parseContext(new Context(array(
|
||||
'method' => $token[1],
|
||||
'comment' => $comment,
|
||||
'line' => $line
|
||||
), $classContext), $annotations);
|
||||
$comment = false;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} elseif ($token[0] === T_FUNCTION) {
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
if ($token[0] === T_STRING) {
|
||||
$this->parseContext(new Context(array(
|
||||
'method' => $token[1],
|
||||
'comment' => $comment,
|
||||
'line' => $line
|
||||
), $classContext), $annotations);
|
||||
$comment = false;
|
||||
}
|
||||
}
|
||||
if (in_array($token[0], array(T_NAMESPACE, T_USE)) === false) { // Skip "use" & "namespace" to prevent "never imported" warnings)
|
||||
// Not a doc-comment for a class, property or method?
|
||||
$this->parseContext(new Context(array('comment' => $comment, 'line' => $line), $classContext), $annotations);
|
||||
$comment = false;
|
||||
}
|
||||
}
|
||||
if ($token[0] === T_NAMESPACE) {
|
||||
$namespace = $this->parseNamespace($tokens, $token, $parseContext);
|
||||
continue;
|
||||
}
|
||||
if ($token[0] === T_USE) {
|
||||
$statements = $this->parseUseStatement($tokens, $token, $parseContext);
|
||||
foreach ($statements as $alias => $target) {
|
||||
if ($target[0] === '\\') {
|
||||
$target = substr($target, 1);
|
||||
}
|
||||
$imports[$alias] = $target;
|
||||
}
|
||||
$this->docParser->setImports($imports);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($comment) { // File ends with a T_DOC_COMMENT
|
||||
$this->parseContext(new Context(array('comment' => $comment, 'line' => $line), $classContext), $annotations);
|
||||
}
|
||||
return $annotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* The next non-whitespace, non-comment token.
|
||||
*
|
||||
* @param array $tokens
|
||||
* @param Context $context
|
||||
* @return string|array The next token (or false)
|
||||
*/
|
||||
private function nextToken(&$tokens, $context) {
|
||||
$token = next($tokens);
|
||||
if ($token[0] === T_WHITESPACE) {
|
||||
return $this->nextToken($tokens, $context);
|
||||
}
|
||||
if ($token[0] === T_COMMENT) {
|
||||
$pos = strpos($token[1], '@SWG\\');
|
||||
if ($pos) {
|
||||
$commentContext = new Context(array('line' => $token[2]), $context);
|
||||
Logger::notice('Annotations are only parsed inside `/**` DocBlocks, skipping ' . $commentContext);
|
||||
}
|
||||
return $this->nextToken($tokens, $context);
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
|
||||
private function parseNamespace(&$tokens, &$token, $parseContext) {
|
||||
$namespace = '';
|
||||
while ($token !== false) {
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
if ($token[0] !== T_STRING && $token[0] !== T_NS_SEPARATOR) {
|
||||
break;
|
||||
}
|
||||
$namespace .= $token[1];
|
||||
}
|
||||
return $namespace;
|
||||
}
|
||||
|
||||
private function parseUseStatement(&$tokens, &$token, $parseContext) {
|
||||
$class = '';
|
||||
$alias = '';
|
||||
$statements = array();
|
||||
$explicitAlias = false;
|
||||
while ($token !== false) {
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
$isNameToken = $token[0] === T_STRING || $token[0] === T_NS_SEPARATOR;
|
||||
if (!$explicitAlias && $isNameToken) {
|
||||
$class .= $token[1];
|
||||
$alias = $token[1];
|
||||
} else if ($explicitAlias && $isNameToken) {
|
||||
$alias .= $token[1];
|
||||
} else if ($token[0] === T_AS) {
|
||||
$explicitAlias = true;
|
||||
$alias = '';
|
||||
} else if ($token === ',') {
|
||||
$statements[strtolower($alias)] = $class;
|
||||
$class = '';
|
||||
$alias = '';
|
||||
$explicitAlias = false;
|
||||
} else if ($token === ';') {
|
||||
$statements[strtolower($alias)] = $class;
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $statements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use doctrine to parse the comment block and add detected annotations to the $annotations array.
|
||||
*
|
||||
* @param Context $context
|
||||
* @param SwaggerAnnotation[] $annotations
|
||||
*/
|
||||
protected function parseContext($context, &$annotations) {
|
||||
try {
|
||||
self::$context = $context;
|
||||
$result = $this->docParser->parse($context->comment, $context);
|
||||
self::$context = null;
|
||||
} catch (\Exception $e) {
|
||||
self::$context = null;
|
||||
if (preg_match('/^(.+) at position ([0-9]+) in ' . preg_quote($context, '/') . '\.$/', $e->getMessage(), $matches)) {
|
||||
$errorMessage = $matches[1];
|
||||
$errorPos = $matches[2];
|
||||
$atPos = strpos($context->comment, '@');
|
||||
$context->line += substr_count($context->comment, "\n", 0, $atPos + $errorPos);
|
||||
$lines = explode("\n", substr($context->comment, $atPos, $errorPos));
|
||||
$context->character = strlen(array_pop($lines)) + 1; // position starts at 0 character starts at 1
|
||||
Logger::warning(new \Exception($errorMessage . ' in ' . $context, $e->getCode(), $e));
|
||||
} else {
|
||||
Logger::warning($e);
|
||||
}
|
||||
return array();
|
||||
}
|
||||
foreach ($result as $annotation) {
|
||||
if ($annotation instanceof Annotations\SwaggerAnnotation) {
|
||||
$annotations[] = $annotation;
|
||||
}
|
||||
}
|
||||
return $annotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the full classname.
|
||||
*
|
||||
* @param string $namespace Active namespace
|
||||
* @param string $class The class name
|
||||
* @param array $uses Active USE statements.
|
||||
* @return string
|
||||
*/
|
||||
private function prefixNamespace($namespace, $class, $uses = array()) {
|
||||
$pos = strpos($class, '\\');
|
||||
if ($pos !== false) {
|
||||
if ($pos === 0) {
|
||||
// Fully qualified name (\Foo\Bar)
|
||||
return substr($class, 1);
|
||||
}
|
||||
// Qualified name (Foo\Bar)
|
||||
foreach ($uses as $alias => $aliasedNamespace) {
|
||||
$alias .= '\\';
|
||||
if (strtolower(substr($class, 0, strlen($alias))) === $alias) {
|
||||
// Aliased namespace (use \Long\Namespace as Foo)
|
||||
return $aliasedNamespace . substr($class, strlen($alias) - 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Unqualified name (Foo)
|
||||
$alias = strtolower($class);
|
||||
if (isset($uses[$alias])) { // Is an alias?
|
||||
return $uses[$alias];
|
||||
}
|
||||
}
|
||||
if ($namespace == '') {
|
||||
return $class;
|
||||
}
|
||||
return $namespace . '\\' . $class;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
namespace SwaggerTests;
|
||||
|
||||
class ParserTest extends \PHPUnit_Framework_TestCase{
|
||||
|
||||
function test_parseFile() {
|
||||
$parser = new \Swagger\Parser();
|
||||
$annotations = $parser->parseFile(__DIR__.'/../Examples/petstore-simple/pets.php');
|
||||
$this->assertInternalType('array', $annotations);
|
||||
$this->assertInstanceOf('Swagger\Annotations\SwaggerAnnotation', $annotations[0]);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue