├── .gitignore ├── .styleci.yml ├── .travis.yml ├── README.md ├── components └── Context.php ├── composer.json ├── composer.lock ├── controllers └── BuildController.php ├── helpers └── DocBlockHelper.php ├── models ├── ControllerDoc.php ├── ControllerParser.php ├── Doc.php ├── FieldDoc.php ├── ModelDoc.php ├── ModelParser.php ├── ObjectParser.php └── Parser.php ├── phpunit.xml.dist ├── tags └── QueryTag.php ├── tests ├── bootstrap.php ├── config │ └── main.php ├── controllers │ ├── BrandController.php │ ├── NewBrandController.php │ └── ProductController.php ├── models │ ├── Brand.php │ ├── NewSpecialOffer.php │ ├── Product.php │ └── SpecialOffer.php ├── modules │ └── api │ │ ├── Module.php │ │ ├── controllers │ │ ├── CountryController.php │ │ └── HelpController.php │ │ └── models │ │ └── Country.php └── unit │ ├── components │ └── ContextTest.php │ └── models │ ├── ControllerParserTest.php │ └── ModelParserTest.php └── views └── slate.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | vendor 3 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: psr2 2 | enabled: 3 | - concat_with_spaces -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.3 5 | 6 | matrix: 7 | fast_finish: true 8 | 9 | install: 10 | - travis_retry composer self-update 11 | - travis_retry composer install --prefer-dist --no-interaction 12 | - export PATH="$HOME/.composer/vendor/bin:$PATH" 13 | - travis_retry composer global require "phpunit/phpunit=4.8.6" 14 | 15 | script: 16 | - vendor/bin/phpunit 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yii2 Rest Controller Documentator 2 | 3 | [![Build Status](https://travis-ci.org/pahanini/yii2-rest-doc.svg?branch=master)](https://travis-ci.org/pahanini/yii2-rest-doc) 4 | [![StyleCI](https://styleci.io/repos/33711749/shield?branch=master)](https://styleci.io/repos/33711749) 5 | [![Latest Stable Version](https://poser.pugx.org/pahanini/yii2-rest-doc/v/stable)](https://packagist.org/packages/pahanini/yii2-rest-doc) 6 | [![Total Downloads](https://poser.pugx.org/pahanini/yii2-rest-doc/downloads)](https://packagist.org/packages/pahanini/yii2-rest-doc) 7 | [![Latest Unstable Version](https://poser.pugx.org/pahanini/yii2-rest-doc/v/unstable)](https://packagist.org/packages/pahanini/yii2-rest-doc) 8 | [![License](https://poser.pugx.org/pahanini/yii2-rest-doc/license)](https://packagist.org/packages/pahanini/yii2-rest-doc) 9 | 10 | 11 | ## About 12 | 13 | Create precise documentation to your Yii2 API [REST](http://www.yiiframework.com/doc-2.0/guide-rest-quick-start.html) 14 | controllers. Library parses your code and generates objects with meta data that could be used with any template 15 | engine to generate great API docs. 16 | 17 | You do not need to edit documentation when you change you code. Just rebuild you docs with this tool. 18 | 19 | ## Install 20 | 21 | - Add `"pahanini/yii2-rest-doc": "*"` to required section of your composer.json 22 | - Add to your console application config 23 | 24 | ``` php 25 | 26 | 'controllerMap' => [ 27 | 'build-rest-doc' => [ 28 | 'sourceDirs' => [ 29 | '@frontend\controllers\rest', // <-- path to your API controllers 30 | ], 31 | 'template' => '//restdoc/restdoc.twig', 32 | 'class' => '\pahanini\restdoc\controllers\BuildController', 33 | 'sortProperty' => 'shortDescription', // <-- default value (how controllers will be sorted) 34 | 'targetFile' => 'path/to/nice-documentation.html' 35 | ], 36 | ] 37 | ``` 38 | 39 | ## Template example (twig) 40 | 41 | ``` html 42 | {% for controller in controllers %} 43 |

{{ controller.shortDescription }}

44 |

{{ controller.longDescription }}

45 | {% if controller.hasLabel('authenticated') %} 46 |
Require login and password!
47 | {% endif %} 48 |

List of supported actions: 49 |

54 |

55 |

Get params available for index action:

56 | 63 |

64 |

Model fields:

65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | {% for item in controller.model.fields %} 73 | 74 | 75 | 76 | 77 | 78 | 79 | {% endfor %} 80 |
NameTypeDescriptionCan be updated?
{{ item.name }}{{ item.type }}{{ item.description }}{{ item.isInScenario('api-update') ? 'yes' : 'no' }}
81 | {% endfor %} 82 | ``` 83 | 84 | ## Data available in template 85 | 86 | List of data automatically extracted from code: 87 | 88 | - controller name 89 | - action's of each controller 90 | - model fields 91 | - extra fields 92 | - model rules (TBD) 93 | 94 | List of special tags: 95 | 96 | - short and long description of controller 97 | - query tags 98 | 99 | Inheritance is also supported. Use `@inherited` or `@inheritdoc` tags. 100 | 101 | ### Controller 102 | 103 | - `@restdoc-ignore` - skip controller. 104 | - `@restdoc-label name` - mark controller with label. Label name available via `controller.hasLabel('labelName')` in template 105 | - `@restdoc-query name=false Name of part of name to find users` - query params with description. 106 | 107 | ### Model 108 | 109 | To describe model's fields you can use two approaches. 110 | 111 | #### Link to property tag. 112 | 113 | If you already have phpDocumentator `@property` tag you can use it to describe API field. 114 | Model's doc block example: 115 | 116 | ```php 117 | 118 | /** 119 | * My model. 120 | * 121 | * @property string $title Model's title 122 | */ 123 | ``` 124 | 125 | * `@restdoc-link $title` - use `$title` property to describe `$title` api field 126 | * `@restdoc-link title $model_title` - use `$title` property to describe `$model_title` api field 127 | 128 | ### Separate field description 129 | 130 | If you do not have `@property` tag or API field is not directly connected with property use `@restdoc-field` tag. 131 | 132 | Example: 133 | 134 | ```php 135 | 136 | /** 137 | * @restdoc-field int $id ID 138 | * @restdoc-field string $title Model's title 139 | */ 140 | ``` 141 | 142 | ### Extra fields 143 | 144 | Use @restdoc-extraField and @restdoc-extraLink for extra fields. 145 | 146 | ### Sort fields 147 | 148 | Use @restdoc-sortField to sort field according to your code. 149 | 150 | ### Skip fields 151 | 152 | Use @restdoc-ignore to skip field. 153 | 154 | ```php 155 | 156 | /** 157 | * @restdoc-ignore $hidden 158 | */ 159 | ``` 160 | 161 | ## Integrate With Slate 162 | 163 | [Slate](https://github.com/tripit/slate) is probably one of the best tools to generate nice API. So you can 164 | use this tool to create index.md file for slate. You can use on afterAction event to automatically start slate. 165 | 166 | Example: 167 | 168 | ``` php 169 | 'controllerMap' => [ 170 | 'build-rest-doc' => [ 171 | 'sourceDirs' => [ 172 | '@frontend\controllers\rest', 173 | ], 174 | 'template' => '//restdoc/restdoc.twig', 175 | 'class' => '\pahanini\restdoc\controllers\BuildController', 176 | 'targetFile' => 'path/to/slate/index.md', 177 | 'on afterAction' => function() { exec("bundle exec middleman build") } 178 | ], 179 | ] 180 | ``` 181 | 182 | ## Rationale 183 | 184 | Creating of Yii2 controllers is an easy task, but supporting of documentation in actual state is often boring 185 | and tough challenge. Using automatic tool like [phpDocumentator](https://github.com/phpDocumentor/phpDocumentor2) 186 | or [swagger](http://swagger.io/) makes life easier but its still require to describe all models fields 187 | and rules using tags or comments. 188 | 189 | In other hand Yii2 controllers and models keep a lot of information about internal structure like actions, 190 | field names, scenarios for update and insert operations. This package extracts such an information from 191 | REST controllers and models and using this data along with phpdocumentator tags automatically generates 192 | index.md for [slate](https://github.com/tripit/slate) or any other documentation file. 193 | 194 | -------------------------------------------------------------------------------- /components/Context.php: -------------------------------------------------------------------------------- 1 | ControllerParser::className(), 35 | 'reflection' => new \ReflectionClass($className), 36 | 'objectConfig' => $objectConfig, 37 | ] 38 | ); 39 | $doc = new ControllerDoc(); 40 | if ($parser->parse($doc) === false) { 41 | Yii::error($parser->error, 'restdoc'); 42 | } else { 43 | $doc->prepare(); 44 | $this->_controllers[$doc->path] = $doc; 45 | } 46 | } 47 | 48 | 49 | /** 50 | * Adds module to context. 51 | * 52 | * @param string $module Module class, e.g. \frontend\modules\my\Module 53 | */ 54 | public function addModule($module) 55 | { 56 | /* @var $module Module */ 57 | $module = Yii::createObject($module, ['_id', null]); 58 | $module->setInstance($module); 59 | $this->addDirs($module->getControllerPath()); 60 | 61 | foreach ($module->controllerMap as $value) { 62 | if (is_array($value)) { 63 | $class = $value['class']; 64 | unset($value['class']); 65 | $objectConfig = $value; 66 | } else { 67 | $class = $value; 68 | $objectConfig = null; 69 | } 70 | $this->addControllerDoc($class, $objectConfig); 71 | } 72 | } 73 | 74 | /** 75 | * Adds array of modules to context. 76 | * 77 | * @param string[] $modules Array with names of modules. 78 | */ 79 | public function addModules($modules) 80 | { 81 | $modules = is_array($modules) ? $modules : [$modules]; 82 | foreach ($modules as $module) { 83 | $this->addModule($module); 84 | } 85 | } 86 | 87 | /** 88 | * Adds one or more directories with controllers to context. 89 | * 90 | * @param string[] $dirs 91 | */ 92 | public function addDirs($dirs) 93 | { 94 | $dirs = is_array($dirs) ? $dirs : [$dirs]; 95 | foreach ($dirs as $dir) { 96 | $files = FileHelper::findFiles(Yii::getAlias($dir), [ 97 | 'only' => ['*Controller.php'] 98 | ]); 99 | foreach ($files as $file) { 100 | $this->addFile($file); 101 | } 102 | } 103 | } 104 | 105 | /** 106 | * Adds file to context. 107 | * 108 | * @param string $fileName 109 | */ 110 | public function addFile($fileName) 111 | { 112 | $reflector = new FileReflector($fileName); 113 | $reflector->process(); 114 | 115 | $classes = $reflector->getClasses(); 116 | 117 | if (count($classes) !== 1) { 118 | throw new InvalidArgumentException("File $fileName includes more then one class"); 119 | } 120 | 121 | $this->addControllerDoc($classes[0]->getName()); 122 | } 123 | 124 | /** 125 | * @param $property 126 | */ 127 | public function sortControllers($property) 128 | { 129 | uasort($this->_controllers, function ($a, $b) use ($property) { 130 | return strcmp($a->$property, $b->$property); 131 | }); 132 | } 133 | 134 | /** 135 | * Returns list of controller documents. 136 | * @return \pahanini\restdoc\models\ControllerDoc[] 137 | */ 138 | public function getControllers() 139 | { 140 | return $this->_controllers; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pahanini/yii2-rest-doc", 3 | "description": "A Yii2 tool to create slate index.md for you REST controllers.", 4 | "keywords": ["yii2", "rest", "api", "doc", "slate"], 5 | "homepage": "https://github.com/pahanini/yii2-rest-to-slate", 6 | "type": "yii2-extension", 7 | "license": "BSD-3-Clause", 8 | "authors": [ 9 | { 10 | "name": "Pavel Tetyaev", 11 | "email": "pahanini@gmail.com" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=7.1.0", 16 | "ext-curl": "*", 17 | "yiisoft/yii2": "*", 18 | "phpdocumentor/reflection": ">=3.0.0", 19 | "phpdocumentor/reflection-docblock": "~2.0" 20 | }, 21 | "require-dev": { 22 | "phpunit/phpunit" : ">=6.0" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "pahanini\\restdoc\\": "" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "58c263763c94f387dc8e26cc840640f8", 8 | "packages": [ 9 | { 10 | "name": "bower-asset/inputmask", 11 | "version": "3.3.11", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/RobinHerbots/Inputmask.git", 15 | "reference": "5e670ad62f50c738388d4dcec78d2888505ad77b" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/RobinHerbots/Inputmask/zipball/5e670ad62f50c738388d4dcec78d2888505ad77b", 20 | "reference": "5e670ad62f50c738388d4dcec78d2888505ad77b", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "bower-asset/jquery": ">=1.7" 25 | }, 26 | "type": "bower-asset-library", 27 | "extra": { 28 | "bower-asset-main": [ 29 | "./dist/inputmask/inputmask.js", 30 | "./dist/inputmask/inputmask.extensions.js", 31 | "./dist/inputmask/inputmask.date.extensions.js", 32 | "./dist/inputmask/inputmask.numeric.extensions.js", 33 | "./dist/inputmask/inputmask.phone.extensions.js", 34 | "./dist/inputmask/jquery.inputmask.js", 35 | "./dist/inputmask/global/document.js", 36 | "./dist/inputmask/global/window.js", 37 | "./dist/inputmask/phone-codes/phone.js", 38 | "./dist/inputmask/phone-codes/phone-be.js", 39 | "./dist/inputmask/phone-codes/phone-nl.js", 40 | "./dist/inputmask/phone-codes/phone-ru.js", 41 | "./dist/inputmask/phone-codes/phone-uk.js", 42 | "./dist/inputmask/dependencyLibs/inputmask.dependencyLib.jqlite.js", 43 | "./dist/inputmask/dependencyLibs/inputmask.dependencyLib.jquery.js", 44 | "./dist/inputmask/dependencyLibs/inputmask.dependencyLib.js", 45 | "./dist/inputmask/bindings/inputmask.binding.js" 46 | ], 47 | "bower-asset-ignore": [ 48 | "**/*", 49 | "!dist/*", 50 | "!dist/inputmask/*", 51 | "!dist/min/*", 52 | "!dist/min/inputmask/*" 53 | ] 54 | }, 55 | "license": [ 56 | "http://opensource.org/licenses/mit-license.php" 57 | ], 58 | "description": "Inputmask is a javascript library which creates an input mask. Inputmask can run against vanilla javascript, jQuery and jqlite.", 59 | "keywords": [ 60 | "form", 61 | "input", 62 | "inputmask", 63 | "jquery", 64 | "mask", 65 | "plugins" 66 | ], 67 | "time": "2017-11-21T11:46:23+00:00" 68 | }, 69 | { 70 | "name": "bower-asset/jquery", 71 | "version": "3.4.1", 72 | "source": { 73 | "type": "git", 74 | "url": "https://github.com/jquery/jquery-dist.git", 75 | "reference": "15bc73803f76bc53b654b9fdbbbc096f56d7c03d" 76 | }, 77 | "dist": { 78 | "type": "zip", 79 | "url": "https://api.github.com/repos/jquery/jquery-dist/zipball/15bc73803f76bc53b654b9fdbbbc096f56d7c03d", 80 | "reference": "15bc73803f76bc53b654b9fdbbbc096f56d7c03d", 81 | "shasum": "" 82 | }, 83 | "type": "bower-asset-library", 84 | "extra": { 85 | "bower-asset-main": "dist/jquery.js", 86 | "bower-asset-ignore": [ 87 | "package.json" 88 | ] 89 | }, 90 | "license": [ 91 | "MIT" 92 | ], 93 | "keywords": [ 94 | "browser", 95 | "javascript", 96 | "jquery", 97 | "library" 98 | ], 99 | "time": "2019-05-01T21:19:28+00:00" 100 | }, 101 | { 102 | "name": "bower-asset/punycode", 103 | "version": "v1.3.2", 104 | "source": { 105 | "type": "git", 106 | "url": "https://github.com/bestiejs/punycode.js.git", 107 | "reference": "38c8d3131a82567bfef18da09f7f4db68c84f8a3" 108 | }, 109 | "dist": { 110 | "type": "zip", 111 | "url": "https://api.github.com/repos/bestiejs/punycode.js/zipball/38c8d3131a82567bfef18da09f7f4db68c84f8a3", 112 | "reference": "38c8d3131a82567bfef18da09f7f4db68c84f8a3", 113 | "shasum": "" 114 | }, 115 | "type": "bower-asset-library", 116 | "extra": { 117 | "bower-asset-main": "punycode.js", 118 | "bower-asset-ignore": [ 119 | "coverage", 120 | "tests", 121 | ".*", 122 | "component.json", 123 | "Gruntfile.js", 124 | "node_modules", 125 | "package.json" 126 | ] 127 | }, 128 | "time": "2014-10-22T12:02:42+00:00" 129 | }, 130 | { 131 | "name": "bower-asset/yii2-pjax", 132 | "version": "2.0.7.1", 133 | "source": { 134 | "type": "git", 135 | "url": "https://github.com/yiisoft/jquery-pjax.git", 136 | "reference": "aef7b953107264f00234902a3880eb50dafc48be" 137 | }, 138 | "dist": { 139 | "type": "zip", 140 | "url": "https://api.github.com/repos/yiisoft/jquery-pjax/zipball/aef7b953107264f00234902a3880eb50dafc48be", 141 | "reference": "aef7b953107264f00234902a3880eb50dafc48be", 142 | "shasum": "" 143 | }, 144 | "require": { 145 | "bower-asset/jquery": ">=1.8" 146 | }, 147 | "type": "bower-asset-library", 148 | "extra": { 149 | "bower-asset-main": "./jquery.pjax.js", 150 | "bower-asset-ignore": [ 151 | ".travis.yml", 152 | "Gemfile", 153 | "Gemfile.lock", 154 | "CONTRIBUTING.md", 155 | "vendor/", 156 | "script/", 157 | "test/" 158 | ] 159 | }, 160 | "license": [ 161 | "MIT" 162 | ], 163 | "time": "2017-10-12T10:11:14+00:00" 164 | }, 165 | { 166 | "name": "cebe/markdown", 167 | "version": "1.2.1", 168 | "source": { 169 | "type": "git", 170 | "url": "https://github.com/cebe/markdown.git", 171 | "reference": "9bac5e971dd391e2802dca5400bbeacbaea9eb86" 172 | }, 173 | "dist": { 174 | "type": "zip", 175 | "url": "https://api.github.com/repos/cebe/markdown/zipball/9bac5e971dd391e2802dca5400bbeacbaea9eb86", 176 | "reference": "9bac5e971dd391e2802dca5400bbeacbaea9eb86", 177 | "shasum": "" 178 | }, 179 | "require": { 180 | "lib-pcre": "*", 181 | "php": ">=5.4.0" 182 | }, 183 | "require-dev": { 184 | "cebe/indent": "*", 185 | "facebook/xhprof": "*@dev", 186 | "phpunit/phpunit": "4.1.*" 187 | }, 188 | "bin": [ 189 | "bin/markdown" 190 | ], 191 | "type": "library", 192 | "extra": { 193 | "branch-alias": { 194 | "dev-master": "1.2.x-dev" 195 | } 196 | }, 197 | "autoload": { 198 | "psr-4": { 199 | "cebe\\markdown\\": "" 200 | } 201 | }, 202 | "notification-url": "https://packagist.org/downloads/", 203 | "license": [ 204 | "MIT" 205 | ], 206 | "authors": [ 207 | { 208 | "name": "Carsten Brandt", 209 | "email": "mail@cebe.cc", 210 | "homepage": "http://cebe.cc/", 211 | "role": "Creator" 212 | } 213 | ], 214 | "description": "A super fast, highly extensible markdown parser for PHP", 215 | "homepage": "https://github.com/cebe/markdown#readme", 216 | "keywords": [ 217 | "extensible", 218 | "fast", 219 | "gfm", 220 | "markdown", 221 | "markdown-extra" 222 | ], 223 | "time": "2018-03-26T11:24:36+00:00" 224 | }, 225 | { 226 | "name": "ezyang/htmlpurifier", 227 | "version": "v4.12.0", 228 | "source": { 229 | "type": "git", 230 | "url": "https://github.com/ezyang/htmlpurifier.git", 231 | "reference": "a617e55bc62a87eec73bd456d146d134ad716f03" 232 | }, 233 | "dist": { 234 | "type": "zip", 235 | "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/a617e55bc62a87eec73bd456d146d134ad716f03", 236 | "reference": "a617e55bc62a87eec73bd456d146d134ad716f03", 237 | "shasum": "" 238 | }, 239 | "require": { 240 | "php": ">=5.2" 241 | }, 242 | "require-dev": { 243 | "simpletest/simpletest": "dev-master#72de02a7b80c6bb8864ef9bf66d41d2f58f826bd" 244 | }, 245 | "type": "library", 246 | "autoload": { 247 | "psr-0": { 248 | "HTMLPurifier": "library/" 249 | }, 250 | "files": [ 251 | "library/HTMLPurifier.composer.php" 252 | ] 253 | }, 254 | "notification-url": "https://packagist.org/downloads/", 255 | "license": [ 256 | "LGPL-2.1-or-later" 257 | ], 258 | "authors": [ 259 | { 260 | "name": "Edward Z. Yang", 261 | "email": "admin@htmlpurifier.org", 262 | "homepage": "http://ezyang.com" 263 | } 264 | ], 265 | "description": "Standards compliant HTML filter written in PHP", 266 | "homepage": "http://htmlpurifier.org/", 267 | "keywords": [ 268 | "html" 269 | ], 270 | "time": "2019-10-28T03:44:26+00:00" 271 | }, 272 | { 273 | "name": "nikic/php-parser", 274 | "version": "v1.4.1", 275 | "source": { 276 | "type": "git", 277 | "url": "https://github.com/nikic/PHP-Parser.git", 278 | "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51" 279 | }, 280 | "dist": { 281 | "type": "zip", 282 | "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", 283 | "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", 284 | "shasum": "" 285 | }, 286 | "require": { 287 | "ext-tokenizer": "*", 288 | "php": ">=5.3" 289 | }, 290 | "type": "library", 291 | "extra": { 292 | "branch-alias": { 293 | "dev-master": "1.4-dev" 294 | } 295 | }, 296 | "autoload": { 297 | "files": [ 298 | "lib/bootstrap.php" 299 | ] 300 | }, 301 | "notification-url": "https://packagist.org/downloads/", 302 | "license": [ 303 | "BSD-3-Clause" 304 | ], 305 | "authors": [ 306 | { 307 | "name": "Nikita Popov" 308 | } 309 | ], 310 | "description": "A PHP parser written in PHP", 311 | "keywords": [ 312 | "parser", 313 | "php" 314 | ], 315 | "time": "2015-09-19T14:15:08+00:00" 316 | }, 317 | { 318 | "name": "phpdocumentor/reflection", 319 | "version": "3.0.1", 320 | "source": { 321 | "type": "git", 322 | "url": "https://github.com/phpDocumentor/Reflection.git", 323 | "reference": "793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d" 324 | }, 325 | "dist": { 326 | "type": "zip", 327 | "url": "https://api.github.com/repos/phpDocumentor/Reflection/zipball/793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d", 328 | "reference": "793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d", 329 | "shasum": "" 330 | }, 331 | "require": { 332 | "nikic/php-parser": "^1.0", 333 | "php": ">=5.3.3", 334 | "phpdocumentor/reflection-docblock": "~2.0", 335 | "psr/log": "~1.0" 336 | }, 337 | "require-dev": { 338 | "behat/behat": "~2.4", 339 | "mockery/mockery": "~0.8", 340 | "phpunit/phpunit": "~4.0" 341 | }, 342 | "type": "library", 343 | "extra": { 344 | "branch-alias": { 345 | "dev-master": "1.0.x-dev" 346 | } 347 | }, 348 | "autoload": { 349 | "psr-0": { 350 | "phpDocumentor": [ 351 | "src/", 352 | "tests/unit/", 353 | "tests/mocks/" 354 | ] 355 | } 356 | }, 357 | "notification-url": "https://packagist.org/downloads/", 358 | "license": [ 359 | "MIT" 360 | ], 361 | "description": "Reflection library to do Static Analysis for PHP Projects", 362 | "homepage": "http://www.phpdoc.org", 363 | "keywords": [ 364 | "phpDocumentor", 365 | "phpdoc", 366 | "reflection", 367 | "static analysis" 368 | ], 369 | "time": "2016-05-21T08:42:32+00:00" 370 | }, 371 | { 372 | "name": "phpdocumentor/reflection-docblock", 373 | "version": "2.0.5", 374 | "source": { 375 | "type": "git", 376 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 377 | "reference": "e6a969a640b00d8daa3c66518b0405fb41ae0c4b" 378 | }, 379 | "dist": { 380 | "type": "zip", 381 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e6a969a640b00d8daa3c66518b0405fb41ae0c4b", 382 | "reference": "e6a969a640b00d8daa3c66518b0405fb41ae0c4b", 383 | "shasum": "" 384 | }, 385 | "require": { 386 | "php": ">=5.3.3" 387 | }, 388 | "require-dev": { 389 | "phpunit/phpunit": "~4.0" 390 | }, 391 | "suggest": { 392 | "dflydev/markdown": "~1.0", 393 | "erusev/parsedown": "~1.0" 394 | }, 395 | "type": "library", 396 | "extra": { 397 | "branch-alias": { 398 | "dev-master": "2.0.x-dev" 399 | } 400 | }, 401 | "autoload": { 402 | "psr-0": { 403 | "phpDocumentor": [ 404 | "src/" 405 | ] 406 | } 407 | }, 408 | "notification-url": "https://packagist.org/downloads/", 409 | "license": [ 410 | "MIT" 411 | ], 412 | "authors": [ 413 | { 414 | "name": "Mike van Riel", 415 | "email": "mike.vanriel@naenius.com" 416 | } 417 | ], 418 | "time": "2016-01-25T08:17:30+00:00" 419 | }, 420 | { 421 | "name": "psr/log", 422 | "version": "1.1.3", 423 | "source": { 424 | "type": "git", 425 | "url": "https://github.com/php-fig/log.git", 426 | "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" 427 | }, 428 | "dist": { 429 | "type": "zip", 430 | "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", 431 | "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", 432 | "shasum": "" 433 | }, 434 | "require": { 435 | "php": ">=5.3.0" 436 | }, 437 | "type": "library", 438 | "extra": { 439 | "branch-alias": { 440 | "dev-master": "1.1.x-dev" 441 | } 442 | }, 443 | "autoload": { 444 | "psr-4": { 445 | "Psr\\Log\\": "Psr/Log/" 446 | } 447 | }, 448 | "notification-url": "https://packagist.org/downloads/", 449 | "license": [ 450 | "MIT" 451 | ], 452 | "authors": [ 453 | { 454 | "name": "PHP-FIG", 455 | "homepage": "http://www.php-fig.org/" 456 | } 457 | ], 458 | "description": "Common interface for logging libraries", 459 | "homepage": "https://github.com/php-fig/log", 460 | "keywords": [ 461 | "log", 462 | "psr", 463 | "psr-3" 464 | ], 465 | "time": "2020-03-23T09:12:05+00:00" 466 | }, 467 | { 468 | "name": "yiisoft/yii2", 469 | "version": "2.0.35", 470 | "source": { 471 | "type": "git", 472 | "url": "https://github.com/yiisoft/yii2-framework.git", 473 | "reference": "d42809e4969cdc0adb97197ba32774b3e4cd9e8e" 474 | }, 475 | "dist": { 476 | "type": "zip", 477 | "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/d42809e4969cdc0adb97197ba32774b3e4cd9e8e", 478 | "reference": "d42809e4969cdc0adb97197ba32774b3e4cd9e8e", 479 | "shasum": "" 480 | }, 481 | "require": { 482 | "bower-asset/inputmask": "~3.2.2 | ~3.3.5", 483 | "bower-asset/jquery": "3.4.*@stable | 3.3.*@stable | 3.2.*@stable | 3.1.*@stable | 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable", 484 | "bower-asset/punycode": "1.3.*", 485 | "bower-asset/yii2-pjax": "~2.0.1", 486 | "cebe/markdown": "~1.0.0 | ~1.1.0 | ~1.2.0", 487 | "ext-ctype": "*", 488 | "ext-mbstring": "*", 489 | "ezyang/htmlpurifier": "~4.6", 490 | "lib-pcre": "*", 491 | "php": ">=5.4.0", 492 | "yiisoft/yii2-composer": "~2.0.4" 493 | }, 494 | "bin": [ 495 | "yii" 496 | ], 497 | "type": "library", 498 | "extra": { 499 | "branch-alias": { 500 | "dev-master": "2.0.x-dev" 501 | } 502 | }, 503 | "autoload": { 504 | "psr-4": { 505 | "yii\\": "" 506 | } 507 | }, 508 | "notification-url": "https://packagist.org/downloads/", 509 | "license": [ 510 | "BSD-3-Clause" 511 | ], 512 | "authors": [ 513 | { 514 | "name": "Qiang Xue", 515 | "email": "qiang.xue@gmail.com", 516 | "homepage": "http://www.yiiframework.com/", 517 | "role": "Founder and project lead" 518 | }, 519 | { 520 | "name": "Alexander Makarov", 521 | "email": "sam@rmcreative.ru", 522 | "homepage": "http://rmcreative.ru/", 523 | "role": "Core framework development" 524 | }, 525 | { 526 | "name": "Maurizio Domba", 527 | "homepage": "http://mdomba.info/", 528 | "role": "Core framework development" 529 | }, 530 | { 531 | "name": "Carsten Brandt", 532 | "email": "mail@cebe.cc", 533 | "homepage": "http://cebe.cc/", 534 | "role": "Core framework development" 535 | }, 536 | { 537 | "name": "Timur Ruziev", 538 | "email": "resurtm@gmail.com", 539 | "homepage": "http://resurtm.com/", 540 | "role": "Core framework development" 541 | }, 542 | { 543 | "name": "Paul Klimov", 544 | "email": "klimov.paul@gmail.com", 545 | "role": "Core framework development" 546 | }, 547 | { 548 | "name": "Dmitry Naumenko", 549 | "email": "d.naumenko.a@gmail.com", 550 | "role": "Core framework development" 551 | }, 552 | { 553 | "name": "Boudewijn Vahrmeijer", 554 | "email": "info@dynasource.eu", 555 | "homepage": "http://dynasource.eu", 556 | "role": "Core framework development" 557 | } 558 | ], 559 | "description": "Yii PHP Framework Version 2", 560 | "homepage": "http://www.yiiframework.com/", 561 | "keywords": [ 562 | "framework", 563 | "yii2" 564 | ], 565 | "funding": [ 566 | { 567 | "url": "https://github.com/yiisoft", 568 | "type": "github" 569 | }, 570 | { 571 | "url": "https://opencollective.com/yiisoft", 572 | "type": "open_collective" 573 | }, 574 | { 575 | "url": "https://tidelift.com/funding/github/packagist/yiisoft/yii2", 576 | "type": "tidelift" 577 | } 578 | ], 579 | "time": "2020-05-02T11:11:31+00:00" 580 | }, 581 | { 582 | "name": "yiisoft/yii2-composer", 583 | "version": "2.0.9", 584 | "source": { 585 | "type": "git", 586 | "url": "https://github.com/yiisoft/yii2-composer.git", 587 | "reference": "d191176c4f8372e397a9e3df27360dca6a70efaa" 588 | }, 589 | "dist": { 590 | "type": "zip", 591 | "url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/d191176c4f8372e397a9e3df27360dca6a70efaa", 592 | "reference": "d191176c4f8372e397a9e3df27360dca6a70efaa", 593 | "shasum": "" 594 | }, 595 | "require": { 596 | "composer-plugin-api": "^1.0 | ^2.0" 597 | }, 598 | "require-dev": { 599 | "composer/composer": "^1.0 | ^2.0@dev", 600 | "phpunit/phpunit": "<7" 601 | }, 602 | "type": "composer-plugin", 603 | "extra": { 604 | "class": "yii\\composer\\Plugin", 605 | "branch-alias": { 606 | "dev-master": "2.0.x-dev" 607 | } 608 | }, 609 | "autoload": { 610 | "psr-4": { 611 | "yii\\composer\\": "" 612 | } 613 | }, 614 | "notification-url": "https://packagist.org/downloads/", 615 | "license": [ 616 | "BSD-3-Clause" 617 | ], 618 | "authors": [ 619 | { 620 | "name": "Qiang Xue", 621 | "email": "qiang.xue@gmail.com" 622 | }, 623 | { 624 | "name": "Carsten Brandt", 625 | "email": "mail@cebe.cc" 626 | } 627 | ], 628 | "description": "The composer plugin for Yii extension installer", 629 | "keywords": [ 630 | "composer", 631 | "extension installer", 632 | "yii2" 633 | ], 634 | "funding": [ 635 | { 636 | "url": "https://github.com/yiisoft", 637 | "type": "github" 638 | }, 639 | { 640 | "url": "https://opencollective.com/yiisoft", 641 | "type": "open_collective" 642 | }, 643 | { 644 | "url": "https://tidelift.com/funding/github/packagist/yiisoft/yii2-composer", 645 | "type": "tidelift" 646 | } 647 | ], 648 | "time": "2020-04-20T18:47:46+00:00" 649 | } 650 | ], 651 | "packages-dev": [ 652 | { 653 | "name": "doctrine/instantiator", 654 | "version": "1.3.0", 655 | "source": { 656 | "type": "git", 657 | "url": "https://github.com/doctrine/instantiator.git", 658 | "reference": "ae466f726242e637cebdd526a7d991b9433bacf1" 659 | }, 660 | "dist": { 661 | "type": "zip", 662 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1", 663 | "reference": "ae466f726242e637cebdd526a7d991b9433bacf1", 664 | "shasum": "" 665 | }, 666 | "require": { 667 | "php": "^7.1" 668 | }, 669 | "require-dev": { 670 | "doctrine/coding-standard": "^6.0", 671 | "ext-pdo": "*", 672 | "ext-phar": "*", 673 | "phpbench/phpbench": "^0.13", 674 | "phpstan/phpstan-phpunit": "^0.11", 675 | "phpstan/phpstan-shim": "^0.11", 676 | "phpunit/phpunit": "^7.0" 677 | }, 678 | "type": "library", 679 | "extra": { 680 | "branch-alias": { 681 | "dev-master": "1.2.x-dev" 682 | } 683 | }, 684 | "autoload": { 685 | "psr-4": { 686 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 687 | } 688 | }, 689 | "notification-url": "https://packagist.org/downloads/", 690 | "license": [ 691 | "MIT" 692 | ], 693 | "authors": [ 694 | { 695 | "name": "Marco Pivetta", 696 | "email": "ocramius@gmail.com", 697 | "homepage": "http://ocramius.github.com/" 698 | } 699 | ], 700 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 701 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html", 702 | "keywords": [ 703 | "constructor", 704 | "instantiate" 705 | ], 706 | "time": "2019-10-21T16:45:58+00:00" 707 | }, 708 | { 709 | "name": "myclabs/deep-copy", 710 | "version": "1.9.5", 711 | "source": { 712 | "type": "git", 713 | "url": "https://github.com/myclabs/DeepCopy.git", 714 | "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef" 715 | }, 716 | "dist": { 717 | "type": "zip", 718 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef", 719 | "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef", 720 | "shasum": "" 721 | }, 722 | "require": { 723 | "php": "^7.1" 724 | }, 725 | "replace": { 726 | "myclabs/deep-copy": "self.version" 727 | }, 728 | "require-dev": { 729 | "doctrine/collections": "^1.0", 730 | "doctrine/common": "^2.6", 731 | "phpunit/phpunit": "^7.1" 732 | }, 733 | "type": "library", 734 | "autoload": { 735 | "psr-4": { 736 | "DeepCopy\\": "src/DeepCopy/" 737 | }, 738 | "files": [ 739 | "src/DeepCopy/deep_copy.php" 740 | ] 741 | }, 742 | "notification-url": "https://packagist.org/downloads/", 743 | "license": [ 744 | "MIT" 745 | ], 746 | "description": "Create deep copies (clones) of your objects", 747 | "keywords": [ 748 | "clone", 749 | "copy", 750 | "duplicate", 751 | "object", 752 | "object graph" 753 | ], 754 | "time": "2020-01-17T21:11:47+00:00" 755 | }, 756 | { 757 | "name": "phar-io/manifest", 758 | "version": "1.0.3", 759 | "source": { 760 | "type": "git", 761 | "url": "https://github.com/phar-io/manifest.git", 762 | "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" 763 | }, 764 | "dist": { 765 | "type": "zip", 766 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", 767 | "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", 768 | "shasum": "" 769 | }, 770 | "require": { 771 | "ext-dom": "*", 772 | "ext-phar": "*", 773 | "phar-io/version": "^2.0", 774 | "php": "^5.6 || ^7.0" 775 | }, 776 | "type": "library", 777 | "extra": { 778 | "branch-alias": { 779 | "dev-master": "1.0.x-dev" 780 | } 781 | }, 782 | "autoload": { 783 | "classmap": [ 784 | "src/" 785 | ] 786 | }, 787 | "notification-url": "https://packagist.org/downloads/", 788 | "license": [ 789 | "BSD-3-Clause" 790 | ], 791 | "authors": [ 792 | { 793 | "name": "Arne Blankerts", 794 | "email": "arne@blankerts.de", 795 | "role": "Developer" 796 | }, 797 | { 798 | "name": "Sebastian Heuer", 799 | "email": "sebastian@phpeople.de", 800 | "role": "Developer" 801 | }, 802 | { 803 | "name": "Sebastian Bergmann", 804 | "email": "sebastian@phpunit.de", 805 | "role": "Developer" 806 | } 807 | ], 808 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 809 | "time": "2018-07-08T19:23:20+00:00" 810 | }, 811 | { 812 | "name": "phar-io/version", 813 | "version": "2.0.1", 814 | "source": { 815 | "type": "git", 816 | "url": "https://github.com/phar-io/version.git", 817 | "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" 818 | }, 819 | "dist": { 820 | "type": "zip", 821 | "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", 822 | "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", 823 | "shasum": "" 824 | }, 825 | "require": { 826 | "php": "^5.6 || ^7.0" 827 | }, 828 | "type": "library", 829 | "autoload": { 830 | "classmap": [ 831 | "src/" 832 | ] 833 | }, 834 | "notification-url": "https://packagist.org/downloads/", 835 | "license": [ 836 | "BSD-3-Clause" 837 | ], 838 | "authors": [ 839 | { 840 | "name": "Arne Blankerts", 841 | "email": "arne@blankerts.de", 842 | "role": "Developer" 843 | }, 844 | { 845 | "name": "Sebastian Heuer", 846 | "email": "sebastian@phpeople.de", 847 | "role": "Developer" 848 | }, 849 | { 850 | "name": "Sebastian Bergmann", 851 | "email": "sebastian@phpunit.de", 852 | "role": "Developer" 853 | } 854 | ], 855 | "description": "Library for handling version information and constraints", 856 | "time": "2018-07-08T19:19:57+00:00" 857 | }, 858 | { 859 | "name": "phpspec/prophecy", 860 | "version": "v1.10.3", 861 | "source": { 862 | "type": "git", 863 | "url": "https://github.com/phpspec/prophecy.git", 864 | "reference": "451c3cd1418cf640de218914901e51b064abb093" 865 | }, 866 | "dist": { 867 | "type": "zip", 868 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", 869 | "reference": "451c3cd1418cf640de218914901e51b064abb093", 870 | "shasum": "" 871 | }, 872 | "require": { 873 | "doctrine/instantiator": "^1.0.2", 874 | "php": "^5.3|^7.0", 875 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", 876 | "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", 877 | "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" 878 | }, 879 | "require-dev": { 880 | "phpspec/phpspec": "^2.5 || ^3.2", 881 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" 882 | }, 883 | "type": "library", 884 | "extra": { 885 | "branch-alias": { 886 | "dev-master": "1.10.x-dev" 887 | } 888 | }, 889 | "autoload": { 890 | "psr-4": { 891 | "Prophecy\\": "src/Prophecy" 892 | } 893 | }, 894 | "notification-url": "https://packagist.org/downloads/", 895 | "license": [ 896 | "MIT" 897 | ], 898 | "authors": [ 899 | { 900 | "name": "Konstantin Kudryashov", 901 | "email": "ever.zet@gmail.com", 902 | "homepage": "http://everzet.com" 903 | }, 904 | { 905 | "name": "Marcello Duarte", 906 | "email": "marcello.duarte@gmail.com" 907 | } 908 | ], 909 | "description": "Highly opinionated mocking framework for PHP 5.3+", 910 | "homepage": "https://github.com/phpspec/prophecy", 911 | "keywords": [ 912 | "Double", 913 | "Dummy", 914 | "fake", 915 | "mock", 916 | "spy", 917 | "stub" 918 | ], 919 | "time": "2020-03-05T15:02:03+00:00" 920 | }, 921 | { 922 | "name": "phpunit/php-code-coverage", 923 | "version": "8.0.2", 924 | "source": { 925 | "type": "git", 926 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 927 | "reference": "ca6647ffddd2add025ab3f21644a441d7c146cdc" 928 | }, 929 | "dist": { 930 | "type": "zip", 931 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca6647ffddd2add025ab3f21644a441d7c146cdc", 932 | "reference": "ca6647ffddd2add025ab3f21644a441d7c146cdc", 933 | "shasum": "" 934 | }, 935 | "require": { 936 | "ext-dom": "*", 937 | "ext-xmlwriter": "*", 938 | "php": "^7.3", 939 | "phpunit/php-file-iterator": "^3.0", 940 | "phpunit/php-text-template": "^2.0", 941 | "phpunit/php-token-stream": "^4.0", 942 | "sebastian/code-unit-reverse-lookup": "^2.0", 943 | "sebastian/environment": "^5.0", 944 | "sebastian/version": "^3.0", 945 | "theseer/tokenizer": "^1.1.3" 946 | }, 947 | "require-dev": { 948 | "phpunit/phpunit": "^9.0" 949 | }, 950 | "suggest": { 951 | "ext-pcov": "*", 952 | "ext-xdebug": "*" 953 | }, 954 | "type": "library", 955 | "extra": { 956 | "branch-alias": { 957 | "dev-master": "8.0-dev" 958 | } 959 | }, 960 | "autoload": { 961 | "classmap": [ 962 | "src/" 963 | ] 964 | }, 965 | "notification-url": "https://packagist.org/downloads/", 966 | "license": [ 967 | "BSD-3-Clause" 968 | ], 969 | "authors": [ 970 | { 971 | "name": "Sebastian Bergmann", 972 | "email": "sebastian@phpunit.de", 973 | "role": "lead" 974 | } 975 | ], 976 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 977 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 978 | "keywords": [ 979 | "coverage", 980 | "testing", 981 | "xunit" 982 | ], 983 | "funding": [ 984 | { 985 | "url": "https://github.com/sebastianbergmann", 986 | "type": "github" 987 | } 988 | ], 989 | "time": "2020-05-23T08:02:54+00:00" 990 | }, 991 | { 992 | "name": "phpunit/php-file-iterator", 993 | "version": "3.0.1", 994 | "source": { 995 | "type": "git", 996 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 997 | "reference": "4ac5b3e13df14829daa60a2eb4fdd2f2b7d33cf4" 998 | }, 999 | "dist": { 1000 | "type": "zip", 1001 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/4ac5b3e13df14829daa60a2eb4fdd2f2b7d33cf4", 1002 | "reference": "4ac5b3e13df14829daa60a2eb4fdd2f2b7d33cf4", 1003 | "shasum": "" 1004 | }, 1005 | "require": { 1006 | "php": "^7.3" 1007 | }, 1008 | "require-dev": { 1009 | "phpunit/phpunit": "^9.0" 1010 | }, 1011 | "type": "library", 1012 | "extra": { 1013 | "branch-alias": { 1014 | "dev-master": "3.0-dev" 1015 | } 1016 | }, 1017 | "autoload": { 1018 | "classmap": [ 1019 | "src/" 1020 | ] 1021 | }, 1022 | "notification-url": "https://packagist.org/downloads/", 1023 | "license": [ 1024 | "BSD-3-Clause" 1025 | ], 1026 | "authors": [ 1027 | { 1028 | "name": "Sebastian Bergmann", 1029 | "email": "sebastian@phpunit.de", 1030 | "role": "lead" 1031 | } 1032 | ], 1033 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 1034 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 1035 | "keywords": [ 1036 | "filesystem", 1037 | "iterator" 1038 | ], 1039 | "funding": [ 1040 | { 1041 | "url": "https://github.com/sebastianbergmann", 1042 | "type": "github" 1043 | } 1044 | ], 1045 | "time": "2020-04-18T05:02:12+00:00" 1046 | }, 1047 | { 1048 | "name": "phpunit/php-invoker", 1049 | "version": "3.0.0", 1050 | "source": { 1051 | "type": "git", 1052 | "url": "https://github.com/sebastianbergmann/php-invoker.git", 1053 | "reference": "7579d5a1ba7f3ac11c80004d205877911315ae7a" 1054 | }, 1055 | "dist": { 1056 | "type": "zip", 1057 | "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/7579d5a1ba7f3ac11c80004d205877911315ae7a", 1058 | "reference": "7579d5a1ba7f3ac11c80004d205877911315ae7a", 1059 | "shasum": "" 1060 | }, 1061 | "require": { 1062 | "php": "^7.3" 1063 | }, 1064 | "require-dev": { 1065 | "ext-pcntl": "*", 1066 | "phpunit/phpunit": "^9.0" 1067 | }, 1068 | "suggest": { 1069 | "ext-pcntl": "*" 1070 | }, 1071 | "type": "library", 1072 | "extra": { 1073 | "branch-alias": { 1074 | "dev-master": "3.0-dev" 1075 | } 1076 | }, 1077 | "autoload": { 1078 | "classmap": [ 1079 | "src/" 1080 | ] 1081 | }, 1082 | "notification-url": "https://packagist.org/downloads/", 1083 | "license": [ 1084 | "BSD-3-Clause" 1085 | ], 1086 | "authors": [ 1087 | { 1088 | "name": "Sebastian Bergmann", 1089 | "email": "sebastian@phpunit.de", 1090 | "role": "lead" 1091 | } 1092 | ], 1093 | "description": "Invoke callables with a timeout", 1094 | "homepage": "https://github.com/sebastianbergmann/php-invoker/", 1095 | "keywords": [ 1096 | "process" 1097 | ], 1098 | "time": "2020-02-07T06:06:11+00:00" 1099 | }, 1100 | { 1101 | "name": "phpunit/php-text-template", 1102 | "version": "2.0.0", 1103 | "source": { 1104 | "type": "git", 1105 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 1106 | "reference": "526dc996cc0ebdfa428cd2dfccd79b7b53fee346" 1107 | }, 1108 | "dist": { 1109 | "type": "zip", 1110 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/526dc996cc0ebdfa428cd2dfccd79b7b53fee346", 1111 | "reference": "526dc996cc0ebdfa428cd2dfccd79b7b53fee346", 1112 | "shasum": "" 1113 | }, 1114 | "require": { 1115 | "php": "^7.3" 1116 | }, 1117 | "type": "library", 1118 | "extra": { 1119 | "branch-alias": { 1120 | "dev-master": "2.0-dev" 1121 | } 1122 | }, 1123 | "autoload": { 1124 | "classmap": [ 1125 | "src/" 1126 | ] 1127 | }, 1128 | "notification-url": "https://packagist.org/downloads/", 1129 | "license": [ 1130 | "BSD-3-Clause" 1131 | ], 1132 | "authors": [ 1133 | { 1134 | "name": "Sebastian Bergmann", 1135 | "email": "sebastian@phpunit.de", 1136 | "role": "lead" 1137 | } 1138 | ], 1139 | "description": "Simple template engine.", 1140 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 1141 | "keywords": [ 1142 | "template" 1143 | ], 1144 | "time": "2020-02-01T07:43:44+00:00" 1145 | }, 1146 | { 1147 | "name": "phpunit/php-timer", 1148 | "version": "3.1.4", 1149 | "source": { 1150 | "type": "git", 1151 | "url": "https://github.com/sebastianbergmann/php-timer.git", 1152 | "reference": "dc9368fae6ef2ffa57eba80a7410bcef81df6258" 1153 | }, 1154 | "dist": { 1155 | "type": "zip", 1156 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/dc9368fae6ef2ffa57eba80a7410bcef81df6258", 1157 | "reference": "dc9368fae6ef2ffa57eba80a7410bcef81df6258", 1158 | "shasum": "" 1159 | }, 1160 | "require": { 1161 | "php": "^7.3" 1162 | }, 1163 | "require-dev": { 1164 | "phpunit/phpunit": "^9.0" 1165 | }, 1166 | "type": "library", 1167 | "extra": { 1168 | "branch-alias": { 1169 | "dev-master": "3.1-dev" 1170 | } 1171 | }, 1172 | "autoload": { 1173 | "classmap": [ 1174 | "src/" 1175 | ] 1176 | }, 1177 | "notification-url": "https://packagist.org/downloads/", 1178 | "license": [ 1179 | "BSD-3-Clause" 1180 | ], 1181 | "authors": [ 1182 | { 1183 | "name": "Sebastian Bergmann", 1184 | "email": "sebastian@phpunit.de", 1185 | "role": "lead" 1186 | } 1187 | ], 1188 | "description": "Utility class for timing", 1189 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 1190 | "keywords": [ 1191 | "timer" 1192 | ], 1193 | "funding": [ 1194 | { 1195 | "url": "https://github.com/sebastianbergmann", 1196 | "type": "github" 1197 | } 1198 | ], 1199 | "time": "2020-04-20T06:00:37+00:00" 1200 | }, 1201 | { 1202 | "name": "phpunit/php-token-stream", 1203 | "version": "4.0.1", 1204 | "source": { 1205 | "type": "git", 1206 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 1207 | "reference": "cdc0db5aed8fbfaf475fbd95bfd7bab83c7a779c" 1208 | }, 1209 | "dist": { 1210 | "type": "zip", 1211 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/cdc0db5aed8fbfaf475fbd95bfd7bab83c7a779c", 1212 | "reference": "cdc0db5aed8fbfaf475fbd95bfd7bab83c7a779c", 1213 | "shasum": "" 1214 | }, 1215 | "require": { 1216 | "ext-tokenizer": "*", 1217 | "php": "^7.3" 1218 | }, 1219 | "require-dev": { 1220 | "phpunit/phpunit": "^9.0" 1221 | }, 1222 | "type": "library", 1223 | "extra": { 1224 | "branch-alias": { 1225 | "dev-master": "4.0-dev" 1226 | } 1227 | }, 1228 | "autoload": { 1229 | "classmap": [ 1230 | "src/" 1231 | ] 1232 | }, 1233 | "notification-url": "https://packagist.org/downloads/", 1234 | "license": [ 1235 | "BSD-3-Clause" 1236 | ], 1237 | "authors": [ 1238 | { 1239 | "name": "Sebastian Bergmann", 1240 | "email": "sebastian@phpunit.de" 1241 | } 1242 | ], 1243 | "description": "Wrapper around PHP's tokenizer extension.", 1244 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 1245 | "keywords": [ 1246 | "tokenizer" 1247 | ], 1248 | "funding": [ 1249 | { 1250 | "url": "https://github.com/sebastianbergmann", 1251 | "type": "github" 1252 | } 1253 | ], 1254 | "time": "2020-05-06T09:56:31+00:00" 1255 | }, 1256 | { 1257 | "name": "phpunit/phpunit", 1258 | "version": "9.1.5", 1259 | "source": { 1260 | "type": "git", 1261 | "url": "https://github.com/sebastianbergmann/phpunit.git", 1262 | "reference": "1b570cd7edbe136055bf5f651857dc8af6b829d2" 1263 | }, 1264 | "dist": { 1265 | "type": "zip", 1266 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1b570cd7edbe136055bf5f651857dc8af6b829d2", 1267 | "reference": "1b570cd7edbe136055bf5f651857dc8af6b829d2", 1268 | "shasum": "" 1269 | }, 1270 | "require": { 1271 | "doctrine/instantiator": "^1.2.0", 1272 | "ext-dom": "*", 1273 | "ext-json": "*", 1274 | "ext-libxml": "*", 1275 | "ext-mbstring": "*", 1276 | "ext-xml": "*", 1277 | "ext-xmlwriter": "*", 1278 | "myclabs/deep-copy": "^1.9.1", 1279 | "phar-io/manifest": "^1.0.3", 1280 | "phar-io/version": "^2.0.1", 1281 | "php": "^7.3", 1282 | "phpspec/prophecy": "^1.8.1", 1283 | "phpunit/php-code-coverage": "^8.0.1", 1284 | "phpunit/php-file-iterator": "^3.0", 1285 | "phpunit/php-invoker": "^3.0", 1286 | "phpunit/php-text-template": "^2.0", 1287 | "phpunit/php-timer": "^3.1.4", 1288 | "sebastian/code-unit": "^1.0.2", 1289 | "sebastian/comparator": "^4.0", 1290 | "sebastian/diff": "^4.0", 1291 | "sebastian/environment": "^5.0.1", 1292 | "sebastian/exporter": "^4.0", 1293 | "sebastian/global-state": "^4.0", 1294 | "sebastian/object-enumerator": "^4.0", 1295 | "sebastian/resource-operations": "^3.0", 1296 | "sebastian/type": "^2.0", 1297 | "sebastian/version": "^3.0" 1298 | }, 1299 | "require-dev": { 1300 | "ext-pdo": "*", 1301 | "phpspec/prophecy-phpunit": "^2.0" 1302 | }, 1303 | "suggest": { 1304 | "ext-soap": "*", 1305 | "ext-xdebug": "*" 1306 | }, 1307 | "bin": [ 1308 | "phpunit" 1309 | ], 1310 | "type": "library", 1311 | "extra": { 1312 | "branch-alias": { 1313 | "dev-master": "9.1-dev" 1314 | } 1315 | }, 1316 | "autoload": { 1317 | "classmap": [ 1318 | "src/" 1319 | ], 1320 | "files": [ 1321 | "src/Framework/Assert/Functions.php" 1322 | ] 1323 | }, 1324 | "notification-url": "https://packagist.org/downloads/", 1325 | "license": [ 1326 | "BSD-3-Clause" 1327 | ], 1328 | "authors": [ 1329 | { 1330 | "name": "Sebastian Bergmann", 1331 | "email": "sebastian@phpunit.de", 1332 | "role": "lead" 1333 | } 1334 | ], 1335 | "description": "The PHP Unit Testing framework.", 1336 | "homepage": "https://phpunit.de/", 1337 | "keywords": [ 1338 | "phpunit", 1339 | "testing", 1340 | "xunit" 1341 | ], 1342 | "funding": [ 1343 | { 1344 | "url": "https://phpunit.de/donate.html", 1345 | "type": "custom" 1346 | }, 1347 | { 1348 | "url": "https://github.com/sebastianbergmann", 1349 | "type": "github" 1350 | } 1351 | ], 1352 | "time": "2020-05-22T13:54:05+00:00" 1353 | }, 1354 | { 1355 | "name": "sebastian/code-unit", 1356 | "version": "1.0.2", 1357 | "source": { 1358 | "type": "git", 1359 | "url": "https://github.com/sebastianbergmann/code-unit.git", 1360 | "reference": "ac958085bc19fcd1d36425c781ef4cbb5b06e2a5" 1361 | }, 1362 | "dist": { 1363 | "type": "zip", 1364 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/ac958085bc19fcd1d36425c781ef4cbb5b06e2a5", 1365 | "reference": "ac958085bc19fcd1d36425c781ef4cbb5b06e2a5", 1366 | "shasum": "" 1367 | }, 1368 | "require": { 1369 | "php": "^7.3" 1370 | }, 1371 | "require-dev": { 1372 | "phpunit/phpunit": "^9.0" 1373 | }, 1374 | "type": "library", 1375 | "extra": { 1376 | "branch-alias": { 1377 | "dev-master": "1.0-dev" 1378 | } 1379 | }, 1380 | "autoload": { 1381 | "classmap": [ 1382 | "src/" 1383 | ] 1384 | }, 1385 | "notification-url": "https://packagist.org/downloads/", 1386 | "license": [ 1387 | "BSD-3-Clause" 1388 | ], 1389 | "authors": [ 1390 | { 1391 | "name": "Sebastian Bergmann", 1392 | "email": "sebastian@phpunit.de", 1393 | "role": "lead" 1394 | } 1395 | ], 1396 | "description": "Collection of value objects that represent the PHP code units", 1397 | "homepage": "https://github.com/sebastianbergmann/code-unit", 1398 | "funding": [ 1399 | { 1400 | "url": "https://github.com/sebastianbergmann", 1401 | "type": "github" 1402 | } 1403 | ], 1404 | "time": "2020-04-30T05:58:10+00:00" 1405 | }, 1406 | { 1407 | "name": "sebastian/code-unit-reverse-lookup", 1408 | "version": "2.0.0", 1409 | "source": { 1410 | "type": "git", 1411 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 1412 | "reference": "5b5dbe0044085ac41df47e79d34911a15b96d82e" 1413 | }, 1414 | "dist": { 1415 | "type": "zip", 1416 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5b5dbe0044085ac41df47e79d34911a15b96d82e", 1417 | "reference": "5b5dbe0044085ac41df47e79d34911a15b96d82e", 1418 | "shasum": "" 1419 | }, 1420 | "require": { 1421 | "php": "^7.3" 1422 | }, 1423 | "require-dev": { 1424 | "phpunit/phpunit": "^9.0" 1425 | }, 1426 | "type": "library", 1427 | "extra": { 1428 | "branch-alias": { 1429 | "dev-master": "2.0-dev" 1430 | } 1431 | }, 1432 | "autoload": { 1433 | "classmap": [ 1434 | "src/" 1435 | ] 1436 | }, 1437 | "notification-url": "https://packagist.org/downloads/", 1438 | "license": [ 1439 | "BSD-3-Clause" 1440 | ], 1441 | "authors": [ 1442 | { 1443 | "name": "Sebastian Bergmann", 1444 | "email": "sebastian@phpunit.de" 1445 | } 1446 | ], 1447 | "description": "Looks up which function or method a line of code belongs to", 1448 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 1449 | "time": "2020-02-07T06:20:13+00:00" 1450 | }, 1451 | { 1452 | "name": "sebastian/comparator", 1453 | "version": "4.0.0", 1454 | "source": { 1455 | "type": "git", 1456 | "url": "https://github.com/sebastianbergmann/comparator.git", 1457 | "reference": "85b3435da967696ed618ff745f32be3ff4a2b8e8" 1458 | }, 1459 | "dist": { 1460 | "type": "zip", 1461 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85b3435da967696ed618ff745f32be3ff4a2b8e8", 1462 | "reference": "85b3435da967696ed618ff745f32be3ff4a2b8e8", 1463 | "shasum": "" 1464 | }, 1465 | "require": { 1466 | "php": "^7.3", 1467 | "sebastian/diff": "^4.0", 1468 | "sebastian/exporter": "^4.0" 1469 | }, 1470 | "require-dev": { 1471 | "phpunit/phpunit": "^9.0" 1472 | }, 1473 | "type": "library", 1474 | "extra": { 1475 | "branch-alias": { 1476 | "dev-master": "4.0-dev" 1477 | } 1478 | }, 1479 | "autoload": { 1480 | "classmap": [ 1481 | "src/" 1482 | ] 1483 | }, 1484 | "notification-url": "https://packagist.org/downloads/", 1485 | "license": [ 1486 | "BSD-3-Clause" 1487 | ], 1488 | "authors": [ 1489 | { 1490 | "name": "Sebastian Bergmann", 1491 | "email": "sebastian@phpunit.de" 1492 | }, 1493 | { 1494 | "name": "Jeff Welch", 1495 | "email": "whatthejeff@gmail.com" 1496 | }, 1497 | { 1498 | "name": "Volker Dusch", 1499 | "email": "github@wallbash.com" 1500 | }, 1501 | { 1502 | "name": "Bernhard Schussek", 1503 | "email": "bschussek@2bepublished.at" 1504 | } 1505 | ], 1506 | "description": "Provides the functionality to compare PHP values for equality", 1507 | "homepage": "https://github.com/sebastianbergmann/comparator", 1508 | "keywords": [ 1509 | "comparator", 1510 | "compare", 1511 | "equality" 1512 | ], 1513 | "time": "2020-02-07T06:08:51+00:00" 1514 | }, 1515 | { 1516 | "name": "sebastian/diff", 1517 | "version": "4.0.1", 1518 | "source": { 1519 | "type": "git", 1520 | "url": "https://github.com/sebastianbergmann/diff.git", 1521 | "reference": "3e523c576f29dacecff309f35e4cc5a5c168e78a" 1522 | }, 1523 | "dist": { 1524 | "type": "zip", 1525 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3e523c576f29dacecff309f35e4cc5a5c168e78a", 1526 | "reference": "3e523c576f29dacecff309f35e4cc5a5c168e78a", 1527 | "shasum": "" 1528 | }, 1529 | "require": { 1530 | "php": "^7.3" 1531 | }, 1532 | "require-dev": { 1533 | "phpunit/phpunit": "^9.0", 1534 | "symfony/process": "^4.2 || ^5" 1535 | }, 1536 | "type": "library", 1537 | "extra": { 1538 | "branch-alias": { 1539 | "dev-master": "4.0-dev" 1540 | } 1541 | }, 1542 | "autoload": { 1543 | "classmap": [ 1544 | "src/" 1545 | ] 1546 | }, 1547 | "notification-url": "https://packagist.org/downloads/", 1548 | "license": [ 1549 | "BSD-3-Clause" 1550 | ], 1551 | "authors": [ 1552 | { 1553 | "name": "Sebastian Bergmann", 1554 | "email": "sebastian@phpunit.de" 1555 | }, 1556 | { 1557 | "name": "Kore Nordmann", 1558 | "email": "mail@kore-nordmann.de" 1559 | } 1560 | ], 1561 | "description": "Diff implementation", 1562 | "homepage": "https://github.com/sebastianbergmann/diff", 1563 | "keywords": [ 1564 | "diff", 1565 | "udiff", 1566 | "unidiff", 1567 | "unified diff" 1568 | ], 1569 | "funding": [ 1570 | { 1571 | "url": "https://github.com/sebastianbergmann", 1572 | "type": "github" 1573 | } 1574 | ], 1575 | "time": "2020-05-08T05:01:12+00:00" 1576 | }, 1577 | { 1578 | "name": "sebastian/environment", 1579 | "version": "5.1.0", 1580 | "source": { 1581 | "type": "git", 1582 | "url": "https://github.com/sebastianbergmann/environment.git", 1583 | "reference": "c753f04d68cd489b6973cf9b4e505e191af3b05c" 1584 | }, 1585 | "dist": { 1586 | "type": "zip", 1587 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/c753f04d68cd489b6973cf9b4e505e191af3b05c", 1588 | "reference": "c753f04d68cd489b6973cf9b4e505e191af3b05c", 1589 | "shasum": "" 1590 | }, 1591 | "require": { 1592 | "php": "^7.3" 1593 | }, 1594 | "require-dev": { 1595 | "phpunit/phpunit": "^9.0" 1596 | }, 1597 | "suggest": { 1598 | "ext-posix": "*" 1599 | }, 1600 | "type": "library", 1601 | "extra": { 1602 | "branch-alias": { 1603 | "dev-master": "5.0-dev" 1604 | } 1605 | }, 1606 | "autoload": { 1607 | "classmap": [ 1608 | "src/" 1609 | ] 1610 | }, 1611 | "notification-url": "https://packagist.org/downloads/", 1612 | "license": [ 1613 | "BSD-3-Clause" 1614 | ], 1615 | "authors": [ 1616 | { 1617 | "name": "Sebastian Bergmann", 1618 | "email": "sebastian@phpunit.de" 1619 | } 1620 | ], 1621 | "description": "Provides functionality to handle HHVM/PHP environments", 1622 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1623 | "keywords": [ 1624 | "Xdebug", 1625 | "environment", 1626 | "hhvm" 1627 | ], 1628 | "funding": [ 1629 | { 1630 | "url": "https://github.com/sebastianbergmann", 1631 | "type": "github" 1632 | } 1633 | ], 1634 | "time": "2020-04-14T13:36:52+00:00" 1635 | }, 1636 | { 1637 | "name": "sebastian/exporter", 1638 | "version": "4.0.0", 1639 | "source": { 1640 | "type": "git", 1641 | "url": "https://github.com/sebastianbergmann/exporter.git", 1642 | "reference": "80c26562e964016538f832f305b2286e1ec29566" 1643 | }, 1644 | "dist": { 1645 | "type": "zip", 1646 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/80c26562e964016538f832f305b2286e1ec29566", 1647 | "reference": "80c26562e964016538f832f305b2286e1ec29566", 1648 | "shasum": "" 1649 | }, 1650 | "require": { 1651 | "php": "^7.3", 1652 | "sebastian/recursion-context": "^4.0" 1653 | }, 1654 | "require-dev": { 1655 | "ext-mbstring": "*", 1656 | "phpunit/phpunit": "^9.0" 1657 | }, 1658 | "type": "library", 1659 | "extra": { 1660 | "branch-alias": { 1661 | "dev-master": "4.0-dev" 1662 | } 1663 | }, 1664 | "autoload": { 1665 | "classmap": [ 1666 | "src/" 1667 | ] 1668 | }, 1669 | "notification-url": "https://packagist.org/downloads/", 1670 | "license": [ 1671 | "BSD-3-Clause" 1672 | ], 1673 | "authors": [ 1674 | { 1675 | "name": "Sebastian Bergmann", 1676 | "email": "sebastian@phpunit.de" 1677 | }, 1678 | { 1679 | "name": "Jeff Welch", 1680 | "email": "whatthejeff@gmail.com" 1681 | }, 1682 | { 1683 | "name": "Volker Dusch", 1684 | "email": "github@wallbash.com" 1685 | }, 1686 | { 1687 | "name": "Adam Harvey", 1688 | "email": "aharvey@php.net" 1689 | }, 1690 | { 1691 | "name": "Bernhard Schussek", 1692 | "email": "bschussek@gmail.com" 1693 | } 1694 | ], 1695 | "description": "Provides the functionality to export PHP variables for visualization", 1696 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1697 | "keywords": [ 1698 | "export", 1699 | "exporter" 1700 | ], 1701 | "time": "2020-02-07T06:10:52+00:00" 1702 | }, 1703 | { 1704 | "name": "sebastian/global-state", 1705 | "version": "4.0.0", 1706 | "source": { 1707 | "type": "git", 1708 | "url": "https://github.com/sebastianbergmann/global-state.git", 1709 | "reference": "bdb1e7c79e592b8c82cb1699be3c8743119b8a72" 1710 | }, 1711 | "dist": { 1712 | "type": "zip", 1713 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bdb1e7c79e592b8c82cb1699be3c8743119b8a72", 1714 | "reference": "bdb1e7c79e592b8c82cb1699be3c8743119b8a72", 1715 | "shasum": "" 1716 | }, 1717 | "require": { 1718 | "php": "^7.3", 1719 | "sebastian/object-reflector": "^2.0", 1720 | "sebastian/recursion-context": "^4.0" 1721 | }, 1722 | "require-dev": { 1723 | "ext-dom": "*", 1724 | "phpunit/phpunit": "^9.0" 1725 | }, 1726 | "suggest": { 1727 | "ext-uopz": "*" 1728 | }, 1729 | "type": "library", 1730 | "extra": { 1731 | "branch-alias": { 1732 | "dev-master": "4.0-dev" 1733 | } 1734 | }, 1735 | "autoload": { 1736 | "classmap": [ 1737 | "src/" 1738 | ] 1739 | }, 1740 | "notification-url": "https://packagist.org/downloads/", 1741 | "license": [ 1742 | "BSD-3-Clause" 1743 | ], 1744 | "authors": [ 1745 | { 1746 | "name": "Sebastian Bergmann", 1747 | "email": "sebastian@phpunit.de" 1748 | } 1749 | ], 1750 | "description": "Snapshotting of global state", 1751 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1752 | "keywords": [ 1753 | "global state" 1754 | ], 1755 | "time": "2020-02-07T06:11:37+00:00" 1756 | }, 1757 | { 1758 | "name": "sebastian/object-enumerator", 1759 | "version": "4.0.0", 1760 | "source": { 1761 | "type": "git", 1762 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1763 | "reference": "e67516b175550abad905dc952f43285957ef4363" 1764 | }, 1765 | "dist": { 1766 | "type": "zip", 1767 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67516b175550abad905dc952f43285957ef4363", 1768 | "reference": "e67516b175550abad905dc952f43285957ef4363", 1769 | "shasum": "" 1770 | }, 1771 | "require": { 1772 | "php": "^7.3", 1773 | "sebastian/object-reflector": "^2.0", 1774 | "sebastian/recursion-context": "^4.0" 1775 | }, 1776 | "require-dev": { 1777 | "phpunit/phpunit": "^9.0" 1778 | }, 1779 | "type": "library", 1780 | "extra": { 1781 | "branch-alias": { 1782 | "dev-master": "4.0-dev" 1783 | } 1784 | }, 1785 | "autoload": { 1786 | "classmap": [ 1787 | "src/" 1788 | ] 1789 | }, 1790 | "notification-url": "https://packagist.org/downloads/", 1791 | "license": [ 1792 | "BSD-3-Clause" 1793 | ], 1794 | "authors": [ 1795 | { 1796 | "name": "Sebastian Bergmann", 1797 | "email": "sebastian@phpunit.de" 1798 | } 1799 | ], 1800 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1801 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1802 | "time": "2020-02-07T06:12:23+00:00" 1803 | }, 1804 | { 1805 | "name": "sebastian/object-reflector", 1806 | "version": "2.0.0", 1807 | "source": { 1808 | "type": "git", 1809 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1810 | "reference": "f4fd0835cabb0d4a6546d9fe291e5740037aa1e7" 1811 | }, 1812 | "dist": { 1813 | "type": "zip", 1814 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/f4fd0835cabb0d4a6546d9fe291e5740037aa1e7", 1815 | "reference": "f4fd0835cabb0d4a6546d9fe291e5740037aa1e7", 1816 | "shasum": "" 1817 | }, 1818 | "require": { 1819 | "php": "^7.3" 1820 | }, 1821 | "require-dev": { 1822 | "phpunit/phpunit": "^9.0" 1823 | }, 1824 | "type": "library", 1825 | "extra": { 1826 | "branch-alias": { 1827 | "dev-master": "2.0-dev" 1828 | } 1829 | }, 1830 | "autoload": { 1831 | "classmap": [ 1832 | "src/" 1833 | ] 1834 | }, 1835 | "notification-url": "https://packagist.org/downloads/", 1836 | "license": [ 1837 | "BSD-3-Clause" 1838 | ], 1839 | "authors": [ 1840 | { 1841 | "name": "Sebastian Bergmann", 1842 | "email": "sebastian@phpunit.de" 1843 | } 1844 | ], 1845 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1846 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1847 | "time": "2020-02-07T06:19:40+00:00" 1848 | }, 1849 | { 1850 | "name": "sebastian/recursion-context", 1851 | "version": "4.0.0", 1852 | "source": { 1853 | "type": "git", 1854 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1855 | "reference": "cdd86616411fc3062368b720b0425de10bd3d579" 1856 | }, 1857 | "dist": { 1858 | "type": "zip", 1859 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cdd86616411fc3062368b720b0425de10bd3d579", 1860 | "reference": "cdd86616411fc3062368b720b0425de10bd3d579", 1861 | "shasum": "" 1862 | }, 1863 | "require": { 1864 | "php": "^7.3" 1865 | }, 1866 | "require-dev": { 1867 | "phpunit/phpunit": "^9.0" 1868 | }, 1869 | "type": "library", 1870 | "extra": { 1871 | "branch-alias": { 1872 | "dev-master": "4.0-dev" 1873 | } 1874 | }, 1875 | "autoload": { 1876 | "classmap": [ 1877 | "src/" 1878 | ] 1879 | }, 1880 | "notification-url": "https://packagist.org/downloads/", 1881 | "license": [ 1882 | "BSD-3-Clause" 1883 | ], 1884 | "authors": [ 1885 | { 1886 | "name": "Sebastian Bergmann", 1887 | "email": "sebastian@phpunit.de" 1888 | }, 1889 | { 1890 | "name": "Jeff Welch", 1891 | "email": "whatthejeff@gmail.com" 1892 | }, 1893 | { 1894 | "name": "Adam Harvey", 1895 | "email": "aharvey@php.net" 1896 | } 1897 | ], 1898 | "description": "Provides functionality to recursively process PHP variables", 1899 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1900 | "time": "2020-02-07T06:18:20+00:00" 1901 | }, 1902 | { 1903 | "name": "sebastian/resource-operations", 1904 | "version": "3.0.0", 1905 | "source": { 1906 | "type": "git", 1907 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1908 | "reference": "8c98bf0dfa1f9256d0468b9803a1e1df31b6fa98" 1909 | }, 1910 | "dist": { 1911 | "type": "zip", 1912 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/8c98bf0dfa1f9256d0468b9803a1e1df31b6fa98", 1913 | "reference": "8c98bf0dfa1f9256d0468b9803a1e1df31b6fa98", 1914 | "shasum": "" 1915 | }, 1916 | "require": { 1917 | "php": "^7.3" 1918 | }, 1919 | "require-dev": { 1920 | "phpunit/phpunit": "^9.0" 1921 | }, 1922 | "type": "library", 1923 | "extra": { 1924 | "branch-alias": { 1925 | "dev-master": "3.0-dev" 1926 | } 1927 | }, 1928 | "autoload": { 1929 | "classmap": [ 1930 | "src/" 1931 | ] 1932 | }, 1933 | "notification-url": "https://packagist.org/downloads/", 1934 | "license": [ 1935 | "BSD-3-Clause" 1936 | ], 1937 | "authors": [ 1938 | { 1939 | "name": "Sebastian Bergmann", 1940 | "email": "sebastian@phpunit.de" 1941 | } 1942 | ], 1943 | "description": "Provides a list of PHP built-in functions that operate on resources", 1944 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1945 | "time": "2020-02-07T06:13:02+00:00" 1946 | }, 1947 | { 1948 | "name": "sebastian/type", 1949 | "version": "2.0.0", 1950 | "source": { 1951 | "type": "git", 1952 | "url": "https://github.com/sebastianbergmann/type.git", 1953 | "reference": "9e8f42f740afdea51f5f4e8cec2035580e797ee1" 1954 | }, 1955 | "dist": { 1956 | "type": "zip", 1957 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/9e8f42f740afdea51f5f4e8cec2035580e797ee1", 1958 | "reference": "9e8f42f740afdea51f5f4e8cec2035580e797ee1", 1959 | "shasum": "" 1960 | }, 1961 | "require": { 1962 | "php": "^7.3" 1963 | }, 1964 | "require-dev": { 1965 | "phpunit/phpunit": "^9.0" 1966 | }, 1967 | "type": "library", 1968 | "extra": { 1969 | "branch-alias": { 1970 | "dev-master": "2.0-dev" 1971 | } 1972 | }, 1973 | "autoload": { 1974 | "classmap": [ 1975 | "src/" 1976 | ] 1977 | }, 1978 | "notification-url": "https://packagist.org/downloads/", 1979 | "license": [ 1980 | "BSD-3-Clause" 1981 | ], 1982 | "authors": [ 1983 | { 1984 | "name": "Sebastian Bergmann", 1985 | "email": "sebastian@phpunit.de", 1986 | "role": "lead" 1987 | } 1988 | ], 1989 | "description": "Collection of value objects that represent the types of the PHP type system", 1990 | "homepage": "https://github.com/sebastianbergmann/type", 1991 | "time": "2020-02-07T06:13:43+00:00" 1992 | }, 1993 | { 1994 | "name": "sebastian/version", 1995 | "version": "3.0.0", 1996 | "source": { 1997 | "type": "git", 1998 | "url": "https://github.com/sebastianbergmann/version.git", 1999 | "reference": "0411bde656dce64202b39c2f4473993a9081d39e" 2000 | }, 2001 | "dist": { 2002 | "type": "zip", 2003 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/0411bde656dce64202b39c2f4473993a9081d39e", 2004 | "reference": "0411bde656dce64202b39c2f4473993a9081d39e", 2005 | "shasum": "" 2006 | }, 2007 | "require": { 2008 | "php": "^7.3" 2009 | }, 2010 | "type": "library", 2011 | "extra": { 2012 | "branch-alias": { 2013 | "dev-master": "3.0-dev" 2014 | } 2015 | }, 2016 | "autoload": { 2017 | "classmap": [ 2018 | "src/" 2019 | ] 2020 | }, 2021 | "notification-url": "https://packagist.org/downloads/", 2022 | "license": [ 2023 | "BSD-3-Clause" 2024 | ], 2025 | "authors": [ 2026 | { 2027 | "name": "Sebastian Bergmann", 2028 | "email": "sebastian@phpunit.de", 2029 | "role": "lead" 2030 | } 2031 | ], 2032 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 2033 | "homepage": "https://github.com/sebastianbergmann/version", 2034 | "time": "2020-01-21T06:36:37+00:00" 2035 | }, 2036 | { 2037 | "name": "theseer/tokenizer", 2038 | "version": "1.1.3", 2039 | "source": { 2040 | "type": "git", 2041 | "url": "https://github.com/theseer/tokenizer.git", 2042 | "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" 2043 | }, 2044 | "dist": { 2045 | "type": "zip", 2046 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", 2047 | "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", 2048 | "shasum": "" 2049 | }, 2050 | "require": { 2051 | "ext-dom": "*", 2052 | "ext-tokenizer": "*", 2053 | "ext-xmlwriter": "*", 2054 | "php": "^7.0" 2055 | }, 2056 | "type": "library", 2057 | "autoload": { 2058 | "classmap": [ 2059 | "src/" 2060 | ] 2061 | }, 2062 | "notification-url": "https://packagist.org/downloads/", 2063 | "license": [ 2064 | "BSD-3-Clause" 2065 | ], 2066 | "authors": [ 2067 | { 2068 | "name": "Arne Blankerts", 2069 | "email": "arne@blankerts.de", 2070 | "role": "Developer" 2071 | } 2072 | ], 2073 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 2074 | "time": "2019-06-13T22:48:21+00:00" 2075 | } 2076 | ], 2077 | "aliases": [], 2078 | "minimum-stability": "stable", 2079 | "stability-flags": [], 2080 | "prefer-stable": false, 2081 | "prefer-lowest": false, 2082 | "platform": { 2083 | "php": ">=7.1.0", 2084 | "ext-curl": "*" 2085 | }, 2086 | "platform-dev": [], 2087 | "plugin-api-version": "1.1.0" 2088 | } 2089 | -------------------------------------------------------------------------------- /controllers/BuildController.php: -------------------------------------------------------------------------------- 1 | sourceDirs) { 51 | $context->addDirs($this->sourceDirs); 52 | } 53 | if ($this->sourceModules) { 54 | $context->addModules($this->sourceModules); 55 | } 56 | if ($this->sortProperty) { 57 | $context->sortControllers($this->sortProperty); 58 | } 59 | $result = $this->renderPartial(Yii::getAlias($this->template), ['controllers' => $context->controllers]); 60 | if ($this->targetFile) { 61 | file_put_contents(Yii::getAlias($this->targetFile), $result); 62 | } else { 63 | echo $result; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /helpers/DocBlockHelper.php: -------------------------------------------------------------------------------- 1 | getTagsByName('inheritdoc') || $docBlock->getTagsByName('inherited'); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /models/ControllerDoc.php: -------------------------------------------------------------------------------- 1 | _longDescription; 56 | } 57 | 58 | /** 59 | * @return string 60 | */ 61 | public function getShortDescription() 62 | { 63 | return $this->_shortDescription; 64 | } 65 | 66 | /** 67 | * @param $value 68 | * @return bool If label attached to doc 69 | */ 70 | public function hasLabel($value) 71 | { 72 | return isset($this->_labels[$value]); 73 | } 74 | 75 | /** 76 | * Prepares doc 77 | */ 78 | public function prepare() 79 | { 80 | parent::prepare(); 81 | 82 | foreach ($this->getTagsByName('label') as $tag) { 83 | $this->_labels[$tag->getContent()] = true; 84 | } 85 | 86 | $this->query = $this->getTagsByName('query'); 87 | 88 | if ($this->model) { 89 | $this->model->prepare(); 90 | } 91 | } 92 | 93 | /** 94 | * @param $value 95 | */ 96 | public function setShortDescription($value) 97 | { 98 | if (!$this->_shortDescription && $value) { 99 | $this->_shortDescription = $value; 100 | } 101 | } 102 | 103 | /** 104 | * @param $value 105 | */ 106 | public function setLongDescription($value) 107 | { 108 | if (!$this->_longDescription && $value) { 109 | $this->_longDescription = $value; 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /models/ControllerParser.php: -------------------------------------------------------------------------------- 1 | reflection->isAbstract()) { 44 | $this->error = $this->reflection->name . " is abstract"; 45 | return false; 46 | } 47 | 48 | $this->parseClass($doc); 49 | 50 | if ($doc->getTagsByName('ignore')) { 51 | $this->error = $this->reflection->name . " has ignore tag"; 52 | return false; 53 | } 54 | 55 | $doc->path = Inflector::camel2id(substr($this->reflection->getShortName(), 0, -strlen('Controller'))); 56 | $doc->actions = $this->parseActions(); 57 | 58 | // Parse model 59 | $modelParser = Yii::createObject( 60 | [ 61 | 'class' => '\pahanini\restdoc\models\ModelParser', 62 | 'reflection' => new \ReflectionClass($this->getObject()->modelClass), 63 | ] 64 | ); 65 | $doc->model = new ModelDoc(); 66 | $modelParser->parse($doc->model); 67 | 68 | return true; 69 | } 70 | 71 | /** 72 | * @param $doc 73 | * @return bool 74 | */ 75 | public function parseClass(ControllerDoc $doc) 76 | { 77 | if (!$docBlock = new DocBlock($this->reflection)) { 78 | return false; 79 | } 80 | 81 | $doc->longDescription = $docBlock->getLongDescription()->getContents(); 82 | $doc->shortDescription = $docBlock->getShortDescription(); 83 | 84 | $doc->populateTags($docBlock); 85 | 86 | if (DocBlockHelper::isInherit($docBlock)) { 87 | $parentParser = $this->getParentParser(); 88 | $parentParser->parseClass($doc); 89 | } 90 | } 91 | 92 | /** 93 | * include actions defined in controller, as well as those returned by `Controller::actions()` method 94 | * 95 | * @return array 96 | */ 97 | private function parseActions() 98 | { 99 | // default controller actions 100 | $actions = array_keys($this->getObject()->actions()); 101 | $actionMethods = array_filter($this->reflection->getMethods(), 102 | function ($method) { 103 | // should match all methods named actionSomeAction 104 | return preg_match('/action([A-Z]{1}[a-zA-Z]+)/', $method->name, $matches); 105 | }); 106 | $actionMethods = array_map(function ($method) { 107 | return Inflector::slug(str_replace('action', '', $method->name)); 108 | }, $actionMethods); 109 | $actionMethods = array_merge($actions, $actionMethods); 110 | 111 | return array_intersect(['index', 'view', 'create', 'update', 'delete', 'options'], $actionMethods); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /models/Doc.php: -------------------------------------------------------------------------------- 1 | _parent; 32 | } 33 | 34 | /** 35 | * Returns tags with given name. 36 | * 37 | * @param $name 38 | * @return array 39 | */ 40 | public function getTagsByName($name) 41 | { 42 | return isset($this->_tags[$name]) ? $this->_tags[$name] : []; 43 | } 44 | 45 | /** 46 | * @param Doc $value 47 | */ 48 | public function setParent(Doc $value) 49 | { 50 | $this->_parent = $value; 51 | } 52 | 53 | /** 54 | * Extracts tags from docBlock and adds it to document. 55 | * 56 | * @param DocBlock $docBlock 57 | */ 58 | public function populateTags(DocBlock $docBlock) 59 | { 60 | $tags = $docBlock->getTags(); 61 | $offset = strlen(self::TAG_PREFIX); 62 | foreach ($tags as $tag) { 63 | $name = $tag->getName(); 64 | if (strpos($name, self::TAG_PREFIX) === 0) { 65 | $key = substr($name, $offset); 66 | if (!isset($this->_tags)) { 67 | $this->_tags[$key] = []; 68 | } 69 | $this->_tags[$key][] = $tag; 70 | } 71 | } 72 | } 73 | 74 | /** 75 | * Prepares doc. 76 | */ 77 | public function prepare() 78 | { 79 | null; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /models/FieldDoc.php: -------------------------------------------------------------------------------- 1 | _description; 18 | } 19 | 20 | public function getName() 21 | { 22 | return $this->_name; 23 | } 24 | 25 | public function getType() 26 | { 27 | return $this->_type; 28 | } 29 | 30 | public function isInScenario($name) 31 | { 32 | return isset($this->_scenarios[$name]); 33 | } 34 | 35 | public function setDescription($value) 36 | { 37 | if ($value) { 38 | $this->_description = $value; 39 | } 40 | } 41 | 42 | public function setName($value) 43 | { 44 | if ($value) { 45 | $this->_name = $value; 46 | } 47 | } 48 | 49 | public function setType($value) 50 | { 51 | if ($value) { 52 | $this->_type = $value; 53 | } 54 | } 55 | 56 | public function setScenarios(array $value) 57 | { 58 | $this->_scenarios = array_merge($this->_scenarios, $value); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /models/ModelDoc.php: -------------------------------------------------------------------------------- 1 | setName($name); 39 | $field->setParent($this); 40 | $fields[$name] = $field; 41 | } 42 | $fields[$name]->setScenarios($scenarios); 43 | $fields[$name]->setDescription($description); 44 | $fields[$name]->setType($type); 45 | } 46 | 47 | /** 48 | * @param array $fields 49 | * @param string $name 50 | */ 51 | private function _removeField(&$fields, $name) 52 | { 53 | if (isset($fields[$name])) { 54 | unset($fields[$name]); 55 | } 56 | } 57 | 58 | /** 59 | * @param string $name 60 | * @param string $type 61 | * @param string $description 62 | * @param array $scenarios 63 | */ 64 | public function addField($name, $type = '', $description = '', $scenarios = []) 65 | { 66 | $this->_addField($this->_fields, $name, $type, $description, $scenarios); 67 | } 68 | 69 | /** 70 | * @param string $name 71 | * @param string $type 72 | * @param string $description 73 | */ 74 | public function addExtraField($name, $type = '', $description = '') 75 | { 76 | $this->_addField($this->_extraFields, $name, $type, $description); 77 | } 78 | 79 | /** 80 | * @param string $name 81 | */ 82 | public function removeField($name) 83 | { 84 | $this->_removeField($this->_fields, $name); 85 | } 86 | 87 | /** 88 | * @param string $name 89 | */ 90 | public function removeExtraField($name) 91 | { 92 | $this->_removeField($this->_extraFields, $name); 93 | } 94 | 95 | /** 96 | * @param $name 97 | */ 98 | public function addSortField($name) 99 | { 100 | $this->_addField($this->_sortFields, $name); 101 | } 102 | 103 | /** 104 | * Adds scenario info 105 | * 106 | * @param $key 107 | * @param array $fields 108 | */ 109 | public function addScenario($key, array $fields) 110 | { 111 | $this->_scenarios[$key] = $fields; 112 | } 113 | 114 | /** 115 | * @return \pahanini\restdoc\models\FieldDoc[] 116 | */ 117 | public function getExtraFields() 118 | { 119 | return $this->_extraFields; 120 | } 121 | 122 | /** 123 | * @return \pahanini\restdoc\models\FieldDoc[] 124 | */ 125 | public function getFields() 126 | { 127 | return $this->_fields; 128 | } 129 | 130 | /** 131 | * @return \pahanini\restdoc\models\FieldDoc[] 132 | */ 133 | public function getSortFields() 134 | { 135 | return $this->_sortFields; 136 | } 137 | 138 | /** 139 | * @return \pahanini\restdoc\models\ControllerDoc 140 | */ 141 | public function getParent() 142 | { 143 | return $this->_parent; 144 | } 145 | 146 | /** 147 | * Returns property tag 148 | * 149 | * @param $name 150 | * @return mixed 151 | */ 152 | protected function getProperty($name) 153 | { 154 | return isset($this->_properties[$name]) ? $this->_properties[$name] : null; 155 | } 156 | 157 | /** 158 | * Returns scenario 159 | * 160 | * @return array 161 | */ 162 | public function getScenario($name) 163 | { 164 | return isset($this->_scenarios[$name]) ? $this->_scenarios[$name] : null; 165 | } 166 | 167 | /** 168 | * Returns scenarios 169 | * 170 | * @return array 171 | */ 172 | public function getScenarios() 173 | { 174 | return $this->_scenarios; 175 | } 176 | 177 | /** 178 | * Returns array scenarios having given variable name 179 | * 180 | * @return array 181 | */ 182 | public function getScenariosHaving($name) 183 | { 184 | $result = []; 185 | foreach ($this->getScenarios() as $key => $values) { 186 | if (in_array($name, $values)) { 187 | $result[$key] = $values; 188 | } 189 | } 190 | return $result; 191 | } 192 | 193 | /** 194 | * @return bool If model has extra fields 195 | */ 196 | public function hasExtraFields() 197 | { 198 | return !empty($this->_extraFields); 199 | } 200 | 201 | /** 202 | * @return bool If model has fields 203 | */ 204 | public function hasFields() 205 | { 206 | return !empty($this->_fields); 207 | } 208 | 209 | /** 210 | * @return bool If model has sort fields 211 | */ 212 | public function hasSortFields() 213 | { 214 | return !empty($this->_sortFields); 215 | } 216 | 217 | /** 218 | * @param string $name 219 | * @return bool 220 | */ 221 | public function hasScenario($name) 222 | { 223 | return isset($this->_scenarios[$name]); 224 | } 225 | 226 | /** 227 | * @param DocBlock $docBlock 228 | */ 229 | public function populateProperties(DocBlock $docBlock) 230 | { 231 | foreach ($docBlock->getTagsByName('property') as $tag) { 232 | $name = trim($tag->getVariableName(), '$'); 233 | $this->_properties[$name] = $tag; 234 | } 235 | } 236 | 237 | /** 238 | * @todo refactor this code, may be it is a good idea to join fields and extra fields in one array 239 | */ 240 | public function prepare() 241 | { 242 | foreach ($this->getFields() as $field) { 243 | $field->setScenarios($this->getScenariosHaving($field->getName())); 244 | } 245 | 246 | foreach ($this->getTagsByName('field') as $tag) { 247 | $name = trim($tag->getVariableName(), '$'); 248 | $this->addField($name, $tag->getType(), $tag->getDescription(), $this->getScenariosHaving($name)); 249 | } 250 | 251 | foreach ($this->getTagsByName('extraField') as $tag) { 252 | $name = trim($tag->getVariableName(), '$'); 253 | $this->addExtraField($name, $tag->getType(), $tag->getDescription()); 254 | } 255 | 256 | foreach ($this->getTagsByName('sortField') as $tag) { 257 | $name = trim($tag->getContent(), '$'); 258 | $this->addSortField($name); 259 | } 260 | 261 | foreach ($this->getTagsByName('link') as $tag) { 262 | $name = trim($tag->getVariableName(), '$'); 263 | $propertyName = $tag->getType() ? trim($tag->getType(), '\\') : $name; 264 | if ($propertyTag = $this->getProperty($propertyName)) { 265 | $this->addField( 266 | $name, 267 | $propertyTag->getType(), 268 | $propertyTag->getDescription(), 269 | array_merge( 270 | $this->getScenariosHaving($name), 271 | $this->getScenariosHaving($propertyName) 272 | ) 273 | ); 274 | } 275 | } 276 | 277 | foreach ($this->getTagsByName('extraLink') as $tag) { 278 | $name = trim($tag->getVariableName(), '$'); 279 | $propertyName = $tag->getType() ? trim($tag->getType(), '\\') : $name; 280 | if ($propertyTag = $this->getProperty($propertyName)) { 281 | $this->addExtraField( 282 | $name, 283 | $propertyTag->getType(), 284 | $propertyTag->getDescription(), 285 | array_merge( 286 | $this->getScenariosHaving($name), 287 | $this->getScenariosHaving($propertyName) 288 | ) 289 | ); 290 | } 291 | } 292 | 293 | foreach ($this->getTagsByName('ignore') as $key => $tag) { 294 | $name = trim($tag->getVariableName(), '$'); 295 | $this->removeField($name); 296 | $this->removeExtraField($name); 297 | } 298 | 299 | foreach ($this->_fields as $field) { 300 | $field->prepare(); 301 | } 302 | foreach ($this->_extraFields as $field) { 303 | $field->prepare(); 304 | } 305 | foreach ($this->_sortFields as $field) { 306 | $field->prepare(); 307 | } 308 | } 309 | 310 | public function setParent(Doc $value) 311 | { 312 | $this->_parent = $value; 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /models/ModelParser.php: -------------------------------------------------------------------------------- 1 | getObject(); 21 | 22 | foreach ($object->scenarios() as $key => $fields) { 23 | $doc->addScenario($key, $fields); 24 | } 25 | 26 | foreach ($object->extraFields() as $key => $value) { 27 | $doc->addExtraField(is_numeric($key) ? $value : $key); 28 | } 29 | 30 | foreach ($object->fields() as $key => $value) { 31 | $doc->addField(is_numeric($key) ? $value : $key); 32 | } 33 | 34 | $this->parseClass($doc); 35 | $this->parseFields($doc, 'fields'); 36 | $this->parseFields($doc, 'extraFields'); 37 | 38 | return true; 39 | } 40 | 41 | /** 42 | * @param $doc 43 | * @return bool 44 | */ 45 | public function parseClass($doc) 46 | { 47 | if (!$docBlock = new DocBlock($this->reflection)) { 48 | return false; 49 | } 50 | 51 | $doc->populateProperties($docBlock); 52 | $doc->populateTags($docBlock); 53 | 54 | if (DocBlockHelper::isInherit($docBlock)) { 55 | $parentParser = $this->getParentParser(); 56 | $parentParser->parseClass($doc); 57 | } 58 | } 59 | 60 | /** 61 | * @param \pahanini\restdoc\models\ModelDoc $doc 62 | * @param string $methodName 63 | * @return bool 64 | */ 65 | public function parseFields(ModelDoc $doc, $methodName) 66 | { 67 | if (!$docBlock = new DocBlock($this->reflection->getMethod($methodName))) { 68 | return false; 69 | } 70 | 71 | $doc->populateTags($docBlock); 72 | 73 | if (DocBlockHelper::isInherit($docBlock)) { 74 | $parentParser = $this->getParentParser(); 75 | $parentParser->parseFields($doc, $methodName); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /models/ObjectParser.php: -------------------------------------------------------------------------------- 1 | reflection->newInstanceArgs($this->objectArgs); 35 | if ($this->objectConfig) { 36 | $object = Yii::configure($object, $this->objectConfig); 37 | } 38 | return $object; 39 | } 40 | 41 | /** 42 | * Returns object based on reflection 43 | */ 44 | public function getObject() 45 | { 46 | if (!$this->_object) { 47 | $this->_object = $this->createObject(); 48 | } 49 | return $this->_object; 50 | } 51 | 52 | /** 53 | * @return bool|object|ReflectionDoc 54 | * @throws InvalidConfigException 55 | */ 56 | public function getParentParser() 57 | { 58 | if (!$reflection = $this->reflection->getParentClass()) { 59 | return false; 60 | } 61 | return Yii::createObject( 62 | [ 63 | 'class' => static::className(), 64 | 'reflection' => $reflection, 65 | ] 66 | ); 67 | } 68 | 69 | /** 70 | * Extracts data from reflection's docBlock and adds it to current $doc. 71 | * 72 | * @param $doc 73 | * @return bool $doc If docBlock 74 | * @throws \Exception 75 | */ 76 | protected function parseDocBlock($doc) 77 | { 78 | if (!$docBlock = new DocBlock($this->reflection)) { 79 | return false; 80 | } 81 | 82 | if ($docBlock->getTagsByName(self::TAG_PREFIX . 'ignore')) { 83 | throw new \Exception("Ignoring due tag"); 84 | } 85 | 86 | if (!$doc->shortDescription && ($value = $docBlock->getShortDescription())) { 87 | $doc->shortDescription = $value; 88 | } 89 | 90 | if (!$doc->longDescription && ($value = $docBlock->getLongDescription()->getContents())) { 91 | $doc->longDescription = $value; 92 | } 93 | 94 | $tags = $docBlock->getTags(); 95 | 96 | $offset = strlen(self::TAG_PREFIX); 97 | foreach ($tags as $tag) { 98 | $name = $tag->getName(); 99 | if (strpos($name, self::TAG_PREFIX) === 0) { 100 | $doc->addTag(substr($name, $offset), $tag); 101 | } 102 | } 103 | 104 | return (bool)$docBlock->getTagsByName('inherited') || (bool)$docBlock->getTagsByName('inheritdoc'); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /models/Parser.php: -------------------------------------------------------------------------------- 1 | reflection instanceof \Reflector)) { 32 | throw new InvalidConfigException("Reflection property must be set"); 33 | } 34 | } 35 | 36 | /** 37 | * @param \pahanini\restdoc\models\Doc $doc 38 | * @return bool Weather parse was successful 39 | */ 40 | public function parse(Doc $doc) 41 | { 42 | return false; 43 | } 44 | 45 | /** 46 | * Registers all tags handlers. 47 | */ 48 | public static function registerTagHandlers() 49 | { 50 | static $isRegistered; 51 | 52 | if (!$isRegistered) { 53 | $mapping = [ 54 | 'query' => '\pahanini\restdoc\tags\QueryTag', 55 | 'field' => '\phpDocumentor\Reflection\DocBlock\Tag\ParamTag', 56 | 'link' => '\phpDocumentor\Reflection\DocBlock\Tag\ParamTag', 57 | 'label' => '\phpDocumentor\Reflection\DocBlock\Tag', 58 | 'extraField' => '\phpDocumentor\Reflection\DocBlock\Tag\ParamTag', 59 | 'extraLink' => '\phpDocumentor\Reflection\DocBlock\Tag\ParamTag', 60 | 'ignore' => '\phpDocumentor\Reflection\DocBlock\Tag\ParamTag', 61 | ]; 62 | foreach ($mapping as $suffix => $class) { 63 | $tagName = Doc::TAG_PREFIX . $suffix; 64 | Tag::registerTagHandler($tagName, $class); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | tests 13 | 14 | 15 | 16 | 17 | src 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tags/QueryTag.php: -------------------------------------------------------------------------------- 1 | description, 2); 20 | $tmp = explode('=', array_shift($parts)); 21 | if (count($tmp) == 2) { 22 | $this->defaultValue = $tmp[1]; 23 | $this->variableName = $tmp[0]; 24 | } else { 25 | $this->variableName = $tmp[0]; 26 | } 27 | $this->setDescription(join(' ', str_replace("\n", " ", $parts))); 28 | 29 | return $this; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 'unit', 5 | 'basePath' => dirname(__DIR__), 6 | ]; 7 | -------------------------------------------------------------------------------- /tests/controllers/BrandController.php: -------------------------------------------------------------------------------- 1 | function () { 23 | return 'title'; 24 | } 25 | ]; 26 | } 27 | 28 | public function scenarios() 29 | { 30 | return [ 31 | 'api' => ['title'] 32 | ]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/models/NewSpecialOffer.php: -------------------------------------------------------------------------------- 1 | Detail link. 9 | * @restdoc-extraLink $full_name 10 | */ 11 | class NewSpecialOffer extends SpecialOffer 12 | { 13 | /** 14 | * @inheritdoc 15 | * 16 | * @restdoc-extraField string $alpha2 Code country. Detail link. 17 | * @restdoc-ignore $ignore 18 | */ 19 | public function extraFields() 20 | { 21 | return [ 22 | 'alpha2' => function () { 23 | return 'RU'; 24 | }, 25 | 'full_name' => function () { 26 | return 'full_name'; 27 | }, 28 | 'ignore' => function () { 29 | return 'ignore'; 30 | } 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/models/Product.php: -------------------------------------------------------------------------------- 1 | Details link. 11 | */ 12 | class Product extends ActiveRecord 13 | { 14 | /** 15 | * @restdoc-field int $id 16 | * @restdoc-field string $title 17 | * @restdoc-sortField $title 18 | */ 19 | public function fields() 20 | { 21 | return [ 22 | 'id', 23 | 'title' => function () { 24 | return 'title'; 25 | } 26 | ]; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/models/SpecialOffer.php: -------------------------------------------------------------------------------- 1 | Detail link. 9 | * 10 | * @restdoc-link comment $note 11 | */ 12 | class SpecialOffer extends Product 13 | { 14 | public $comment = 'MyComment'; 15 | 16 | /** 17 | * @inheritdoc 18 | * 19 | * @restdoc-field string $title 20 | * @restdoc-link comment $text 21 | * @restdoc-link $comment 22 | * @restdoc-sortField $text 23 | * @restdoc-ignore $is_ignore 24 | */ 25 | public function fields() 26 | { 27 | return [ 28 | 'id', 29 | 'title' => function () { 30 | return 'title'; 31 | }, 32 | 'text' => 'Comment', 33 | 'is_ignore' 34 | ]; 35 | } 36 | 37 | public function scenarios() 38 | { 39 | return [ 40 | 'api-create' => ['id', 'title', 'note', 'is_ignore'], 41 | 'api-update' => ['comment'], 42 | ]; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/modules/api/Module.php: -------------------------------------------------------------------------------- 1 | 'tests\controllers\BrandController', 11 | 'product' => [ 12 | 'class' => 'tests\controllers\ProductController', 13 | 'modelClass' => 'tests\models\SpecialOffer' 14 | ], 15 | ]; 16 | } 17 | -------------------------------------------------------------------------------- /tests/modules/api/controllers/CountryController.php: -------------------------------------------------------------------------------- 1 | function () { 25 | return 'title'; 26 | } 27 | ]; 28 | } 29 | 30 | public function scenarios() 31 | { 32 | return [ 33 | 'api' => ['title'] 34 | ]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/unit/components/ContextTest.php: -------------------------------------------------------------------------------- 1 | addFile(Yii::getAlias('@tests/controllers/ProductController.php')); 15 | 16 | $doc = $context->getControllers()['product']; 17 | $this->assertEquals('Product Controller.', $doc->shortDescription); 18 | $this->assertEquals( 19 | 'Product controller allows to manipulate with products. Second line of description.', 20 | str_replace("\n", " ", $doc->longDescription) 21 | ); 22 | $this->assertEquals('product', $doc->path); 23 | $this->assertEquals('name', $doc->query[0]->variableName); 24 | } 25 | 26 | public function testBrand() 27 | { 28 | $context = new Context(); 29 | $context->addFile(Yii::getAlias('@tests/controllers/BrandController.php')); 30 | 31 | $product = $context->getControllers()['brand']; 32 | $this->assertEquals('Brand Controller.', $product->shortDescription); 33 | $this->assertEquals('brand', $product->path); 34 | } 35 | 36 | public function testSort() 37 | { 38 | $context = new Context(); 39 | $context->addDirs(Yii::getAlias('@tests/controllers')); 40 | $context->sortControllers('shortDescription'); 41 | $controllers = $context->getControllers(); 42 | $this->assertEquals(['brand', 'new-brand', 'product'], array_keys($controllers)); 43 | } 44 | 45 | public function testModule() 46 | { 47 | $context = new Context(); 48 | $context->addModule('tests\modules\api\Module'); 49 | 50 | $controllers = $context->getControllers(); 51 | $this->assertEquals(3, count($controllers)); 52 | 53 | $this->assertEquals('Manager\'s comment Detail link.', $controllers['product']->model->fields['note']->description); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tests/unit/models/ControllerParserTest.php: -------------------------------------------------------------------------------- 1 | ControllerParser::className(), 17 | 'reflection' => new \ReflectionClass('\tests\controllers\NewBrandController'), 18 | ] 19 | ); 20 | $doc = new ControllerDoc(); 21 | 22 | $parser->parse($doc); 23 | $doc->prepare(); 24 | 25 | $this->assertEquals('New brand Controller.', $doc->shortDescription); 26 | $this->assertEquals('Brand info.', $doc->longDescription); 27 | $this->assertEquals(2, count($doc->query)); 28 | } 29 | 30 | 31 | public function testTags() 32 | { 33 | $parser = Yii::createObject( 34 | [ 35 | 'class' => ControllerParser::className(), 36 | 'reflection' => new \ReflectionClass('\tests\controllers\ProductController'), 37 | ] 38 | ); 39 | $doc = new ControllerDoc(); 40 | 41 | $parser->parseClass($doc); 42 | $doc->prepare(); 43 | 44 | $this->assertEquals('name', $doc->query[0]->variableName); 45 | $this->assertEquals('false', $doc->query[0]->defaultValue); 46 | $this->assertTrue($doc->hasLabel('labelA')); 47 | $this->assertFalse($doc->hasLabel('labelB')); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/unit/models/ModelParserTest.php: -------------------------------------------------------------------------------- 1 | ModelParser::className(), 17 | 'reflection' => new \ReflectionClass('\tests\models\SpecialOffer'), 18 | ] 19 | ); 20 | $doc = new ModelDoc(); 21 | 22 | $parser->parse($doc); 23 | $doc->prepare(); 24 | 25 | $this->assertTrue($doc->hasFields()); 26 | $this->assertFalse($doc->hasExtraFields()); 27 | $this->assertTrue($doc->hasSortFields()); 28 | } 29 | 30 | public function testInherit() 31 | { 32 | $parser = Yii::createObject( 33 | [ 34 | 'class' => ModelParser::className(), 35 | 'reflection' => new \ReflectionClass('\tests\models\NewSpecialOffer'), 36 | ] 37 | ); 38 | $doc = new ModelDoc(); 39 | 40 | $parser->parse($doc); 41 | $doc->prepare(); 42 | 43 | $this->assertEquals(2, count($doc->scenarios)); 44 | 45 | $this->assertEquals(2, count($doc->extraFields)); 46 | $this->assertEquals('string', $doc->extraFields['alpha2']->type); 47 | $this->assertEquals('Code country. Detail link.', $doc->extraFields['alpha2']->description); 48 | $this->assertEquals('string|null', $doc->extraFields['full_name']->type); 49 | $this->assertEquals('Full name. Detail link.', $doc->extraFields['full_name']->description); 50 | $this->assertArrayNotHasKey('ignore', $doc->extraFields); 51 | 52 | $this->assertEquals(5, count($doc->fields)); 53 | $this->assertEquals('int', $doc->fields['id']->type); 54 | $this->assertEquals('string', $doc->fields['title']->type); 55 | $this->assertEquals('string', $doc->fields['comment']->type); 56 | $this->assertArrayNotHasKey('is_ignore', $doc->fields); 57 | 58 | $this->assertEquals('string', $doc->fields['note']->type); 59 | $this->assertEquals('string', $doc->fields['text']->type); 60 | 61 | $this->assertEquals(2, count($doc->sortFields)); 62 | $this->assertEquals('title', $doc->sortFields['title']->name); 63 | $this->assertEquals('text', $doc->sortFields['text']->name); 64 | 65 | $this->assertTrue($doc->fields['id']->isInScenario('api-create')); 66 | $this->assertFalse($doc->fields['id']->isInScenario('api-update')); 67 | 68 | $this->assertTrue($doc->fields['text']->isInScenario('api-update')); 69 | $this->assertFalse($doc->fields['text']->isInScenario('api-create')); 70 | 71 | $this->assertTrue($doc->hasFields()); 72 | $this->assertTrue($doc->hasExtraFields()); 73 | $this->assertTrue($doc->hasSortFields()); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /views/slate.php: -------------------------------------------------------------------------------- 1 | --- 2 | title: API Reference 3 | 4 | language_tabs: 5 | - shell 6 | - ruby 7 | - python 8 | 9 | toc_footers: 10 | - Sign Up for a Developer Key 11 | - Documentation Powered by Slate 12 | 13 | includes: 14 | - errors 15 | 16 | search: true 17 | --- 18 | 19 | # Introduction 20 | 21 | Welcome to the Kittn API! You can use our API to access Kittn API endpoints, which can get information on various cats, kittens, and breeds in our database. 22 | 23 | We have language bindings in Shell, Ruby, and Python! You can view code examples in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right. 24 | 25 | This example API documentation page was created with [Slate](http://github.com/tripit/slate). Feel free to edit it and use it as a base for your own API's documentation. 26 | 27 | # Authentication 28 | 29 | > To authorize, use this code: 30 | 31 | ```ruby 32 | require 'kittn' 33 | 34 | api = Kittn::APIClient.authorize!('meowmeowmeow') 35 | ``` 36 | 37 | ```python 38 | import kittn 39 | 40 | api = kittn.authorize('meowmeowmeow') 41 | ``` 42 | 43 | ```shell 44 | # With shell, you can just pass the correct header with each request 45 | curl "api_endpoint_here" 46 | -H "Authorization: meowmeowmeow" 47 | ``` 48 | 49 | > Make sure to replace `meowmeowmeow` with your API key. 50 | 51 | Kittn uses API keys to allow access to the API. You can register a new Kittn API key at our [developer portal](http://example.com/developers). 52 | 53 | Kittn expects for the API key to be included in all API requests to the server in a header that looks like the following: 54 | 55 | `Authorization: meowmeowmeow` 56 | 57 | 60 | 61 | 62 | 63 | #shortDesc?> 64 | 65 | longDesc?> 66 | 67 | 68 | 69 | # Kittens 70 | 71 | ## Get All Kittens 72 | 73 | ```ruby 74 | require 'kittn' 75 | 76 | api = Kittn::APIClient.authorize!('meowmeowmeow') 77 | api.kittens.get 78 | ``` 79 | 80 | ```python 81 | import kittn 82 | 83 | api = kittn.authorize('meowmeowmeow') 84 | api.kittens.get() 85 | ``` 86 | 87 | ```shell 88 | curl "http://example.com/api/kittens" 89 | -H "Authorization: meowmeowmeow" 90 | ``` 91 | 92 | > The above command returns JSON structured like this: 93 | 94 | ```json 95 | [ 96 | { 97 | "id": 1, 98 | "name": "Fluffums", 99 | "breed": "calico", 100 | "fluffiness": 6, 101 | "cuteness": 7 102 | }, 103 | { 104 | "id": 2, 105 | "name": "Isis", 106 | "breed": "unknown", 107 | "fluffiness": 5, 108 | "cuteness": 10 109 | } 110 | ] 111 | ``` 112 | 113 | This endpoint retrieves all kittens. 114 | 115 | ### HTTP Request 116 | 117 | `GET http://example.com/kittens` 118 | 119 | ### Query Parameters 120 | 121 | Parameter | Default | Description 122 | --------- | ------- | ----------- 123 | include_cats | false | If set to true, the result will also include cats. 124 | available | true | If set to false, the result will include kittens that have already been adopted. 125 | 126 | 129 | 130 | ## Get a Specific Kitten 131 | 132 | ```ruby 133 | require 'kittn' 134 | 135 | api = Kittn::APIClient.authorize!('meowmeowmeow') 136 | api.kittens.get(2) 137 | ``` 138 | 139 | ```python 140 | import kittn 141 | 142 | api = kittn.authorize('meowmeowmeow') 143 | api.kittens.get(2) 144 | ``` 145 | 146 | ```shell 147 | curl "http://example.com/api/kittens/3" 148 | -H "Authorization: meowmeowmeow" 149 | ``` 150 | 151 | > The above command returns JSON structured like this: 152 | 153 | ```json 154 | { 155 | "id": 2, 156 | "name": "Isis", 157 | "breed": "unknown", 158 | "fluffiness": 5, 159 | "cuteness": 10 160 | } 161 | ``` 162 | 163 | This endpoint retrieves a specific kitten. 164 | 165 | 166 | 167 | ### HTTP Request 168 | 169 | `GET http://example.com/kittens/` 170 | 171 | ### URL Parameters 172 | 173 | Parameter | Description 174 | --------- | ----------- 175 | ID | The ID of the cat to retrieve 176 | 177 | --------------------------------------------------------------------------------