1
|
|
|
<?php |
2
|
|
|
namespace Codeception\Module; |
3
|
|
|
|
4
|
|
|
use Codeception\Configuration; |
5
|
|
|
use Codeception\Exception\ModuleConfigException; |
6
|
|
|
use Codeception\Exception\ModuleException; |
7
|
|
|
use Codeception\Lib\Connector\Yii2 as Yii2Connector; |
8
|
|
|
use Codeception\Lib\Framework; |
9
|
|
|
use Codeception\Lib\Interfaces\ActiveRecord; |
10
|
|
|
use Codeception\Lib\Interfaces\PartedModule; |
11
|
|
|
use Codeception\Lib\Notification; |
12
|
|
|
use Codeception\TestInterface; |
13
|
|
|
use Yii; |
14
|
|
|
use yii\db\ActiveRecordInterface; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* This module provides integration with [Yii framework](http://www.yiiframework.com/) (2.0). |
18
|
|
|
* It initializes Yii framework in test environment and provides actions for functional testing. |
19
|
|
|
* |
20
|
|
|
* ## Config |
21
|
|
|
* |
22
|
|
|
* * `configFile` *required* - the path to the application config file. File should be configured for test environment and return configuration array. |
23
|
|
|
* * `entryUrl` - initial application url (default: http://localhost/index-test.php). |
24
|
|
|
* * `entryScript` - front script title (like: index-test.php). If not set - taken from entryUrl. |
25
|
|
|
* * `cleanup` - (default: true) wrap all database connection inside a transaction and roll it back after the test. Should be disabled for acceptance testing.. |
26
|
|
|
* |
27
|
|
|
* You can use this module by setting params in your functional.suite.yml: |
28
|
|
|
* |
29
|
|
|
* ```yaml |
30
|
|
|
* class_name: FunctionalTester |
31
|
|
|
* modules: |
32
|
|
|
* enabled: |
33
|
|
|
* - Yii2: |
34
|
|
|
* configFile: '/path/to/config.php' |
35
|
|
|
* ``` |
36
|
|
|
* |
37
|
|
|
* ### Parts |
38
|
|
|
* |
39
|
|
|
* * `init` - use module only for initialization (for acceptance tests). |
40
|
|
|
* * `orm` - include only haveRecord/grabRecord/seeRecord/dontSeeRecord actions |
41
|
|
|
* |
42
|
|
|
* ### Example (`functional.suite.yml`) |
43
|
|
|
* |
44
|
|
|
* ```yml |
45
|
|
|
* class_name: FunctionalTester |
46
|
|
|
* modules: |
47
|
|
|
* enabled: |
48
|
|
|
* - Yii2: |
49
|
|
|
* configFile: 'config/test.php' |
50
|
|
|
* ``` |
51
|
|
|
* |
52
|
|
|
* ### Example (`unit.suite.yml`) |
53
|
|
|
* |
54
|
|
|
* ```yml |
55
|
|
|
* class_name: UnitTester |
56
|
|
|
* modules: |
57
|
|
|
* enabled: |
58
|
|
|
* - Asserts |
59
|
|
|
* - Yii2: |
60
|
|
|
* configFile: 'config/test.php' |
61
|
|
|
* part: init |
62
|
|
|
* ``` |
63
|
|
|
* |
64
|
|
|
* ### Example (`acceptance.suite.yml`) |
65
|
|
|
* |
66
|
|
|
* ```yml |
67
|
|
|
* class_name: AcceptanceTester |
68
|
|
|
* modules: |
69
|
|
|
* enabled: |
70
|
|
|
* - WebDriver: |
71
|
|
|
* url: http://127.0.0.1:8080/ |
72
|
|
|
* browser: firefox |
73
|
|
|
* - Yii2: |
74
|
|
|
* configFile: 'config/test.php' |
75
|
|
|
* part: ORM # allow to use AR methods |
76
|
|
|
* cleanup: false # don't wrap test in transaction |
77
|
|
|
* entryScript: index-test.php |
78
|
|
|
* ``` |
79
|
|
|
* |
80
|
|
|
* ## Status |
81
|
|
|
* |
82
|
|
|
* Maintainer: **samdark** |
83
|
|
|
* Stability: **stable** |
84
|
|
|
* |
85
|
|
|
*/ |
86
|
|
|
class Yii2 extends Framework implements ActiveRecord, PartedModule |
87
|
|
|
{ |
88
|
|
|
/** |
89
|
|
|
* Application config file must be set. |
90
|
|
|
* @var array |
91
|
|
|
*/ |
92
|
|
|
protected $config = [ |
93
|
|
|
'cleanup' => false, |
94
|
|
|
'entryScript' => '', |
95
|
|
|
'entryUrl' => 'http://localhost/index-test.php', |
96
|
|
|
]; |
97
|
|
|
|
98
|
|
|
protected $requiredFields = ['configFile']; |
99
|
|
|
protected $transaction; |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* @var \yii\base\Application |
103
|
|
|
*/ |
104
|
|
|
public $app; |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* @var Yii2Connector\FixturesStore[] |
108
|
|
|
*/ |
109
|
|
|
public $loadedFixtures = []; |
110
|
|
|
|
111
|
|
|
public function _initialize() |
112
|
|
|
{ |
113
|
|
|
if (!is_file(codecept_root_dir() . $this->config['configFile'])) { |
114
|
|
|
throw new ModuleConfigException( |
115
|
|
|
__CLASS__, |
116
|
|
|
"The application config file does not exist: " . codecept_root_dir() . $this->config['configFile'] |
117
|
|
|
); |
118
|
|
|
} |
119
|
|
|
$this->defineConstants(); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
public function _before(TestInterface $test) |
123
|
|
|
{ |
124
|
|
|
$entryUrl = $this->config['entryUrl']; |
125
|
|
|
$entryFile = $this->config['entryScript'] ?: basename($entryUrl); |
126
|
|
|
$entryScript = $this->config['entryScript'] ?: parse_url($entryUrl, PHP_URL_PATH); |
127
|
|
|
|
128
|
|
|
$this->client = new Yii2Connector(); |
129
|
|
|
$this->client->defaultServerVars = [ |
130
|
|
|
'SCRIPT_FILENAME' => $entryFile, |
131
|
|
|
'SCRIPT_NAME' => $entryScript, |
132
|
|
|
'SERVER_NAME' => parse_url($entryUrl, PHP_URL_HOST), |
133
|
|
|
'SERVER_PORT' => parse_url($entryUrl, PHP_URL_PORT) ?: '80', |
134
|
|
|
]; |
135
|
|
|
$this->client->defaultServerVars['HTTPS'] = parse_url($entryUrl, PHP_URL_SCHEME) === 'https'; |
136
|
|
|
$this->client->restoreServerVars(); |
137
|
|
|
$this->client->configFile = Configuration::projectDir() . $this->config['configFile']; |
138
|
|
|
$this->app = $this->client->getApplication(); |
|
|
|
|
139
|
|
|
|
140
|
|
|
if ($this->config['cleanup'] && $this->app->has('db')) { |
141
|
|
|
$this->transaction = $this->app->db->beginTransaction(); |
142
|
|
|
} |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
public function _after(\Codeception\TestInterface $test) |
|
|
|
|
146
|
|
|
{ |
147
|
|
|
$_SESSION = []; |
148
|
|
|
$_FILES = []; |
149
|
|
|
$_GET = []; |
150
|
|
|
$_POST = []; |
151
|
|
|
$_COOKIE = []; |
152
|
|
|
$_REQUEST = []; |
153
|
|
|
|
154
|
|
|
foreach ($this->loadedFixtures as $fixture) { |
155
|
|
|
$fixture->unloadFixtures(); |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
if ($this->transaction && $this->config['cleanup']) { |
159
|
|
|
$this->transaction->rollback(); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
$this->client->resetPersistentVars(); |
|
|
|
|
163
|
|
|
|
164
|
|
|
if (\Yii::$app->has('session', true)) { |
165
|
|
|
\Yii::$app->session->close(); |
166
|
|
|
} |
167
|
|
|
parent::_after($test); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
public function _parts() |
171
|
|
|
{ |
172
|
|
|
return ['orm', 'init', 'fixtures', 'email']; |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* Authorizes user on a site without submitting login form. |
177
|
|
|
* Use it for fast pragmatic authorization in functional tests. |
178
|
|
|
* |
179
|
|
|
* ```php |
180
|
|
|
* <?php |
181
|
|
|
* // User is found by id |
182
|
|
|
* $I->amLoggedInAs(1); |
183
|
|
|
* |
184
|
|
|
* // User object is passed as parameter |
185
|
|
|
* $admin = \app\models\User::findByUsername('admin'); |
186
|
|
|
* $I->amLoggedInAs($admin); |
187
|
|
|
* ``` |
188
|
|
|
* Requires `user` component to be enabled and configured. |
189
|
|
|
* |
190
|
|
|
* @param $user |
191
|
|
|
* @throws ModuleException |
192
|
|
|
*/ |
193
|
|
|
public function amLoggedInAs($user) |
194
|
|
|
{ |
195
|
|
|
if (!Yii::$app->has('user')) { |
196
|
|
|
throw new ModuleException($this, 'User component is not loaded'); |
197
|
|
|
} |
198
|
|
|
if ($user instanceof \yii\web\IdentityInterface) { |
|
|
|
|
199
|
|
|
$identity = $user; |
200
|
|
|
} else { |
201
|
|
|
// class name implementing IdentityInterface |
202
|
|
|
$identityClass = Yii::$app->user->identityClass; |
203
|
|
|
$identity = call_user_func([$identityClass, 'findIdentity'], $user); |
204
|
|
|
} |
205
|
|
|
Yii::$app->user->login($identity); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Creates and loads fixtures from a config. |
210
|
|
|
* Signature is the same as for `fixtures()` method of `yii\test\FixtureTrait` |
211
|
|
|
* |
212
|
|
|
* ```php |
213
|
|
|
* <?php |
214
|
|
|
* $I->haveFixtures(, |
215
|
|
|
* 'posts' => PostsFixture::className(), |
216
|
|
|
* 'user' => [ |
217
|
|
|
* 'class' => UserFixture::className(), |
218
|
|
|
* 'dataFile' => '@tests/_data/models/user.php' |
219
|
|
|
* ], |
220
|
|
|
* ); |
221
|
|
|
* ``` |
222
|
|
|
* |
223
|
|
|
* @param $fixtures |
224
|
|
|
* @part fixtures |
225
|
|
|
*/ |
226
|
|
|
public function haveFixtures($fixtures) |
227
|
|
|
{ |
228
|
|
|
$fixturesStore = new Yii2Connector\FixturesStore($fixtures); |
229
|
|
|
$fixturesStore->loadFixtures(); |
230
|
|
|
$this->loadedFixtures[] = $fixturesStore; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
/** |
234
|
|
|
* Returns all loaded fixtures. |
235
|
|
|
* Array of fixture instances |
236
|
|
|
* |
237
|
|
|
* @part fixtures |
238
|
|
|
* @return array |
239
|
|
|
*/ |
240
|
|
|
public function grabFixtures() |
241
|
|
|
{ |
242
|
|
|
return call_user_func_array('array_merge', |
243
|
|
|
array_map( // merge all fixtures from all fixture stores |
244
|
|
|
function ($fixturesStore) { |
245
|
|
|
return $fixturesStore->getFixtures(); |
246
|
|
|
}, |
247
|
|
|
$this->loadedFixtures |
248
|
|
|
) |
249
|
|
|
); |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
/** |
253
|
|
|
* Gets a fixture by name. |
254
|
|
|
* Returns a Fixture instance. If a fixture is an instance of `\yii\test\BaseActiveFixture` a second parameter |
255
|
|
|
* can be used to return a specific model: |
256
|
|
|
* |
257
|
|
|
* ```php |
258
|
|
|
* <?php |
259
|
|
|
* $I->haveFixtures(['users' => UserFixture::className()]); |
260
|
|
|
* |
261
|
|
|
* $users = $I->grabFixture('users'); |
262
|
|
|
* |
263
|
|
|
* // get first user by key, if a fixture is instance of ActiveFixture |
264
|
|
|
* $user = $I->grabFixture('users', 'user1'); |
265
|
|
|
* ``` |
266
|
|
|
* |
267
|
|
|
* @param $name |
268
|
|
|
* @return mixed |
269
|
|
|
* @throws ModuleException if a fixture is not found |
270
|
|
|
* @part fixtures |
271
|
|
|
*/ |
272
|
|
|
public function grabFixture($name, $index = null) |
273
|
|
|
{ |
274
|
|
|
$fixtures = $this->grabFixtures(); |
275
|
|
|
if (!isset($fixtures[$name])) { |
276
|
|
|
throw new ModuleException($this, "Fixture $name is not loaded"); |
277
|
|
|
} |
278
|
|
|
$fixture = $fixtures[$name]; |
279
|
|
|
if ($index === null) { |
280
|
|
|
return $fixture; |
281
|
|
|
} |
282
|
|
|
if ($fixture instanceof \yii\test\BaseActiveFixture) { |
|
|
|
|
283
|
|
|
return $fixture->getModel($index); |
284
|
|
|
} |
285
|
|
|
throw new ModuleException($this, "Fixture $name is not an instance of ActiveFixture and can't be loaded with scond parameter"); |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
/** |
289
|
|
|
* Inserts record into the database. |
290
|
|
|
* |
291
|
|
|
* ``` php |
292
|
|
|
* <?php |
293
|
|
|
* $user_id = $I->haveRecord('app\models\User', array('name' => 'Davert')); |
294
|
|
|
* ?> |
295
|
|
|
* ``` |
296
|
|
|
* |
297
|
|
|
* @param $model |
298
|
|
|
* @param array $attributes |
299
|
|
|
* @return mixed |
300
|
|
|
* @part orm |
301
|
|
|
*/ |
302
|
|
|
public function haveRecord($model, $attributes = []) |
303
|
|
|
{ |
304
|
|
|
/** @var $record \yii\db\ActiveRecord * */ |
305
|
|
|
$record = $this->getModelRecord($model); |
306
|
|
|
$record->setAttributes($attributes, false); |
307
|
|
|
$res = $record->save(false); |
308
|
|
|
if (!$res) { |
309
|
|
|
$this->fail("Record $model was not saved"); |
310
|
|
|
} |
311
|
|
|
return $record->primaryKey; |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
/** |
315
|
|
|
* Checks that record exists in database. |
316
|
|
|
* |
317
|
|
|
* ``` php |
318
|
|
|
* $I->seeRecord('app\models\User', array('name' => 'davert')); |
319
|
|
|
* ``` |
320
|
|
|
* |
321
|
|
|
* @param $model |
322
|
|
|
* @param array $attributes |
323
|
|
|
* @part orm |
324
|
|
|
*/ |
325
|
|
View Code Duplication |
public function seeRecord($model, $attributes = []) |
|
|
|
|
326
|
|
|
{ |
327
|
|
|
$record = $this->findRecord($model, $attributes); |
328
|
|
|
if (!$record) { |
329
|
|
|
$this->fail("Couldn't find $model with " . json_encode($attributes)); |
330
|
|
|
} |
331
|
|
|
$this->debugSection($model, json_encode($record)); |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
/** |
335
|
|
|
* Checks that record does not exist in database. |
336
|
|
|
* |
337
|
|
|
* ``` php |
338
|
|
|
* $I->dontSeeRecord('app\models\User', array('name' => 'davert')); |
339
|
|
|
* ``` |
340
|
|
|
* |
341
|
|
|
* @param $model |
342
|
|
|
* @param array $attributes |
343
|
|
|
* @part orm |
344
|
|
|
*/ |
345
|
|
View Code Duplication |
public function dontSeeRecord($model, $attributes = []) |
|
|
|
|
346
|
|
|
{ |
347
|
|
|
$record = $this->findRecord($model, $attributes); |
348
|
|
|
$this->debugSection($model, json_encode($record)); |
349
|
|
|
if ($record) { |
350
|
|
|
$this->fail("Unexpectedly managed to find $model with " . json_encode($attributes)); |
351
|
|
|
} |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
/** |
355
|
|
|
* Retrieves record from database |
356
|
|
|
* |
357
|
|
|
* ``` php |
358
|
|
|
* $category = $I->grabRecord('app\models\User', array('name' => 'davert')); |
359
|
|
|
* ``` |
360
|
|
|
* |
361
|
|
|
* @param $model |
362
|
|
|
* @param array $attributes |
363
|
|
|
* @return mixed |
364
|
|
|
* @part orm |
365
|
|
|
*/ |
366
|
|
|
public function grabRecord($model, $attributes = []) |
367
|
|
|
{ |
368
|
|
|
return $this->findRecord($model, $attributes); |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
protected function findRecord($model, $attributes = []) |
372
|
|
|
{ |
373
|
|
|
$this->getModelRecord($model); |
374
|
|
|
return call_user_func([$model, 'find']) |
375
|
|
|
->where($attributes) |
376
|
|
|
->one(); |
377
|
|
|
} |
378
|
|
|
|
379
|
|
View Code Duplication |
protected function getModelRecord($model) |
|
|
|
|
380
|
|
|
{ |
381
|
|
|
if (!class_exists($model)) { |
382
|
|
|
throw new \RuntimeException("Model $model does not exist"); |
383
|
|
|
} |
384
|
|
|
$record = new $model; |
385
|
|
|
if (!$record instanceof ActiveRecordInterface) { |
|
|
|
|
386
|
|
|
throw new \RuntimeException("Model $model is not implement interface \\yii\\db\\ActiveRecordInterface"); |
387
|
|
|
} |
388
|
|
|
return $record; |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
/** |
392
|
|
|
* Converting $page to valid Yii 2 URL |
393
|
|
|
* |
394
|
|
|
* Allows input like: |
395
|
|
|
* |
396
|
|
|
* ```php |
397
|
|
|
* <?php |
398
|
|
|
* $I->amOnPage(['site/view','page'=>'about']); |
399
|
|
|
* $I->amOnPage('index-test.php?site/index'); |
400
|
|
|
* $I->amOnPage('http://localhost/index-test.php?site/index'); |
401
|
|
|
* ``` |
402
|
|
|
* |
403
|
|
|
* @param $page string|array parameter for \yii\web\UrlManager::createUrl() |
404
|
|
|
*/ |
405
|
|
|
public function amOnPage($page) |
406
|
|
|
{ |
407
|
|
|
if (is_array($page)) { |
408
|
|
|
$page = Yii::$app->getUrlManager()->createUrl($page); |
409
|
|
|
} |
410
|
|
|
parent::amOnPage($page); |
411
|
|
|
} |
412
|
|
|
|
413
|
|
|
/** |
414
|
|
|
* Similar to amOnPage but accepts route as first argument and params as second |
415
|
|
|
* |
416
|
|
|
* ``` |
417
|
|
|
* $I->amOnRoute('site/view', ['page' => 'about']); |
418
|
|
|
* ``` |
419
|
|
|
* |
420
|
|
|
*/ |
421
|
|
|
public function amOnRoute($route, array $params = []) |
422
|
|
|
{ |
423
|
|
|
array_unshift($params, $route); |
424
|
|
|
$this->amOnPage($params); |
425
|
|
|
} |
426
|
|
|
|
427
|
|
|
/** |
428
|
|
|
* Gets a component from Yii container. Throws exception if component is not available |
429
|
|
|
* |
430
|
|
|
* ```php |
431
|
|
|
* <?php |
432
|
|
|
* $mailer = $I->grabComponent('mailer'); |
433
|
|
|
* ``` |
434
|
|
|
* |
435
|
|
|
* @param $component |
436
|
|
|
* @return mixed |
437
|
|
|
* @throws ModuleException |
438
|
|
|
*/ |
439
|
|
|
public function grabComponent($component) |
440
|
|
|
{ |
441
|
|
|
if (!Yii::$app->has($component)) { |
442
|
|
|
throw new ModuleException($this, "Component $component is not avilable in current application"); |
443
|
|
|
} |
444
|
|
|
return Yii::$app->get($component); |
445
|
|
|
} |
446
|
|
|
|
447
|
|
|
/** |
448
|
|
|
* Checks that email is sent. |
449
|
|
|
* |
450
|
|
|
* ```php |
451
|
|
|
* <?php |
452
|
|
|
* // check that at least 1 email was sent |
453
|
|
|
* $I->seeEmailIsSent(); |
454
|
|
|
* |
455
|
|
|
* // check that only 3 emails were sent |
456
|
|
|
* $I->seeEmailIsSent(3); |
457
|
|
|
* ``` |
458
|
|
|
* |
459
|
|
|
* @param int $num |
460
|
|
|
* @throws ModuleException |
461
|
|
|
* @part email |
462
|
|
|
*/ |
463
|
|
|
public function seeEmailIsSent($num = null) |
464
|
|
|
{ |
465
|
|
|
if ($num === null) { |
466
|
|
|
$this->assertNotEmpty($this->grabSentEmails(), 'emails were sent'); |
467
|
|
|
return; |
468
|
|
|
} |
469
|
|
|
$this->assertEquals($num, count($this->grabSentEmails()), 'number of sent emails is equal to ' . $num); |
470
|
|
|
} |
471
|
|
|
|
472
|
|
|
/** |
473
|
|
|
* Checks that no email was sent |
474
|
|
|
* |
475
|
|
|
* @part email |
476
|
|
|
*/ |
477
|
|
|
public function dontSeeEmailIsSent() |
478
|
|
|
{ |
479
|
|
|
$this->seeEmailIsSent(0); |
480
|
|
|
} |
481
|
|
|
|
482
|
|
|
/** |
483
|
|
|
* Returns array of all sent email messages. |
484
|
|
|
* Each message implements `yii\mail\Message` interface. |
485
|
|
|
* Useful to perform additional checks using `Asserts` module: |
486
|
|
|
* |
487
|
|
|
* ```php |
488
|
|
|
* <?php |
489
|
|
|
* $I->seeEmailIsSent(); |
490
|
|
|
* $messages = $I->grabSentEmails(); |
491
|
|
|
* $I->assertEquals('admin@site,com', $messages[0]->getTo()); |
492
|
|
|
* ``` |
493
|
|
|
* |
494
|
|
|
* @part email |
495
|
|
|
* @return array |
496
|
|
|
* @throws ModuleException |
497
|
|
|
*/ |
498
|
|
|
public function grabSentEmails() |
499
|
|
|
{ |
500
|
|
|
$mailer = $this->grabComponent('mailer'); |
501
|
|
|
if (!$mailer instanceof Yii2Connector\TestMailer) { |
502
|
|
|
throw new ModuleException($this, "Mailer module is not mocked, can't test emails"); |
503
|
|
|
} |
504
|
|
|
return $mailer->getSentMessages(); |
505
|
|
|
} |
506
|
|
|
|
507
|
|
|
/** |
508
|
|
|
* Returns last sent email: |
509
|
|
|
* |
510
|
|
|
* ```php |
511
|
|
|
* <?php |
512
|
|
|
* $I->seeEmailIsSent(); |
513
|
|
|
* $message = $I->grabLastSentEmail(); |
514
|
|
|
* $I->assertEquals('admin@site,com', $message->getTo()); |
515
|
|
|
* ``` |
516
|
|
|
* @part email |
517
|
|
|
*/ |
518
|
|
|
public function grabLastSentEmail() |
519
|
|
|
{ |
520
|
|
|
$this->seeEmailIsSent(); |
521
|
|
|
$messages = $this->grabSentEmails(); |
522
|
|
|
return end($messages); |
523
|
|
|
} |
524
|
|
|
|
525
|
|
|
/** |
526
|
|
|
* Getting domain regex from rule host template |
527
|
|
|
* |
528
|
|
|
* @param string $template |
529
|
|
|
* @return string |
530
|
|
|
*/ |
531
|
|
|
private function getDomainRegex($template) |
532
|
|
|
{ |
533
|
|
|
if (preg_match('#https?://(.*)#', $template, $matches)) { |
534
|
|
|
$template = $matches[1]; |
535
|
|
|
} |
536
|
|
|
$parameters = []; |
537
|
|
|
if (strpos($template, '<') !== false) { |
538
|
|
|
$template = preg_replace_callback( |
539
|
|
|
'/<(?:\w+):?([^>]+)?>/u', |
540
|
|
|
function ($matches) use (&$parameters) { |
541
|
|
|
$key = '#' . count($parameters) . '#'; |
542
|
|
|
$parameters[$key] = isset($matches[1]) ? $matches[1] : '\w+'; |
543
|
|
|
return $key; |
544
|
|
|
}, |
545
|
|
|
$template |
546
|
|
|
); |
547
|
|
|
} |
548
|
|
|
$template = preg_quote($template); |
549
|
|
|
$template = strtr($template, $parameters); |
550
|
|
|
return '/^' . $template . '$/u'; |
551
|
|
|
} |
552
|
|
|
|
553
|
|
|
/** |
554
|
|
|
* Returns a list of regex patterns for recognized domain names |
555
|
|
|
* |
556
|
|
|
* @return array |
557
|
|
|
*/ |
558
|
|
|
public function getInternalDomains() |
559
|
|
|
{ |
560
|
|
|
$domains = [$this->getDomainRegex(Yii::$app->urlManager->hostInfo)]; |
561
|
|
|
|
562
|
|
|
if (Yii::$app->urlManager->enablePrettyUrl) { |
563
|
|
|
foreach (Yii::$app->urlManager->rules as $rule) { |
564
|
|
|
/** @var \yii\web\UrlRule $rule */ |
565
|
|
|
if (isset($rule->host)) { |
566
|
|
|
$domains[] = $this->getDomainRegex($rule->host); |
567
|
|
|
} |
568
|
|
|
} |
569
|
|
|
} |
570
|
|
|
return array_unique($domains); |
571
|
|
|
} |
572
|
|
|
|
573
|
|
|
private function defineConstants() |
574
|
|
|
{ |
575
|
|
|
defined('YII_DEBUG') or define('YII_DEBUG', true); |
576
|
|
|
defined('YII_ENV') or define('YII_ENV', 'test'); |
577
|
|
|
defined('YII_ENABLE_ERROR_HANDLER') or define('YII_ENABLE_ERROR_HANDLER', false); |
578
|
|
|
|
579
|
|
|
if (YII_ENV !== 'test') { |
580
|
|
|
Notification::warning("YII_ENV is not set to `test`, please add \n\n`define(\'YII_ENV\', \'test\');`\n\nto bootstrap file", 'Yii Framework'); |
581
|
|
|
} |
582
|
|
|
} |
583
|
|
|
} |
584
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.