├── .gitignore
├── LICENSE
├── README.md
├── composer.json
├── composer.lock
├── config
├── config.php
├── console.php
├── db.php
└── params.php
├── controllers
└── SiteController.php
├── docker-compose.yml
├── helpers
├── AuthMethodsFromParamsHelper.php
└── BehaviorsFromParamsHelper.php
├── migrations
├── m141022_115823_create_user_table.php
├── m180221_085153_create_post_table.php
└── m200609_112354_create_access_token_table.php
├── models
├── AccessToken.php
├── Post.php
├── Status.php
├── User.php
└── UserIdentity.php
├── modules
└── v1
│ ├── controllers
│ ├── DefaultController.php
│ └── PostController.php
│ └── v1.php
├── vendor
└── .gitignore
├── web
├── .htaccess
└── index.php
├── yii
└── yii.bat
/.gitignore:
--------------------------------------------------------------------------------
1 | # yii console command
2 | # /yii
3 |
4 | # phpstorm project files
5 | .idea
6 |
7 | # netbeans project files
8 | nbproject
9 |
10 | # zend studio for eclipse project files
11 | .buildpath
12 | .project
13 | .settings
14 |
15 | # windows thumbnail cache
16 | Thumbs.db
17 |
18 | _protected/config/db.php
19 |
20 | # composer itself is not needed
21 | composer.phar
22 |
23 | # Mac DS_Store Files
24 | .DS_Store
25 |
26 | # phpunit itself is not needed
27 | phpunit.phar
28 | # local phpunit config
29 | /phpunit.xml
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | // Copyright 2020 @hoaaah
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Yii 2 REST API Template
6 |
7 |
8 |
9 | Yii2 REST API Template
10 | -------------------
11 | This is a a REST API TEMPLATE with Yii2. This template use [Yii2-Micro](https://github.com/hoaaah/yii2-micro) approach so it will be lightweight and easy to deploy.
12 |
13 |
14 | # Installation
15 |
16 | The preferred way to install this template is through [composer](http://getcomposer.org/download/).
17 |
18 | Either run
19 |
20 | ```bash
21 | composer create-project --prefer-dist hoaaah/yii2-rest-api-template [app_name]
22 | ```
23 |
24 | Setup your database configuration from `config/db.php`. Create your database because this template will not create it for you :)
25 |
26 | ```php
27 | [
30 | 'db' => [
31 | 'class' => 'yii\db\Connection',
32 | 'dsn' => 'mysql:host=localhost;dbname=your_db_name',
33 | 'username' => 'root',
34 | 'password' => '',
35 | 'charset' => 'utf8',
36 | ],
37 | ],
38 | ];
39 |
40 | ```
41 |
42 | Then run migration to create table in selected database.
43 |
44 | ```bash
45 | yii migrate
46 | ```
47 |
48 | # Directory Structure
49 | Since this template use MicroFramework approach, directory structure might be a little bit different from Yii2.
50 |
51 | config/ contains application configurations
52 | controllers/ contains Web controller classes
53 | migration/ contains list of your migration files
54 | models/ contains model classes
55 | modules/ contains your rest-api versioning (based on modules)
56 | vendor/ contains dependent 3rd-party packages
57 | web/ contains the entry script and Web resources
58 |
59 | This template use modules as versioning pattern. Every version of API saved in a module. This template already have v1 module, so it means if consumer want to use v1 API, it can access `https://your-api-url/v1/endpoint`.
60 |
61 |
62 | # API Scenario
63 | ## Supported Authentication
64 | This template support 3 most used authentication. (Actually it's not me who make it, Yii2 already support it all :D ).
65 |
66 | 1. HTTP Basic Auth: the access token is sent as the username. This should only be used when an access token can be safely stored on the API consumer side. For example, the API consumer is a program running on a server.
67 | 2. Query parameter: the access token is sent as a query parameter in the API URL, e.g., https://example.com/users?access-token=xxxxxxxx. Because most Web servers will keep query parameters in server logs, this approach should be mainly used to serve JSONP requests which cannot use HTTP headers to send access tokens.
68 | 3. OAuth 2: the access token is obtained by the consumer from an authorization server and sent to the API server via HTTP Bearer Tokens, according to the OAuth2 protocol.
69 |
70 | ## Global Configuration of AuthMethods and RateLimiter
71 | This template provide global configuration to set your application supported authMethods. You can find global configuration from `app\config\params.php`. Set your supported authMethods and RateLimiter from this file.
72 |
73 | ```php
74 | return [
75 | 'useHttpBasicAuth' => true,
76 | 'useHttpBearerAuth' => true,
77 | 'useQueryParamAuth' => true,
78 | 'useRateLimiter' => false,
79 | ];
80 | ```
81 |
82 | Example use in behaviors looks like this
83 |
84 | ```php
85 | use app\helpers\BehaviorsFromParamsHelper;
86 | use yii\rest\ActiveController;
87 |
88 | class PostController extends ActiveController
89 | {
90 | public $modelClass = 'app\models\Post';
91 |
92 | public function behaviors()
93 | {
94 | $behaviors = parent::behaviors();
95 | $behaviors = BehaviorsFromParamsHelper::behaviors($behaviors);
96 | // if you need other behaviors method use like this
97 | // $behaviors['otherMethods'] = $value;
98 | return $behaviors;
99 | }
100 | }
101 | ```
102 |
103 | ### Ratelimiter
104 | To enable your ratelimiter configuration, please follow official guide from [Yii documentation](https://www.yiiframework.com/doc/guide/2.0/en/rest-rate-limiting).
105 |
106 | ## Auth Scenario
107 | This template already have basic endpoint that you can use to start your REST-API. Such as:
108 |
109 | Endpoint | Type |Usage
110 | ---------|------|-----
111 | https://YOUR-API-URL/ | GET| list all post created
112 | https://YOUR-API-URL/view?id={id} | GET| View a post
113 | https://YOUR-API-URL/login | POST | Login with username and password
114 | https://YOUR-API-URL/signup | POST | Signup with username, email and password
115 | https://YOUR-API-URL/v1/post | GET | List all post created
116 | https://YOUR-API-URL/v1/post/create | POST | Create a new post (title, body)
117 | https://YOUR-API-URL/v1/post/update?id={id} | PUT / PATCH | Update a post (title, body)
118 | https://YOUR-API-URL/v1/post/delete?id={id} | DELETE | Delete a post
119 | https://YOUR-API-URL/v1/post/view?id={id} | GET | View a post
120 |
121 | ## Access Token Management
122 | This application manage token via access_token table. Access Token have certain expiration based on $tokenExpiration value. Default Token Expiration are in seconds.
123 |
124 | ```php
125 | public $tokenExpiration = 60 * 24 * 365; // in seconds
126 | ```
127 |
128 | In certain case you want to make a token expire before given tokenExpiration. Use ```expireThisToken()``` method to achieve it.
129 | ```php
130 | $accessToken = AccessToken::findOne(['token' => $token]);
131 | $accessToken->expireThisToken();
132 | ```
133 |
134 | Or you want to make all tokens from certain user expire, use ```makeAllUserTokenExpiredByUserId($userId)``` method to achieve it.
135 | ```php
136 | $user = Yii::$app->user->identity; // or User::findOne($id)
137 | AccessToken::makeAllUserTokenExpiredByUserId($user->id);
138 | ```
139 |
140 | ## API versioning
141 | This template give you versioning scenario based on module application. In Yii2 a module are self-contained software units that consist of model, views, controllers and other supporting components. This template already have v1 module, it means all of endpoint for API v1 created in this module. When you publish a new API version (that break backward compatibility / BBC), you can create a new module. For more information create a module, you can visit this [Yii2 Guide on Creating Module](https://www.yiiframework.com/doc/guide/2.0/en/structure-modules).
142 |
143 |
144 | # TODO
145 | Feel free to contribute if you have any idea.
146 | - [x] Rest API Template
147 | - [x] Login and signup in SiteController
148 | - [x] Example of versioning and Blog Scenario
149 | - [x] Authentication Type from params
150 | - [x] Rate Limit from params
151 | - [x] Change auth_key for every login
152 | - [x] Auth_key have expiration
153 | - [x] each auth_key have application token
154 |
155 |
156 | # Creator
157 |
158 | This Template was created by and is maintained by **[Heru Arief Wijaya](http://belajararief.com/)**.
159 |
160 | * https://twitter.com/hoaaah
161 | * https://github.com/hoaaah
162 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hoaaah/yii2-rest-api-template",
3 | "description": "REST API Template with Yii2",
4 | "license": "Apache-2.0",
5 | "require": {
6 | "yiisoft/yii2": "~2.0.0"
7 | },
8 | "authors": [
9 | {
10 | "name": "Arief Wijaya",
11 | "email": "hoaaah.arief@gmail.com"
12 | }
13 | ],
14 | "repositories": [
15 | {
16 | "type": "composer",
17 | "url": "https://asset-packagist.org"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/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": "32d871bdfcd4ca82767781f505bbd560",
8 | "packages": [
9 | {
10 | "name": "bower-asset/inputmask",
11 | "version": "5.0.9",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/RobinHerbots/Inputmask.git",
15 | "reference": "310a33557e2944daf86d5946a5e8c82b9118f8f7"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/RobinHerbots/Inputmask/zipball/310a33557e2944daf86d5946a5e8c82b9118f8f7",
20 | "reference": "310a33557e2944daf86d5946a5e8c82b9118f8f7"
21 | },
22 | "require": {
23 | "bower-asset/jquery": ">=1.7"
24 | },
25 | "type": "bower-asset",
26 | "license": [
27 | "http://opensource.org/licenses/mit-license.php"
28 | ]
29 | },
30 | {
31 | "name": "bower-asset/jquery",
32 | "version": "3.7.1",
33 | "source": {
34 | "type": "git",
35 | "url": "https://github.com/jquery/jquery-dist.git",
36 | "reference": "fde1f76e2799dd877c176abde0ec836553246991"
37 | },
38 | "dist": {
39 | "type": "zip",
40 | "url": "https://api.github.com/repos/jquery/jquery-dist/zipball/fde1f76e2799dd877c176abde0ec836553246991",
41 | "reference": "fde1f76e2799dd877c176abde0ec836553246991"
42 | },
43 | "type": "bower-asset",
44 | "license": [
45 | "MIT"
46 | ]
47 | },
48 | {
49 | "name": "bower-asset/punycode",
50 | "version": "v2.3.1",
51 | "source": {
52 | "type": "git",
53 | "url": "https://github.com/mathiasbynens/punycode.js.git",
54 | "reference": "9e1b2cda98d215d3a73fcbfe93c62e021f4ba768"
55 | },
56 | "dist": {
57 | "type": "zip",
58 | "url": "https://api.github.com/repos/mathiasbynens/punycode.js/zipball/9e1b2cda98d215d3a73fcbfe93c62e021f4ba768",
59 | "reference": "9e1b2cda98d215d3a73fcbfe93c62e021f4ba768"
60 | },
61 | "type": "bower-asset"
62 | },
63 | {
64 | "name": "bower-asset/yii2-pjax",
65 | "version": "2.0.8",
66 | "source": {
67 | "type": "git",
68 | "url": "git@github.com:yiisoft/jquery-pjax.git",
69 | "reference": "a9298d57da63d14a950f1b94366a864bc62264fb"
70 | },
71 | "dist": {
72 | "type": "zip",
73 | "url": "https://api.github.com/repos/yiisoft/jquery-pjax/zipball/a9298d57da63d14a950f1b94366a864bc62264fb",
74 | "reference": "a9298d57da63d14a950f1b94366a864bc62264fb"
75 | },
76 | "require": {
77 | "bower-asset/jquery": ">=1.8"
78 | },
79 | "type": "bower-asset",
80 | "license": [
81 | "MIT"
82 | ]
83 | },
84 | {
85 | "name": "cebe/markdown",
86 | "version": "1.2.1",
87 | "source": {
88 | "type": "git",
89 | "url": "https://github.com/cebe/markdown.git",
90 | "reference": "9bac5e971dd391e2802dca5400bbeacbaea9eb86"
91 | },
92 | "dist": {
93 | "type": "zip",
94 | "url": "https://api.github.com/repos/cebe/markdown/zipball/9bac5e971dd391e2802dca5400bbeacbaea9eb86",
95 | "reference": "9bac5e971dd391e2802dca5400bbeacbaea9eb86",
96 | "shasum": ""
97 | },
98 | "require": {
99 | "lib-pcre": "*",
100 | "php": ">=5.4.0"
101 | },
102 | "require-dev": {
103 | "cebe/indent": "*",
104 | "facebook/xhprof": "*@dev",
105 | "phpunit/phpunit": "4.1.*"
106 | },
107 | "bin": [
108 | "bin/markdown"
109 | ],
110 | "type": "library",
111 | "extra": {
112 | "branch-alias": {
113 | "dev-master": "1.2.x-dev"
114 | }
115 | },
116 | "autoload": {
117 | "psr-4": {
118 | "cebe\\markdown\\": ""
119 | }
120 | },
121 | "notification-url": "https://packagist.org/downloads/",
122 | "license": [
123 | "MIT"
124 | ],
125 | "authors": [
126 | {
127 | "name": "Carsten Brandt",
128 | "email": "mail@cebe.cc",
129 | "homepage": "http://cebe.cc/",
130 | "role": "Creator"
131 | }
132 | ],
133 | "description": "A super fast, highly extensible markdown parser for PHP",
134 | "homepage": "https://github.com/cebe/markdown#readme",
135 | "keywords": [
136 | "extensible",
137 | "fast",
138 | "gfm",
139 | "markdown",
140 | "markdown-extra"
141 | ],
142 | "support": {
143 | "issues": "https://github.com/cebe/markdown/issues",
144 | "source": "https://github.com/cebe/markdown"
145 | },
146 | "time": "2018-03-26T11:24:36+00:00"
147 | },
148 | {
149 | "name": "ezyang/htmlpurifier",
150 | "version": "v4.17.0",
151 | "source": {
152 | "type": "git",
153 | "url": "https://github.com/ezyang/htmlpurifier.git",
154 | "reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c"
155 | },
156 | "dist": {
157 | "type": "zip",
158 | "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/bbc513d79acf6691fa9cf10f192c90dd2957f18c",
159 | "reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c",
160 | "shasum": ""
161 | },
162 | "require": {
163 | "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0"
164 | },
165 | "require-dev": {
166 | "cerdic/css-tidy": "^1.7 || ^2.0",
167 | "simpletest/simpletest": "dev-master"
168 | },
169 | "suggest": {
170 | "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.",
171 | "ext-bcmath": "Used for unit conversion and imagecrash protection",
172 | "ext-iconv": "Converts text to and from non-UTF-8 encodings",
173 | "ext-tidy": "Used for pretty-printing HTML"
174 | },
175 | "type": "library",
176 | "autoload": {
177 | "files": [
178 | "library/HTMLPurifier.composer.php"
179 | ],
180 | "psr-0": {
181 | "HTMLPurifier": "library/"
182 | },
183 | "exclude-from-classmap": [
184 | "/library/HTMLPurifier/Language/"
185 | ]
186 | },
187 | "notification-url": "https://packagist.org/downloads/",
188 | "license": [
189 | "LGPL-2.1-or-later"
190 | ],
191 | "authors": [
192 | {
193 | "name": "Edward Z. Yang",
194 | "email": "admin@htmlpurifier.org",
195 | "homepage": "http://ezyang.com"
196 | }
197 | ],
198 | "description": "Standards compliant HTML filter written in PHP",
199 | "homepage": "http://htmlpurifier.org/",
200 | "keywords": [
201 | "html"
202 | ],
203 | "support": {
204 | "issues": "https://github.com/ezyang/htmlpurifier/issues",
205 | "source": "https://github.com/ezyang/htmlpurifier/tree/v4.17.0"
206 | },
207 | "time": "2023-11-17T15:01:25+00:00"
208 | },
209 | {
210 | "name": "paragonie/random_compat",
211 | "version": "v9.99.100",
212 | "source": {
213 | "type": "git",
214 | "url": "https://github.com/paragonie/random_compat.git",
215 | "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a"
216 | },
217 | "dist": {
218 | "type": "zip",
219 | "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a",
220 | "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a",
221 | "shasum": ""
222 | },
223 | "require": {
224 | "php": ">= 7"
225 | },
226 | "require-dev": {
227 | "phpunit/phpunit": "4.*|5.*",
228 | "vimeo/psalm": "^1"
229 | },
230 | "suggest": {
231 | "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
232 | },
233 | "type": "library",
234 | "notification-url": "https://packagist.org/downloads/",
235 | "license": [
236 | "MIT"
237 | ],
238 | "authors": [
239 | {
240 | "name": "Paragon Initiative Enterprises",
241 | "email": "security@paragonie.com",
242 | "homepage": "https://paragonie.com"
243 | }
244 | ],
245 | "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
246 | "keywords": [
247 | "csprng",
248 | "polyfill",
249 | "pseudorandom",
250 | "random"
251 | ],
252 | "support": {
253 | "email": "info@paragonie.com",
254 | "issues": "https://github.com/paragonie/random_compat/issues",
255 | "source": "https://github.com/paragonie/random_compat"
256 | },
257 | "time": "2020-10-15T08:29:30+00:00"
258 | },
259 | {
260 | "name": "yiisoft/yii2",
261 | "version": "2.0.50",
262 | "source": {
263 | "type": "git",
264 | "url": "https://github.com/yiisoft/yii2-framework.git",
265 | "reference": "a90b6638cce84e78ed8f681a5cad4a14c76464b4"
266 | },
267 | "dist": {
268 | "type": "zip",
269 | "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/a90b6638cce84e78ed8f681a5cad4a14c76464b4",
270 | "reference": "a90b6638cce84e78ed8f681a5cad4a14c76464b4",
271 | "shasum": ""
272 | },
273 | "require": {
274 | "bower-asset/inputmask": "^5.0.8 ",
275 | "bower-asset/jquery": "3.7.*@stable | 3.6.*@stable | 3.5.*@stable | 3.4.*@stable | 3.3.*@stable | 3.2.*@stable | 3.1.*@stable | 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable",
276 | "bower-asset/punycode": "^2.2",
277 | "bower-asset/yii2-pjax": "~2.0.1",
278 | "cebe/markdown": "~1.0.0 | ~1.1.0 | ~1.2.0",
279 | "ext-ctype": "*",
280 | "ext-mbstring": "*",
281 | "ezyang/htmlpurifier": "^4.17",
282 | "lib-pcre": "*",
283 | "paragonie/random_compat": ">=1",
284 | "php": ">=7.3.0",
285 | "yiisoft/yii2-composer": "~2.0.4"
286 | },
287 | "bin": [
288 | "yii"
289 | ],
290 | "type": "library",
291 | "extra": {
292 | "branch-alias": {
293 | "dev-master": "2.0.x-dev"
294 | }
295 | },
296 | "autoload": {
297 | "psr-4": {
298 | "yii\\": ""
299 | }
300 | },
301 | "notification-url": "https://packagist.org/downloads/",
302 | "license": [
303 | "BSD-3-Clause"
304 | ],
305 | "authors": [
306 | {
307 | "name": "Qiang Xue",
308 | "email": "qiang.xue@gmail.com",
309 | "homepage": "https://www.yiiframework.com/",
310 | "role": "Founder and project lead"
311 | },
312 | {
313 | "name": "Alexander Makarov",
314 | "email": "sam@rmcreative.ru",
315 | "homepage": "https://rmcreative.ru/",
316 | "role": "Core framework development"
317 | },
318 | {
319 | "name": "Maurizio Domba",
320 | "homepage": "http://mdomba.info/",
321 | "role": "Core framework development"
322 | },
323 | {
324 | "name": "Carsten Brandt",
325 | "email": "mail@cebe.cc",
326 | "homepage": "https://www.cebe.cc/",
327 | "role": "Core framework development"
328 | },
329 | {
330 | "name": "Timur Ruziev",
331 | "email": "resurtm@gmail.com",
332 | "homepage": "http://resurtm.com/",
333 | "role": "Core framework development"
334 | },
335 | {
336 | "name": "Paul Klimov",
337 | "email": "klimov.paul@gmail.com",
338 | "role": "Core framework development"
339 | },
340 | {
341 | "name": "Dmitry Naumenko",
342 | "email": "d.naumenko.a@gmail.com",
343 | "role": "Core framework development"
344 | },
345 | {
346 | "name": "Boudewijn Vahrmeijer",
347 | "email": "info@dynasource.eu",
348 | "homepage": "http://dynasource.eu",
349 | "role": "Core framework development"
350 | }
351 | ],
352 | "description": "Yii PHP Framework Version 2",
353 | "homepage": "https://www.yiiframework.com/",
354 | "keywords": [
355 | "framework",
356 | "yii2"
357 | ],
358 | "support": {
359 | "forum": "https://forum.yiiframework.com/",
360 | "irc": "ircs://irc.libera.chat:6697/yii",
361 | "issues": "https://github.com/yiisoft/yii2/issues?state=open",
362 | "source": "https://github.com/yiisoft/yii2",
363 | "wiki": "https://www.yiiframework.com/wiki"
364 | },
365 | "funding": [
366 | {
367 | "url": "https://github.com/yiisoft",
368 | "type": "github"
369 | },
370 | {
371 | "url": "https://opencollective.com/yiisoft",
372 | "type": "open_collective"
373 | },
374 | {
375 | "url": "https://tidelift.com/funding/github/packagist/yiisoft/yii2",
376 | "type": "tidelift"
377 | }
378 | ],
379 | "time": "2024-05-30T17:23:31+00:00"
380 | },
381 | {
382 | "name": "yiisoft/yii2-composer",
383 | "version": "2.0.10",
384 | "source": {
385 | "type": "git",
386 | "url": "https://github.com/yiisoft/yii2-composer.git",
387 | "reference": "94bb3f66e779e2774f8776d6e1bdeab402940510"
388 | },
389 | "dist": {
390 | "type": "zip",
391 | "url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/94bb3f66e779e2774f8776d6e1bdeab402940510",
392 | "reference": "94bb3f66e779e2774f8776d6e1bdeab402940510",
393 | "shasum": ""
394 | },
395 | "require": {
396 | "composer-plugin-api": "^1.0 | ^2.0"
397 | },
398 | "require-dev": {
399 | "composer/composer": "^1.0 | ^2.0@dev",
400 | "phpunit/phpunit": "<7"
401 | },
402 | "type": "composer-plugin",
403 | "extra": {
404 | "class": "yii\\composer\\Plugin",
405 | "branch-alias": {
406 | "dev-master": "2.0.x-dev"
407 | }
408 | },
409 | "autoload": {
410 | "psr-4": {
411 | "yii\\composer\\": ""
412 | }
413 | },
414 | "notification-url": "https://packagist.org/downloads/",
415 | "license": [
416 | "BSD-3-Clause"
417 | ],
418 | "authors": [
419 | {
420 | "name": "Qiang Xue",
421 | "email": "qiang.xue@gmail.com"
422 | },
423 | {
424 | "name": "Carsten Brandt",
425 | "email": "mail@cebe.cc"
426 | }
427 | ],
428 | "description": "The composer plugin for Yii extension installer",
429 | "keywords": [
430 | "composer",
431 | "extension installer",
432 | "yii2"
433 | ],
434 | "support": {
435 | "forum": "http://www.yiiframework.com/forum/",
436 | "irc": "irc://irc.freenode.net/yii",
437 | "issues": "https://github.com/yiisoft/yii2-composer/issues",
438 | "source": "https://github.com/yiisoft/yii2-composer",
439 | "wiki": "http://www.yiiframework.com/wiki/"
440 | },
441 | "funding": [
442 | {
443 | "url": "https://github.com/yiisoft",
444 | "type": "github"
445 | },
446 | {
447 | "url": "https://opencollective.com/yiisoft",
448 | "type": "open_collective"
449 | },
450 | {
451 | "url": "https://tidelift.com/funding/github/packagist/yiisoft/yii2-composer",
452 | "type": "tidelift"
453 | }
454 | ],
455 | "time": "2020-06-24T00:04:01+00:00"
456 | }
457 | ],
458 | "packages-dev": [],
459 | "aliases": [],
460 | "minimum-stability": "stable",
461 | "stability-flags": [],
462 | "prefer-stable": false,
463 | "prefer-lowest": false,
464 | "platform": [],
465 | "platform-dev": [],
466 | "plugin-api-version": "2.6.0"
467 | }
468 |
--------------------------------------------------------------------------------
/config/config.php:
--------------------------------------------------------------------------------
1 | 'micro-app',
17 | // the basePath of the application will be the `micro-app` directory
18 | 'basePath' => dirname(__DIR__),
19 | 'modules' => [
20 | 'v1' => [
21 | 'class' => 'app\modules\v1\v1',
22 | ],
23 | ],
24 | // this is where the application will find all controllers
25 | 'controllerNamespace' => 'app\controllers',
26 | // set an alias to enable autoloading of classes from the 'micro' namespace
27 | 'aliases' => [
28 | '@app' => __DIR__.'/../',
29 | ],
30 | 'components' => [
31 | 'urlManager' => [
32 | 'class' => 'yii\web\UrlManager',
33 | 'enablePrettyUrl' => true,
34 | 'showScriptName' => false,
35 | 'rules' => [
36 | '' => 'site/',
37 | ],
38 | ],
39 | 'user' => [
40 | 'identityClass' => 'app\models\UserIdentity',
41 | 'enableAutoLogin' => false,
42 | 'enableSession' => false,
43 | 'loginUrl' => null,
44 | ],
45 | 'request' => [
46 | 'parsers' => [
47 | 'application/json' => 'yii\web\JsonParser',
48 | ],
49 | 'enableCsrfCookie' => false,
50 | ],
51 | 'db' => $db,
52 | ],
53 | 'params' => $params,
54 | ];
--------------------------------------------------------------------------------
/config/console.php:
--------------------------------------------------------------------------------
1 | 'basic-console',
8 | 'basePath' => dirname(__DIR__),
9 | 'bootstrap' => ['log'],
10 | 'controllerNamespace' => 'app\console\controllers',
11 | 'controllerMap' => [
12 | 'migrate' => [
13 | 'class' => 'yii\console\controllers\MigrateController',
14 | 'migrationPath' => '@app/migrations',
15 | ],
16 | ],
17 | 'components' => [
18 | 'authManager' => [
19 | 'class' => 'yii\rbac\DbManager',
20 | ],
21 | 'cache' => [
22 | 'class' => 'yii\caching\FileCache',
23 | ],
24 | 'user' => [
25 | 'class' => 'app\models\UserIdentity',
26 | 'enableAutoLogin' => false,
27 | 'enableSession' => false,
28 | 'loginUrl' => null,
29 | ],
30 | 'log' => [
31 | 'targets' => [
32 | [
33 | 'class' => 'yii\log\FileTarget',
34 | 'levels' => ['error', 'warning'],
35 | ],
36 | ],
37 | ],
38 | 'db' => $db,
39 | ],
40 | 'params' => $params,
41 | ];
42 |
43 |
44 | return $config;
--------------------------------------------------------------------------------
/config/db.php:
--------------------------------------------------------------------------------
1 | 'yii\db\Connection',
6 | 'dsn' => 'mysql:host=localhost;dbname=yii2-rest-api',
7 | 'username' => 'root',
8 | 'password' => '',
9 | 'charset' => 'utf8',
10 | ];
--------------------------------------------------------------------------------
/config/params.php:
--------------------------------------------------------------------------------
1 | true,
6 | 'useHttpBearerAuth' => true,
7 | 'useQueryParamAuth' => true,
8 |
9 | /**
10 | * use rate limiter for user
11 | * you must modified your UserIdentity class, follow this guidelines for complete guide
12 | * https://www.yiiframework.com/doc/guide/2.0/en/rest-rate-limiting
13 | */
14 | 'useRateLimiter' => false,
15 | ];
16 |
--------------------------------------------------------------------------------
/controllers/SiteController.php:
--------------------------------------------------------------------------------
1 | ['POST'],
24 | 'login' => ['POST'],
25 | ];
26 | }
27 |
28 | public function actionIndex()
29 | {
30 | $post = Post::find()->all();
31 | return [
32 | 'status' => Status::STATUS_OK,
33 | 'message' => 'Hello :)',
34 | 'data' => $post
35 | ];
36 | }
37 |
38 |
39 | public function actionView($id)
40 | {
41 | $post = Post::findOne($id);
42 | return [
43 | 'status' => Status::STATUS_FOUND,
44 | 'message' => 'Data Found',
45 | 'data' => $post
46 | ];
47 | }
48 |
49 | public function actionSignup()
50 | {
51 | $model = new User();
52 | $params = Yii::$app->request->post();
53 | if(!$params) {
54 | Yii::$app->response->statusCode = Status::STATUS_BAD_REQUEST;
55 | return [
56 | 'status' => Status::STATUS_BAD_REQUEST,
57 | 'message' => "Need username, password, and email.",
58 | 'data' => ''
59 | ];
60 | }
61 |
62 |
63 | $model->username = $params['username'];
64 | $model->email = $params['email'];
65 |
66 | $model->setPassword($params['password']);
67 | $model->generateAuthKey();
68 | $model->status = User::STATUS_ACTIVE;
69 |
70 | if ($model->save()) {
71 | Yii::$app->response->statusCode = Status::STATUS_CREATED;
72 | $response['isSuccess'] = 201;
73 | $response['message'] = 'You are now a member!';
74 | $response['user'] = \app\models\User::findByUsername($model->username);
75 | return [
76 | 'status' => Status::STATUS_CREATED,
77 | 'message' => 'You are now a member',
78 | 'data' => User::findByUsername($model->username),
79 | ];
80 | } else {
81 | Yii::$app->response->statusCode = Status::STATUS_BAD_REQUEST;
82 | $model->getErrors();
83 | $response['hasErrors'] = $model->hasErrors();
84 | $response['errors'] = $model->getErrors();
85 | return [
86 | 'status' => Status::STATUS_BAD_REQUEST,
87 | 'message' => 'Error saving data!',
88 | 'data' => [
89 | 'hasErrors' => $model->hasErrors(),
90 | 'getErrors' => $model->getErrors(),
91 | ]
92 | ];
93 | }
94 | }
95 |
96 | public function actionLogin()
97 | {
98 | $params = Yii::$app->request->post();
99 | if(empty($params['username']) || empty($params['password'])) return [
100 | 'status' => Status::STATUS_BAD_REQUEST,
101 | 'message' => "Need username and password.",
102 | 'data' => ''
103 | ];
104 |
105 | $user = User::findByUsername($params['username']);
106 |
107 | if ($user->validatePassword($params['password'])) {
108 | if(isset($params['consumer'])) $user->consumer = $params['consumer'];
109 | if(isset($params['access_given'])) $user->access_given = $params['access_given'];
110 |
111 | Yii::$app->response->statusCode = Status::STATUS_FOUND;
112 | $user->generateAuthKey();
113 | $user->save();
114 | return [
115 | 'status' => Status::STATUS_FOUND,
116 | 'message' => 'Login Succeed, save your token',
117 | 'data' => [
118 | 'id' => $user->username,
119 | 'token' => $user->auth_key,
120 | 'email' => $user['email'],
121 | ]
122 | ];
123 | } else {
124 | Yii::$app->response->statusCode = Status::STATUS_UNAUTHORIZED;
125 | return [
126 | 'status' => Status::STATUS_UNAUTHORIZED,
127 | 'message' => 'Username and Password not found. Check Again!',
128 | 'data' => ''
129 | ];
130 | }
131 | }
132 | }
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | php:
4 | image: yiisoftware/yii2-php:7.3-apache
5 | volumes:
6 | - ~/.composer-docker/cache:/root/.composer/cache:delegated
7 | - ./:/app:delegated
8 | ports:
9 | - '8000:80'
--------------------------------------------------------------------------------
/helpers/AuthMethodsFromParamsHelper.php:
--------------------------------------------------------------------------------
1 | getAuthMethods();
34 | }
35 |
36 | /**
37 | * Give array of authMethods from params.php
38 | * the result of $this->getAuthMethods would be like following
39 | * ```php
40 | * [
41 | * Http:BasicAuth::class,
42 | * HttpBearerAuth::class,
43 | * QueryParamAuth::class,
44 | * ]
45 | * ```
46 | *
47 | * @return array the array representation of the object
48 | */
49 | private function getAuthMethods(){
50 | $authMethodsArray = null;
51 | if(Yii::$app->params['useHttpBasicAuth']) $authMethodsArray[] = HttpBasicAuth::class;
52 | if(Yii::$app->params['useHttpBearerAuth']) $authMethodsArray[] = HttpBearerAuth::class;
53 | if(Yii::$app->params['useQueryParamAuth']) $authMethodsArray[] = QueryParamAuth::class;
54 | if($authMethodsArray == null) throw new NotSupportedException('You must choose at least one auth methods, configure your app\config\params.php for more options.');
55 | return $authMethodsArray;
56 | }
57 | }
--------------------------------------------------------------------------------
/helpers/BehaviorsFromParamsHelper.php:
--------------------------------------------------------------------------------
1 | getBehaviors($behaviors);
45 | }
46 |
47 | private function getBehaviors($behaviors){
48 | $behaviors['authenticator'] = [
49 | 'class' => CompositeAuth::class,
50 | 'authMethods' => AuthMethodsFromParamsHelper::authMethods(),
51 | ];
52 | if(Yii::$app->params['useRateLimiter']){
53 | $behaviors['rateLimiter']['enableRateLimitHeaders'] = false;
54 | }
55 | return $behaviors;
56 | }
57 | }
--------------------------------------------------------------------------------
/migrations/m141022_115823_create_user_table.php:
--------------------------------------------------------------------------------
1 | db->driverName === 'mysql') {
14 | $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
15 | }
16 |
17 | $this->createTable('{{%user}}', [
18 | 'id' => $this->primaryKey(),
19 | 'username' => $this->string()->notNull()->unique(),
20 | 'email' => $this->string()->notNull()->unique(),
21 | 'password_hash' => $this->string()->notNull(),
22 | 'status' => $this->smallInteger()->notNull(),
23 | 'auth_key' => $this->string(32)->notNull(),
24 | 'password_reset_token' => $this->string()->unique(),
25 | 'account_activation_token' => $this->string()->unique(),
26 | 'created_at' => $this->integer()->notNull(),
27 | 'updated_at' => $this->integer()->notNull(),
28 | ], $tableOptions);
29 | }
30 |
31 | public function down()
32 | {
33 | $this->dropTable('{{%user}}');
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/migrations/m180221_085153_create_post_table.php:
--------------------------------------------------------------------------------
1 | createTable('post', [
22 | 'id' => $this->primaryKey(),
23 | 'title' => $this->string(),
24 | 'body' => $this->text(),
25 | ]);
26 | }
27 |
28 | /**
29 | * {@inheritdoc}
30 | */
31 | public function safeDown()
32 | {
33 | $this->dropTable('post');
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/migrations/m200609_112354_create_access_token_table.php:
--------------------------------------------------------------------------------
1 | createTable('{{%access_token}}', [
16 | 'id' => $this->primaryKey(),
17 | 'user_id' => $this->integer(),
18 | 'consumer' => $this->string(),
19 | 'token' => $this->string(32)->notNull()->unique(),
20 | 'access_given' => $this->json(),
21 | 'used_at' => $this->integer(),
22 | 'expire_at' => $this->integer(),
23 | 'created_at' => $this->integer()->notNull(),
24 | 'updated_at' => $this->integer()->notNull(),
25 | ]);
26 | }
27 |
28 | /**
29 | * {@inheritdoc}
30 | */
31 | public function safeDown()
32 | {
33 | $this->dropTable('{{%access_token}}');
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/models/AccessToken.php:
--------------------------------------------------------------------------------
1 | auth_key = Yii::$app->security->generateRandomString();
57 | $accessToken = new AccessToken();
58 | $accessToken->user_id = $user->id;
59 | $accessToken->consumer = $user->consumer ?? $accessToken->defaultConsumer;
60 | $accessToken->access_given = $user->access_given ?? $accessToken->defaultAccessGiven;
61 | $accessToken->token = $user->auth_key;
62 | $accessToken->used_at = strtotime("now");
63 | $accessToken->expire_at = $accessToken->tokenExpiration + $accessToken->used_at;
64 | $accessToken->save();
65 | }
66 |
67 | /**
68 | * Make all user token based on any user_id expired
69 | *
70 | * @param int @userId
71 | * @return nothing
72 | */
73 | public static function makeAllUserTokenExpiredByUserId($userId){
74 | AccessToken::updateAll(['expire_at' => strtotime("now")], ['user_id' => $userId]);
75 | }
76 |
77 | /**
78 | * Expire any access_token
79 | *
80 | * @return bool
81 | */
82 | public function expireThisToken(){
83 | $this->expire_at = strtotime("now");
84 | return $this->save();
85 | }
86 |
87 | /**
88 | * {@inheritdoc}
89 | */
90 | public function rules()
91 | {
92 | return [
93 | [['user_id', 'used_at', 'expire_at', 'created_at', 'updated_at'], 'integer'],
94 | [['token'], 'required'],
95 | [['access_given'], 'string'],
96 | [['consumer'], 'string', 'max' => 255],
97 | [['token'], 'string', 'max' => 32],
98 | [['token'], 'unique'],
99 | ];
100 | }
101 |
102 | /**
103 | * {@inheritdoc}
104 | */
105 | public function attributeLabels()
106 | {
107 | return [
108 | 'id' => 'ID',
109 | 'user_id' => 'User ID',
110 | 'consumer' => 'Consumer',
111 | 'token' => 'Token',
112 | 'access_given' => 'Access Given',
113 | 'used_at' => 'Used At',
114 | 'expire_at' => 'Expire At',
115 | 'created_at' => 'Created At',
116 | 'updated_at' => 'Updated At',
117 | ];
118 | }
119 |
120 | public function behaviors()
121 | {
122 | return [
123 | TimestampBehavior::class,
124 | ];
125 | }
126 | }
--------------------------------------------------------------------------------
/models/Post.php:
--------------------------------------------------------------------------------
1 | 'Active',
29 | self::STATUS_INACTIVE => 'Inactive',
30 | self::STATUS_DELETED => 'Deleted'
31 | ];
32 |
33 | /**
34 | * Returns the validation rules for attributes.
35 | *
36 | * @return array
37 | */
38 | public function rules()
39 | {
40 | return [
41 | ['username', 'filter', 'filter' => 'trim'],
42 | ['username', 'required'],
43 | ['username', 'string', 'min' => 2, 'max' => 255],
44 | ['username', 'unique'],
45 | [['consumer', 'access_given'], 'safe'],
46 | ['email', 'filter', 'filter' => 'trim'],
47 | ['email', 'required'],
48 | ['email', 'email'],
49 | ['email', 'string', 'max' => 255],
50 | ['status', 'required'],
51 | ];
52 | }
53 |
54 | /**
55 | * Returns a list of behaviors that this component should behave as.
56 | *
57 | * @return array
58 | */
59 | public function behaviors()
60 | {
61 | return [
62 | TimestampBehavior::className(),
63 | ];
64 | }
65 |
66 | /**
67 | * Returns the attribute labels.
68 | *
69 | * @return array
70 | */
71 | public function attributeLabels()
72 | {
73 | return [
74 | 'id' => Yii::t('app', 'ID'),
75 | 'username' => Yii::t('app', 'Username'),
76 | 'password' => Yii::t('app', 'Password'),
77 | 'email' => Yii::t('app', 'Email'),
78 | 'status' => Yii::t('app', 'Status'),
79 | 'created_at' => Yii::t('app', 'Created At'),
80 | 'updated_at' => Yii::t('app', 'Updated At'),
81 | ];
82 | }
83 |
84 | //------------------------------------------------------------------------------------------------//
85 | // USER FINDERS
86 | //------------------------------------------------------------------------------------------------//
87 |
88 | /**
89 | * Finds user by username.
90 | *
91 | * @param string $username
92 | * @return static|null
93 | */
94 | public static function findByUsername($username)
95 | {
96 | return static::findOne(['username' => $username]);
97 | }
98 |
99 | /**
100 | * Finds user by email.
101 | *
102 | * @param string $email
103 | * @return static|null
104 | */
105 | public static function findByEmail($email)
106 | {
107 | return static::findOne(['email' => $email]);
108 | }
109 |
110 | /**
111 | * Finds user by password reset token.
112 | *
113 | * @param string $token Password reset token.
114 | * @return null|static
115 | */
116 | public static function findByPasswordResetToken($token)
117 | {
118 | if (!static::isPasswordResetTokenValid($token)) {
119 | return null;
120 | }
121 |
122 | return static::findOne([
123 | 'password_reset_token' => $token,
124 | 'status' => User::STATUS_ACTIVE,
125 | ]);
126 | }
127 |
128 | /**
129 | * Finds user by account activation token.
130 | *
131 | * @param string $token Account activation token.
132 | * @return static|null
133 | */
134 | public static function findByAccountActivationToken($token)
135 | {
136 | return static::findOne([
137 | 'account_activation_token' => $token,
138 | 'status' => User::STATUS_INACTIVE,
139 | ]);
140 | }
141 |
142 |
143 | //------------------------------------------------------------------------------------------------//
144 | // HELPERS
145 | //------------------------------------------------------------------------------------------------//
146 |
147 | /**
148 | * Returns the user status in nice format.
149 | *
150 | * @param integer $status Status integer value.
151 | * @return string Nicely formatted status.
152 | */
153 | public function getStatusName($status)
154 | {
155 | return $this->statusList[$status];
156 | }
157 |
158 | /**
159 | * Generates new password reset token.
160 | */
161 | public function generatePasswordResetToken()
162 | {
163 | $this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time();
164 | }
165 |
166 | /**
167 | * Removes password reset token.
168 | */
169 | public function removePasswordResetToken()
170 | {
171 | $this->password_reset_token = null;
172 | }
173 |
174 | /**
175 | * Finds out if password reset token is valid.
176 | *
177 | * @param string $token Password reset token.
178 | * @return bool
179 | */
180 | public static function isPasswordResetTokenValid($token)
181 | {
182 | if (empty($token)) {
183 | return false;
184 | }
185 |
186 | $timestamp = (int) substr($token, strrpos($token, '_') + 1);
187 | $expire = Yii::$app->params['user.passwordResetTokenExpire'];
188 | return $timestamp + $expire >= time();
189 | }
190 |
191 | /**
192 | * Generates new account activation token.
193 | */
194 | public function generateAccountActivationToken()
195 | {
196 | $this->account_activation_token = Yii::$app->security->generateRandomString() . '_' . time();
197 | }
198 |
199 | /**
200 | * Removes account activation token.
201 | */
202 | public function removeAccountActivationToken()
203 | {
204 | $this->account_activation_token = null;
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/models/UserIdentity.php:
--------------------------------------------------------------------------------
1 | $id, 'status' => User::STATUS_ACTIVE]);
54 | }
55 |
56 | /**
57 | * Finds an identity by the given access token.
58 | *
59 | * @param mixed $token
60 | * @param null $type
61 | * @return void|IdentityInterface
62 | *
63 | * @throws NotSupportedException
64 | */
65 | public static function findIdentityByAccessToken($token, $type = null)
66 | {
67 | $accessToken = AccessToken::find()->where(['token' => $token])->andWhere(['>', 'expire_at', strtotime('now')])->one();
68 | if(!$accessToken) return $accessToken;
69 | return User::findOne(['id' => $accessToken->user_id]);
70 | // return User::findOne(['auth_key' => $token, 'status' => User::STATUS_ACTIVE]);
71 | }
72 |
73 | /**
74 | * Returns an ID that can uniquely identify a user identity.
75 | *
76 | * @return int|mixed|string
77 | */
78 | public function getId()
79 | {
80 | return $this->getPrimaryKey();
81 | }
82 |
83 | /**
84 | * Returns a key that can be used to check the validity of a given
85 | * identity ID. The key should be unique for each individual user, and
86 | * should be persistent so that it can be used to check the validity of
87 | * the user identity. The space of such keys should be big enough to defeat
88 | * potential identity attacks.
89 | *
90 | * @return string
91 | */
92 | public function getAuthKey()
93 | {
94 | return $this->auth_key;
95 | }
96 |
97 | /**
98 | * Validates the given auth key.
99 | *
100 | * @param string $authKey The given auth key.
101 | * @return boolean Whether the given auth key is valid.
102 | */
103 | public function validateAuthKey($authKey)
104 | {
105 | return $this->getAuthKey() === $authKey;
106 | }
107 |
108 | //------------------------------------------------------------------------------------------------//
109 | // IMPORTANT IDENTITY HELPERS
110 | //------------------------------------------------------------------------------------------------//
111 |
112 | /**
113 | * Generates "remember me" authentication key.
114 | */
115 | public function generateAuthKey()
116 | {
117 | $this->auth_key = Yii::$app->security->generateRandomString();
118 | AccessToken::generateAuthKey($this);
119 | }
120 |
121 | /**
122 | * Validates password.
123 | *
124 | * @param string $password
125 | * @return bool
126 | *
127 | * @throws \yii\base\InvalidConfigException
128 | */
129 | public function validatePassword($password)
130 | {
131 | return Yii::$app->security->validatePassword($password, $this->password_hash);
132 | }
133 |
134 | /**
135 | * Generates password hash from password and sets it to the model.
136 | *
137 | * @param string $password
138 | *
139 | * @throws \yii\base\Exception
140 | * @throws \yii\base\InvalidConfigException
141 | */
142 | public function setPassword($password)
143 | {
144 | $this->password_hash = Yii::$app->security->generatePasswordHash($password);
145 | }
146 |
147 | }
148 |
--------------------------------------------------------------------------------
/modules/v1/controllers/DefaultController.php:
--------------------------------------------------------------------------------
1 | Status::STATUS_OK,
19 | 'message' => "You may customize this page by editing the following file:". __FILE__,
20 | 'data' => ''
21 | ];
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/modules/v1/controllers/PostController.php:
--------------------------------------------------------------------------------
1 | run();
--------------------------------------------------------------------------------
/yii:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | run();
21 | exit($exitCode);
22 |
--------------------------------------------------------------------------------
/yii.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | rem -------------------------------------------------------------
4 | rem Yii command line bootstrap script for Windows.
5 | rem
6 | rem @author Qiang Xue
7 | rem @link http://www.yiiframework.com/
8 | rem @copyright Copyright (c) 2008 Yii Software LLC
9 | rem @license http://www.yiiframework.com/license/
10 | rem -------------------------------------------------------------
11 |
12 | @setlocal
13 |
14 | set YII_PATH=%~dp0
15 |
16 | if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
17 |
18 | "%PHP_COMMAND%" "%YII_PATH%yii" %*
19 |
20 | @endlocal
21 |
--------------------------------------------------------------------------------