Browse Source

Merge branch 'master' into redis

* master: (108 commits)
  fixed indentation in test files
  cleanup tests
  Travis-CI build status.
  CS fixes.
  CS fixes.
  Change magic property to normal method. (Thanks to @maxlapko.)
  CS fixes.
  Fixed duplicated merge.
  Fixes issue #50: implemented pager widgets.
  fix: remove pointless unset
  remove fix for unset timezone from bootstrap app
  mod: don't use ArrayHelper to avoid loading another class during every request
  better have timezone fix in application
  Add exception classes hierarchy UML diagram.
  Initial Gettext support.
  RBAC sql files identation fix
  RBAC sql files
  DbManager some Db fields renaming
  DbManager code style fix + right Exception type fix
  Unit tests fix
  ...

Conflicts:
	tests/unit/framework/caching/ApcCacheTest.php
	tests/unit/framework/caching/CacheTest.php
tags/2.0.0-beta
Carsten Brandt 12 years ago
parent
commit
3518813ac4
  1. 3
      .gitignore
  2. 7
      apps/bootstrap/index.php
  3. 15
      apps/bootstrap/protected/config/main.php
  4. 9
      apps/bootstrap/protected/controllers/SiteController.php
  5. 2
      apps/bootstrap/protected/models/ContactForm.php
  6. 26
      apps/bootstrap/protected/views/layouts/main.php
  7. 16
      apps/bootstrap/protected/views/site/contact.php
  8. 4
      apps/bootstrap/protected/views/site/login.php
  9. 2
      build/build
  10. 79
      composer.json
  11. 212
      composer.lock
  12. 2
      docs/guide/performance.md
  13. 94
      docs/guide/upgrade-from-v1.md
  14. BIN
      docs/internals/exception_hierarchy.png
  15. BIN
      docs/internals/exception_hierarchy.vsd
  16. 470
      framework/helpers/base/ConsoleColor.php
  17. 282
      framework/widgets/Menu.php
  18. 2
      readme.md
  19. 51
      tests/unit/DatabaseTestCase.php
  20. 1
      tests/unit/MysqlTestCase.php
  21. 47
      tests/unit/TestCase.php
  22. 4
      tests/unit/bootstrap.php
  23. 18
      tests/unit/data/config.php
  24. BIN
      tests/unit/data/i18n/test.mo
  25. 64
      tests/unit/data/i18n/test.po
  26. 6
      tests/unit/data/mysql.sql
  27. 350
      tests/unit/data/sqlite.sql
  28. 7
      tests/unit/framework/YiiBaseTest.php
  29. 6
      tests/unit/framework/base/ComponentTest.php
  30. 129
      tests/unit/framework/base/DictionaryTest.php
  31. 2
      tests/unit/framework/base/ModelTest.php
  32. 6
      tests/unit/framework/base/ObjectTest.php
  33. 139
      tests/unit/framework/base/VectorTest.php
  34. 6
      tests/unit/framework/caching/ApcCacheTest.php
  35. 6
      tests/unit/framework/caching/CacheTest.php
  36. 9
      tests/unit/framework/caching/DbCacheTest.php
  37. 2
      tests/unit/framework/caching/FileCacheTest.php
  38. 4
      tests/unit/framework/caching/MemCacheTest.php
  39. 4
      tests/unit/framework/caching/MemCachedTest.php
  40. 6
      tests/unit/framework/caching/WinCacheTest.php
  41. 4
      tests/unit/framework/caching/XCacheTest.php
  42. 4
      tests/unit/framework/caching/ZendDataCacheTest.php
  43. 5
      tests/unit/framework/db/ActiveRecordTest.php
  44. 4
      tests/unit/framework/db/CommandTest.php
  45. 11
      tests/unit/framework/db/ConnectionTest.php
  46. 4
      tests/unit/framework/db/QueryTest.php
  47. 12
      tests/unit/framework/db/sqlite/SqliteActiveRecordTest.php
  48. 21
      tests/unit/framework/db/sqlite/SqliteCommandTest.php
  49. 47
      tests/unit/framework/db/sqlite/SqliteConnectionTest.php
  50. 20
      tests/unit/framework/db/sqlite/SqliteQueryTest.php
  51. 10
      tests/unit/framework/helpers/ArrayHelperTest.php
  52. 16
      tests/unit/framework/helpers/HtmlTest.php
  53. 2
      tests/unit/framework/helpers/JsonTest.php
  54. 46
      tests/unit/framework/helpers/StringHelperTest.php
  55. 3
      tests/unit/framework/helpers/VarDumperTest.php
  56. 14
      tests/unit/framework/i18n/GettextMessageSourceTest.php
  57. 95
      tests/unit/framework/i18n/GettextMoFileTest.php
  58. 95
      tests/unit/framework/i18n/GettextPoFileTest.php
  59. 248
      tests/unit/framework/rbac/ManagerTestBase.php
  60. 37
      tests/unit/framework/rbac/PhpManagerTest.php
  61. 2
      tests/web/app/index.php
  62. 0
      yii/.htaccess
  63. 2
      yii/Yii.php
  64. 15
      yii/YiiBase.php
  65. 14
      yii/assets.php
  66. 0
      yii/assets/jquery.min.js
  67. 4
      yii/assets/yii.activeForm.js
  68. 72
      yii/assets/yii.captcha.js
  69. 26
      yii/assets/yii.debug.js
  70. 0
      yii/assets/yii.js
  71. 2
      yii/assets/yii.validation.js
  72. 0
      yii/base/Action.php
  73. 0
      yii/base/ActionEvent.php
  74. 0
      yii/base/ActionFilter.php
  75. 16
      yii/base/Application.php
  76. 0
      yii/base/Behavior.php
  77. 0
      yii/base/Component.php
  78. 11
      yii/base/Controller.php
  79. 6
      yii/base/Dictionary.php
  80. 0
      yii/base/DictionaryIterator.php
  81. 0
      yii/base/ErrorException.php
  82. 4
      yii/base/ErrorHandler.php
  83. 0
      yii/base/Event.php
  84. 0
      yii/base/Exception.php
  85. 5
      yii/base/HttpException.php
  86. 0
      yii/base/InlineAction.php
  87. 0
      yii/base/InvalidCallException.php
  88. 0
      yii/base/InvalidConfigException.php
  89. 0
      yii/base/InvalidParamException.php
  90. 0
      yii/base/InvalidRequestException.php
  91. 0
      yii/base/InvalidRouteException.php
  92. 16
      yii/base/Model.php
  93. 0
      yii/base/ModelEvent.php
  94. 0
      yii/base/Module.php
  95. 0
      yii/base/NotSupportedException.php
  96. 8
      yii/base/Object.php
  97. 0
      yii/base/Request.php
  98. 0
      yii/base/Response.php
  99. 0
      yii/base/Theme.php
  100. 0
      yii/base/UnknownClassException.php
  101. Some files were not shown because too many files have changed in this diff Show More

3
.gitignore vendored

@ -11,3 +11,6 @@ nbproject
# windows thumbnail cache
Thumbs.db
# composer vendor dir
/yii/vendor

7
apps/bootstrap/index.php

@ -1,8 +1,13 @@
<?php
// comment out the following line to disable debug mode
defined('YII_DEBUG') or define('YII_DEBUG', true);
require(__DIR__ . '/../../framework/yii.php');
$frameworkPath = __DIR__ . '/../../yii';
require($frameworkPath . '/Yii.php');
// Register Composer autoloader
@include($frameworkPath . '/vendor/autoload.php');
$config = require(__DIR__ . '/protected/config/main.php');
$application = new yii\web\Application($config);

15
apps/bootstrap/protected/config/main.php

@ -3,6 +3,12 @@
return array(
'id' => 'hello',
'basePath' => dirname(__DIR__),
'preload' => array('log'),
'modules' => array(
'debug' => array(
'class' => 'yii\debug\Module',
)
),
'components' => array(
'cache' => array(
'class' => 'yii\caching\FileCache',
@ -14,6 +20,15 @@ return array(
'assetManager' => array(
'bundles' => require(__DIR__ . '/assets.php'),
),
'log' => array(
'class' => 'yii\logging\Router',
'targets' => array(
'file' => array(
'class' => 'yii\logging\FileTarget',
'levels' => array('error', 'warning'),
),
),
),
),
'params' => array(
'adminEmail' => 'admin@example.com',

9
apps/bootstrap/protected/controllers/SiteController.php

@ -6,6 +6,15 @@ use app\models\ContactForm;
class SiteController extends Controller
{
public function actions()
{
return array(
'captcha' => array(
'class' => 'yii\web\CaptchaAction',
),
);
}
public function actionIndex()
{
echo $this->render('index');

2
apps/bootstrap/protected/models/ContactForm.php

@ -26,7 +26,7 @@ class ContactForm extends Model
// email has to be a valid email address
array('email', 'email'),
// verifyCode needs to be entered correctly
//array('verifyCode', 'captcha', 'allowEmpty' => !Captcha::checkRequirements()),
array('verifyCode', 'captcha'),
);
}

26
apps/bootstrap/protected/views/layouts/main.php

@ -1,9 +1,11 @@
<?php
use yii\helpers\Html;
use yii\widgets\Menu;
/**
* @var $this \yii\base\View
* @var $content string
*/
use yii\helpers\Html;
$this->registerAssetBundle('app');
?>
<?php $this->beginPage(); ?>
@ -23,16 +25,17 @@ $this->registerAssetBundle('app');
<div class="navbar">
<div class="navbar-inner">
<div class="container">
<ul class="nav">
<li><?php echo Html::a('Home', Yii::$app->homeUrl); ?></li>
<li><?php echo Html::a('About', array('/site/about')); ?></li>
<li><?php echo Html::a('Contact', array('/site/contact')); ?></li>
<?php if (Yii::$app->user->isGuest): ?>
<li><?php echo Html::a('Login', array('/site/login')); ?></li>
<?php else: ?>
<li><?php echo Html::a('Logout (' . Html::encode(Yii::$app->user->identity->username) . ')', array('/site/logout')); ?></li>
<?php endif; ?>
</ul>
<?php $this->widget(Menu::className(), array(
'options' => array('class' => 'nav'),
'items' => array(
array('label' => 'Home', 'url' => array('/site/index')),
array('label' => 'About', 'url' => array('/site/about')),
array('label' => 'Contact', 'url' => array('/site/contact')),
Yii::$app->user->isGuest ?
array('label' => 'Login', 'url' => array('/site/login')) :
array('label' => 'Logout (' . Yii::$app->user->identity->username .')' , 'url' => array('/site/logout')),
),
)); ?>
</div>
</div>
</div>
@ -55,6 +58,7 @@ $this->registerAssetBundle('app');
</div>
<?php $this->endBody(); ?>
</div>
<?php $this->widget('yii\debug\Toolbar'); ?>
</body>
</html>
<?php $this->endPage(); ?>

16
apps/bootstrap/protected/views/site/contact.php

@ -1,5 +1,8 @@
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\widgets\Captcha;
/**
* @var yii\base\View $this
* @var yii\widgets\ActiveForm $form
@ -10,7 +13,7 @@ $this->params['breadcrumbs'][] = $this->title;
?>
<h1><?php echo Html::encode($this->title); ?></h1>
<?php if(Yii::$app->session->hasFlash('contactFormSubmitted')): ?>
<?php if (Yii::$app->session->hasFlash('contactFormSubmitted')): ?>
<div class="alert alert-success">
Thank you for contacting us. We will respond to you as soon as possible.
</div>
@ -20,7 +23,7 @@ $this->params['breadcrumbs'][] = $this->title;
If you have business inquiries or other questions, please fill out the following form to contact us. Thank you.
</p>
<?php $form = $this->beginWidget('yii\widgets\ActiveForm', array(
<?php $form = $this->beginWidget(ActiveForm::className(), array(
'options' => array('class' => 'form-horizontal'),
'fieldConfig' => array('inputOptions' => array('class' => 'input-xlarge')),
)); ?>
@ -28,6 +31,15 @@ $this->params['breadcrumbs'][] = $this->title;
<?php echo $form->field($model, 'email')->textInput(); ?>
<?php echo $form->field($model, 'subject')->textInput(); ?>
<?php echo $form->field($model, 'body')->textArea(array('rows' => 6)); ?>
<?php
$field = $form->field($model, 'verifyCode');
echo $field->begin();
echo $field->label();
$this->widget(Captcha::className());
echo Html::activeTextInput($model, 'verifyCode', array('class' => 'input-medium'));
echo $field->error();
echo $field->end();
?>
<div class="form-actions">
<?php echo Html::submitButton('Submit', null, null, array('class' => 'btn btn-primary')); ?>
</div>

4
apps/bootstrap/protected/views/site/login.php

@ -1,5 +1,7 @@
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/**
* @var yii\base\View $this
* @var yii\widgets\ActiveForm $form
@ -12,7 +14,7 @@ $this->params['breadcrumbs'][] = $this->title;
<p>Please fill out the following fields to login:</p>
<?php $form = $this->beginWidget('yii\widgets\ActiveForm', array('options' => array('class' => 'form-horizontal'))); ?>
<?php $form = $this->beginWidget(ActiveForm::className(), array('options' => array('class' => 'form-horizontal'))); ?>
<?php echo $form->field($model, 'username')->textInput(); ?>
<?php echo $form->field($model, 'password')->passwordInput(); ?>
<?php echo $form->field($model, 'rememberMe')->checkbox(); ?>

2
build/build

@ -11,7 +11,7 @@
// fcgi doesn't have STDIN defined by default
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
require(__DIR__ . '/../framework/yii.php');
require(__DIR__ . '/../framework/Yii.php');
$id = 'yiic-build';
$basePath = __DIR__;

79
composer.json

@ -0,0 +1,79 @@
{
"name": "yiisoft/yii2",
"description": "Yii2 Web Programming Framework",
"keywords": ["yii", "framework"],
"homepage": "http://www.yiiframework.com/",
"type": "library",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Qiang Xue",
"email": "qiang.xue@gmail.com",
"homepage": "http://www.yiiframework.com/",
"role": "Founder and project lead"
},
{
"name": "Alexander Makarov",
"email": "sam@rmcreative.ru",
"homepage": "http://rmcreative.ru/",
"role": "Core framework development"
},
{
"name": "Maurizio Domba",
"homepage": "http://mdomba.info/",
"role": "Core framework development"
},
{
"name": "Carsten Brandt",
"email": "mail@cebe.cc",
"homepage": "http://cebe.cc/",
"role": "Core framework development"
},
{
"name": "Timur Ruziev",
"email": "resurtm@gmail.com",
"homepage": "http://resurtm.com/",
"role": "Core framework development"
},
{
"name": "Paul Klimov",
"email": "klimov.paul@gmail.com",
"role": "Core framework development"
},
{
"name": "Wei Zhuo",
"email": "weizhuo@gmail.com",
"role": "Project site maintenance and development"
},
{
"name": "Sebastián Thierer",
"email": "sebas@artfos.com",
"role": "Component development"
},
{
"name": "Jeffrey Winesett",
"email": "jefftulsa@gmail.com",
"role": "Documentation and marketing"
}
],
"support": {
"issues": "https://github.com/yiisoft/yii2/issues?state=open",
"forum": "http://www.yiiframework.com/forum/",
"wiki": "http://www.yiiframework.com/wiki/",
"irc": "irc://irc.freenode.net/yii",
"source": "https://github.com/yiisoft/yii2"
},
"config": {
"vendor-dir": "yii/vendor"
},
"bin": [
"yii/yiic"
],
"require": {
"php": ">=5.3.0",
"michelf/php-markdown": "1.3",
"twig/twig": "1.12.*",
"smarty/smarty": "3.1.*",
"ezyang/htmlpurifier": "v4.5.0"
}
}

212
composer.lock generated

@ -0,0 +1,212 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
],
"hash": "7d46ce9c4d8d5f4ecae1611ea8f0b49c",
"packages": [
{
"name": "ezyang/htmlpurifier",
"version": "v4.5.0",
"source": {
"type": "git",
"url": "https://github.com/ezyang/htmlpurifier.git",
"reference": "v4.5.0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/v4.5.0",
"reference": "v4.5.0",
"shasum": ""
},
"require": {
"php": ">=5.2"
},
"type": "library",
"autoload": {
"psr-0": {
"HTMLPurifier": "library/"
},
"files": [
"library/HTMLPurifier.composer.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL"
],
"authors": [
{
"name": "Edward Z. Yang",
"email": "admin@htmlpurifier.org",
"homepage": "http://ezyang.com"
}
],
"description": "Standards compliant HTML filter written in PHP",
"homepage": "http://htmlpurifier.org/",
"keywords": [
"html"
],
"time": "2013-02-18 00:04:08"
},
{
"name": "michelf/php-markdown",
"version": "1.3",
"source": {
"type": "git",
"url": "https://github.com/michelf/php-markdown.git",
"reference": "1.3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/michelf/php-markdown/zipball/1.3",
"reference": "1.3",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-lib": "1.3.x-dev"
}
},
"autoload": {
"psr-0": {
"Michelf": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Michel Fortin",
"email": "michel.fortin@michelf.ca",
"homepage": "http://michelf.ca/",
"role": "Developer"
},
{
"name": "John Gruber",
"homepage": "http://daringfireball.net/"
}
],
"description": "PHP Markdown",
"homepage": "http://michelf.ca/projects/php-markdown/",
"keywords": [
"markdown"
],
"time": "2013-04-11 18:53:11"
},
{
"name": "smarty/smarty",
"version": "v3.1.13",
"source": {
"type": "svn",
"url": "http://smarty-php.googlecode.com/svn",
"reference": "/tags/v3.1.13/@4699"
},
"require": {
"php": ">=5.2"
},
"type": "library",
"autoload": {
"classmap": [
"distribution/libs/Smarty.class.php",
"distribution/libs/SmartyBC.class.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0"
],
"authors": [
{
"name": "Monte Ohrt",
"email": "monte@ohrt.com"
},
{
"name": "Uwe Tews",
"email": "uwe.tews@googlemail.com"
},
{
"name": "Rodney Rehm",
"email": "rodney.rehm@medialize.de"
}
],
"description": "Smarty - the compiling PHP template engine",
"homepage": "http://www.smarty.net",
"keywords": [
"templating"
],
"time": "2013-01-26 12:03:52"
},
{
"name": "twig/twig",
"version": "v1.12.3",
"source": {
"type": "git",
"url": "https://github.com/fabpot/Twig.git",
"reference": "v1.12.3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fabpot/Twig/zipball/v1.12.3",
"reference": "v1.12.3",
"shasum": ""
},
"require": {
"php": ">=5.2.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.12-dev"
}
},
"autoload": {
"psr-0": {
"Twig_": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "http://twig.sensiolabs.org",
"keywords": [
"templating"
],
"time": "2013-04-08 12:40:11"
}
],
"packages-dev": [
],
"aliases": [
],
"minimum-stability": "stable",
"stability-flags": [
],
"platform": {
"php": ">=5.3.0"
},
"platform-dev": [
]
}

2
docs/guide/performance.md

@ -162,7 +162,7 @@ class PostController extends Controller
In the view you should access fields of each invidual record from `$posts` as array:
```php
foreach($posts as $post) {
foreach ($posts as $post) {
echo $post['title']."<br>";
}
```

94
docs/guide/upgrade-from-v1.md

@ -6,6 +6,17 @@ We hope this list will make it easier for you to upgrade from Yii 1.1 and quickl
master Yii 2.0 based on your existing Yii knowledge.
Namespace
---------
The most obvious change in Yii 2.0 is the use of namespaces. Almost every core class
is namespaced, e.g., `yii\web\Request`. The "C" prefix is no longer used in class names.
The naming of the namespaces follows the directory structure. For example, `yii\web\Request`
indicates the corresponding class file is `web/Request.php` under the Yii framework folder.
You can use any core class without explicitly include that class file, thanks to the Yii
class loader.
Component and Object
--------------------
@ -15,7 +26,7 @@ via getters and setters. The `Component` class extends from `Object` and support
the event feature and the behavior feature.
If your class does not need the event or behavior feature, you should consider using
`Object` as the based class. This is usually the case for classes that represent basic
`Object` as the base class. This is usually the case for classes that represent basic
data structures.
@ -26,7 +37,7 @@ The `Object` class introduces a uniform way of configuring objects. Any descenda
of `Object` should declare its constructor (if needed) in the following way so that
it can be properly configured:
~~~
```php
class MyClass extends \yii\Object
{
public function __construct($param1, $param2, $config = array())
@ -43,7 +54,7 @@ class MyClass extends \yii\Object
// ... initialization after configuration is applied
}
}
~~~
```
In the above, the last parameter of the constructor must take a configuration array
which contains name-value pairs for initializing the properties at the end of the constructor.
@ -53,13 +64,14 @@ the configuration is applied.
By following this convention, you will be able to create and configure a new object
using a configuration array like the following:
~~~
```php
$object = Yii::createObject(array(
'class' => 'MyClass',
'property1' => 'abc',
'property2' => 'cde',
), $param1, $param2);
~~~
```
Events
@ -69,28 +81,30 @@ There is no longer the need to define an `on`-method in order to define an event
Instead, you can use whatever event names. To attach a handler to an event, you should
use the `on` method now:
~~~
```php
$component->on($eventName, $handler);
// To detach the handler, use:
// $component->off($eventName, $handler);
~~~
```
When you attach a handler, you can now associate it with some parameters which can be later
accessed via the event parameter by the handler:
~~~
```php
$component->on($eventName, $handler, $params);
~~~
```
Because of this change, you can now use "global" events. Simply trigger and attach handlers to
an event of the application instance:
~~~
```php
Yii::$app->on($eventName, $handler);
....
// this will trigger the event and cause $handler to be invoked.
Yii::$app->trigger($eventName);
~~~
```
Path Alias
@ -124,12 +138,13 @@ Because you can access the view object through the "view" application component,
you can now render a view file like the following anywhere in your code, not necessarily
in controllers or widgets:
~~~
```php
$content = Yii::$app->view->renderFile($viewFile, $params);
// You can also explicitly create a new View instance to do the rendering
// $view = new View;
// $view->renderFile($viewFile, $params);
~~~
```
Also, there is no more `CClientScript` in Yii 2.0. The `View` class has taken over its role
with significant improvements. For more details, please see the "assets" subsection.
@ -154,7 +169,7 @@ validation under which scenario. Child classes should overwrite `scenarios()` to
a list of scenarios and the corresponding attributes that need to be validated when
`validate()` is called. For example,
~~~
```php
public function scenarios()
{
return array(
@ -162,7 +177,8 @@ public function scenarios()
'frontend' => array('email', '!name'),
);
}
~~~
```
This method also determines which attributes are safe and which are not. In particular,
given a scenario, if an attribute appears in the corresponding attribute list in `scenarios()`
@ -183,14 +199,15 @@ sending them out. You have to `echo` them explicitly, e.g., `echo $this->render(
A new method called `populate()` is introduced to simplify the data population from user inputs
to a model. For example,
~~~
$post = new Post;
```php
$model = new Post;
if ($this->populate($_POST, $model)) {...}
// which is equivalent to:
if (isset($_POST['Post'])) {
$post->attributes = $_POST['Post'];
$model->attributes = $_POST['Post'];
}
~~~
```
Themes
@ -245,7 +262,7 @@ define a new filter. To use a filter, you should attach the filter class to the
as a behavior. For example, to use the `AccessControl` filter, you should have the following
code in a controller:
~~~
```php
public function behaviors()
{
return array(
@ -258,7 +275,8 @@ public function behaviors()
),
);
}
~~~
```
Assets
@ -290,7 +308,7 @@ Yii 2.0 introduces the *field* concept for building a form using `ActiveForm`. A
is a container consisting of a label, an input, and an error message. It is represented
as an `ActiveField` object. Using fields, you can build a form more cleanly than before:
~~~
```php
<?php $form = $this->beginWidget('yii\widgets\ActiveForm'); ?>
<?php echo $form->field($model, 'username')->textInput(); ?>
<?php echo $form->field($model, 'password')->passwordInput(); ?>
@ -298,7 +316,8 @@ as an `ActiveField` object. Using fields, you can build a form more cleanly than
<?php echo Html::submitButton('Login'); ?>
</div>
<?php $this->endWidget(); ?>
~~~
```
Query Builder
@ -308,7 +327,7 @@ In 1.1, query building is scattered among several classes, including `CDbCommand
`CDbCriteria`, and `CDbCommandBuilder`. Yii 2.0 uses `Query` to represent a DB query
and `QueryBuilder` to generate SQL statements from query objects. For example,
~~~
```php
$query = new \yii\db\Query;
$query->select('id, name')
->from('tbl_user')
@ -317,7 +336,8 @@ $query->select('id, name')
$command = $query->createCommand();
$sql = $command->sql;
$rows = $command->queryAll();
~~~
```
Best of all, such query building methods can be used together with `ActiveRecord`,
as explained in the next sub-section.
@ -331,7 +351,7 @@ is about relational ActiveRecord query. In 1.1, you have to declare the relation
in the `relations()` method. In 2.0, this is done via getter methods that return
an `ActiveQuery` object. For example, the following method declares an "orders" relation:
~~~
```php
class Customer extends \yii\db\ActiveRecord
{
public function getOrders()
@ -339,7 +359,8 @@ class Customer extends \yii\db\ActiveRecord
return $this->hasMany('Order', array('customer_id' => 'id'));
}
}
~~~
```
You can use `$customer->orders` to access the customer's orders. You can also
use `$customer->getOrders()->andWhere('status=1')->all()` to perform on-the-fly
@ -355,7 +376,7 @@ by filtering with the primary keys of the primary records.
Yii 2.0 no longer uses the `model()` method when performing queries. Instead, you
use the `find()` method like the following:
~~~
```php
// to retrieve all *active* customers and order them by their ID:
$customers = Customer::find()
->where(array('status' => $active))
@ -363,7 +384,8 @@ $customers = Customer::find()
->all();
// return the customer whose PK is 1
$customer = Customer::find(1);
~~~
```
The `find()` method returns an instance of `ActiveQuery` which is a subclass of `Query`.
Therefore, you can use all query methods of `Query`.
@ -372,10 +394,9 @@ Instead of returning ActiveRecord objects, you may call `ActiveQuery::asArray()`
return results in terms of arrays. This is more efficient and is especially useful
when you need to return large number of records. For example,
~~~
```php
$customers = Customer::find()->asArray()->all();
~~~
```
By default, ActiveRecord now only saves dirty attributes. In 1.1, all attributes
would be saved to database when you call `save()`, regardless they are changed or not,
@ -390,10 +411,10 @@ within double curly brackets is treated as a table name, and a name enclosed wit
double square brackets is treated as a column name. They will be quoted according to
the database driver being used. For example,
~~~
```php
$command = $connection->createCommand('SELECT [[id]] FROM {{posts}}');
echo $command->sql; // MySQL: SELECT `id` FROM `posts`
~~~
```
This feature is especially useful if you are developing an application that supports
different DBMS.
@ -415,13 +436,14 @@ parameters. For example, if you have rule declared as follows, then it will matc
both `post/popular` and `post/1/popular`. In 1.1, you would have to use two rules to achieve
the same goal.
~~~
```php
array(
'pattern' => 'post/<page:\d+>/<tag>',
'route' => 'post/index',
'defaults' => array('page' => 1),
)
~~~
```
Response

BIN
docs/internals/exception_hierarchy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
docs/internals/exception_hierarchy.vsd

Binary file not shown.

470
framework/helpers/base/ConsoleColor.php

@ -1,470 +0,0 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\helpers\base;
// todo test this on all kinds of terminals, especially windows (check out lib ncurses)
/**
* Console View is the base class for console view components
*
* A console view provides functionality to create rich console application by allowing to format output
* by adding color and font style to it.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class ConsoleColor
{
const FG_BLACK = 30;
const FG_RED = 31;
const FG_GREEN = 32;
const FG_YELLOW = 33;
const FG_BLUE = 34;
const FG_PURPLE = 35;
const FG_CYAN = 36;
const FG_GREY = 37;
const BG_BLACK = 40;
const BG_RED = 41;
const BG_GREEN = 42;
const BG_YELLOW = 43;
const BG_BLUE = 44;
const BG_PURPLE = 45;
const BG_CYAN = 46;
const BG_GREY = 47;
const BOLD = 1;
const ITALIC = 3;
const UNDERLINE = 4;
const BLINK = 5;
const NEGATIVE = 7;
const CONCEALED = 8;
const CROSSED_OUT = 9;
const FRAMED = 51;
const ENCIRCLED = 52;
const OVERLINED = 53;
/**
* Moves the terminal cursor up by sending ANSI control code CUU to the terminal.
* If the cursor is already at the edge of the screen, this has no effect.
* @param integer $rows number of rows the cursor should be moved up
*/
public static function moveCursorUp($rows=1)
{
echo "\033[" . (int) $rows . 'A';
}
/**
* Moves the terminal cursor down by sending ANSI control code CUD to the terminal.
* If the cursor is already at the edge of the screen, this has no effect.
* @param integer $rows number of rows the cursor should be moved down
*/
public static function moveCursorDown($rows=1)
{
echo "\033[" . (int) $rows . 'B';
}
/**
* Moves the terminal cursor forward by sending ANSI control code CUF to the terminal.
* If the cursor is already at the edge of the screen, this has no effect.
* @param integer $steps number of steps the cursor should be moved forward
*/
public static function moveCursorForward($steps=1)
{
echo "\033[" . (int) $steps . 'C';
}
/**
* Moves the terminal cursor backward by sending ANSI control code CUB to the terminal.
* If the cursor is already at the edge of the screen, this has no effect.
* @param integer $steps number of steps the cursor should be moved backward
*/
public static function moveCursorBackward($steps=1)
{
echo "\033[" . (int) $steps . 'D';
}
/**
* Moves the terminal cursor to the beginning of the next line by sending ANSI control code CNL to the terminal.
* @param integer $lines number of lines the cursor should be moved down
*/
public static function moveCursorNextLine($lines=1)
{
echo "\033[" . (int) $lines . 'E';
}
/**
* Moves the terminal cursor to the beginning of the previous line by sending ANSI control code CPL to the terminal.
* @param integer $lines number of lines the cursor should be moved up
*/
public static function moveCursorPrevLine($lines=1)
{
echo "\033[" . (int) $lines . 'F';
}
/**
* Moves the cursor to an absolute position given as column and row by sending ANSI control code CUP or CHA to the terminal.
* @param integer $column 1-based column number, 1 is the left edge of the screen.
* @param integer|null $row 1-based row number, 1 is the top edge of the screen. if not set, will move cursor only in current line.
*/
public static function moveCursorTo($column, $row=null)
{
if ($row === null) {
echo "\033[" . (int) $column . 'G';
} else {
echo "\033[" . (int) $row . ';' . (int) $column . 'H';
}
}
/**
* Scrolls whole page up by sending ANSI control code SU to the terminal.
* New lines are added at the bottom. This is not supported by ANSI.SYS used in windows.
* @param int $lines number of lines to scroll up
*/
public static function scrollUp($lines=1)
{
echo "\033[".(int)$lines."S";
}
/**
* Scrolls whole page down by sending ANSI control code SD to the terminal.
* New lines are added at the top. This is not supported by ANSI.SYS used in windows.
* @param int $lines number of lines to scroll down
*/
public static function scrollDown($lines=1)
{
echo "\033[".(int)$lines."T";
}
/**
* Saves the current cursor position by sending ANSI control code SCP to the terminal.
* Position can then be restored with {@link restoreCursorPosition}.
*/
public static function saveCursorPosition()
{
echo "\033[s";
}
/**
* Restores the cursor position saved with {@link saveCursorPosition} by sending ANSI control code RCP to the terminal.
*/
public static function restoreCursorPosition()
{
echo "\033[u";
}
/**
* Hides the cursor by sending ANSI DECTCEM code ?25l to the terminal.
* Use {@link showCursor} to bring it back.
* Do not forget to show cursor when your application exits. Cursor might stay hidden in terminal after exit.
*/
public static function hideCursor()
{
echo "\033[?25l";
}
/**
* Will show a cursor again when it has been hidden by {@link hideCursor} by sending ANSI DECTCEM code ?25h to the terminal.
*/
public static function showCursor()
{
echo "\033[?25h";
}
/**
* Clears entire screen content by sending ANSI control code ED with argument 2 to the terminal.
* Cursor position will not be changed.
* **Note:** ANSI.SYS implementation used in windows will reset cursor position to upper left corner of the screen.
*/
public static function clearScreen()
{
echo "\033[2J";
}
/**
* Clears text from cursor to the beginning of the screen by sending ANSI control code ED with argument 1 to the terminal.
* Cursor position will not be changed.
*/
public static function clearScreenBeforeCursor()
{
echo "\033[1J";
}
/**
* Clears text from cursor to the end of the screen by sending ANSI control code ED with argument 0 to the terminal.
* Cursor position will not be changed.
*/
public static function clearScreenAfterCursor()
{
echo "\033[0J";
}
/**
* Clears the line, the cursor is currently on by sending ANSI control code EL with argument 2 to the terminal.
* Cursor position will not be changed.
*/
public static function clearLine()
{
echo "\033[2K";
}
/**
* Clears text from cursor position to the beginning of the line by sending ANSI control code EL with argument 1 to the terminal.
* Cursor position will not be changed.
*/
public static function clearLineBeforeCursor()
{
echo "\033[1K";
}
/**
* Clears text from cursor position to the end of the line by sending ANSI control code EL with argument 0 to the terminal.
* Cursor position will not be changed.
*/
public static function clearLineAfterCursor()
{
echo "\033[0K";
}
/**
* Will send ANSI format for following output
*
* You can pass any of the FG_*, BG_* and TEXT_* constants and also xterm256ColorBg
* TODO: documentation
*/
public static function ansiStyle()
{
echo "\033[" . implode(';', func_get_args()) . 'm';
}
/**
* Will return a string formatted with the given ANSI style
*
* See {@link ansiStyle} for possible arguments.
* @param string $string the string to be formatted
* @return string
*/
public static function ansiStyleString($string)
{
$args = func_get_args();
array_shift($args);
$code = implode(';', $args);
return "\033[0m" . ($code !== '' ? "\033[" . $code . "m" : '') . $string."\033[0m";
}
//const COLOR_XTERM256 = 38;// http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors
public static function xterm256ColorFg($i) // TODO naming!
{
return '38;5;'.$i;
}
public static function xterm256ColorBg($i) // TODO naming!
{
return '48;5;'.$i;
}
/**
* Usage: list($w, $h) = ConsoleHelper::getScreenSize();
*
* @return array
*/
public static function getScreenSize()
{
// TODO implement
return array(150,50);
}
/**
* resets any ansi style set by previous method {@link ansiStyle}
* Any output after this is will have default text style.
*/
public static function reset()
{
echo "\033[0m";
}
/**
* Strips ANSI control codes from a string
*
* @param string $string String to strip
* @return string
*/
public static function strip($string)
{
return preg_replace('/\033\[[\d;]+m/', '', $string); // TODO currently only strips color
}
// TODO refactor and review
public static function ansiToHtml($string)
{
$tags = 0;
return preg_replace_callback('/\033\[[\d;]+m/', function($ansi) use (&$tags) {
$styleA = array();
foreach(explode(';', $ansi) as $controlCode)
{
switch($controlCode)
{
case static::FG_BLACK: $style = array('color' => '#000000'); break;
case static::FG_BLUE: $style = array('color' => '#000078'); break;
case static::FG_CYAN: $style = array('color' => '#007878'); break;
case static::FG_GREEN: $style = array('color' => '#007800'); break;
case static::FG_GREY: $style = array('color' => '#787878'); break;
case static::FG_PURPLE: $style = array('color' => '#780078'); break;
case static::FG_RED: $style = array('color' => '#780000'); break;
case static::FG_YELLOW: $style = array('color' => '#787800'); break;
case static::BG_BLACK: $style = array('background-color' => '#000000'); break;
case static::BG_BLUE: $style = array('background-color' => '#000078'); break;
case static::BG_CYAN: $style = array('background-color' => '#007878'); break;
case static::BG_GREEN: $style = array('background-color' => '#007800'); break;
case static::BG_GREY: $style = array('background-color' => '#787878'); break;
case static::BG_PURPLE: $style = array('background-color' => '#780078'); break;
case static::BG_RED: $style = array('background-color' => '#780000'); break;
case static::BG_YELLOW: $style = array('background-color' => '#787800'); break;
case static::BOLD: $style = array('font-weight' => 'bold'); break;
case static::ITALIC: $style = array('font-style' => 'italic'); break;
case static::UNDERLINE: $style = array('text-decoration' => array('underline')); break;
case static::OVERLINED: $style = array('text-decoration' => array('overline')); break;
case static::CROSSED_OUT:$style = array('text-decoration' => array('line-through')); break;
case static::BLINK: $style = array('text-decoration' => array('blink')); break;
case static::NEGATIVE: // ???
case static::CONCEALED:
case static::ENCIRCLED:
case static::FRAMED:
// TODO allow resetting codes
break;
case 0: // ansi reset
$return = '';
for($n=$tags; $tags>0; $tags--) {
$return .= '</span>';
}
return $return;
}
$styleA = ArrayHelper::merge($styleA, $style);
}
$styleString[] = array();
foreach($styleA as $name => $content) {
if ($name === 'text-decoration') {
$content = implode(' ', $content);
}
$styleString[] = $name.':'.$content;
}
$tags++;
return '<span' . (!empty($styleString) ? 'style="' . implode(';', $styleString) : '') . '>';
}, $string);
}
/**
* TODO syntax copied from https://github.com/pear/Console_Color2/blob/master/Console/Color2.php
*
* Converts colorcodes in the format %y (for yellow) into ansi-control
* codes. The conversion table is: ('bold' meaning 'light' on some
* terminals). It's almost the same conversion table irssi uses.
* <pre>
* text text background
* ------------------------------------------------
* %k %K %0 black dark grey black
* %r %R %1 red bold red red
* %g %G %2 green bold green green
* %y %Y %3 yellow bold yellow yellow
* %b %B %4 blue bold blue blue
* %m %M %5 magenta bold magenta magenta
* %p %P magenta (think: purple)
* %c %C %6 cyan bold cyan cyan
* %w %W %7 white bold white white
*
* %F Blinking, Flashing
* %U Underline
* %8 Reverse
* %_,%9 Bold
*
* %n Resets the color
* %% A single %
* </pre>
* First param is the string to convert, second is an optional flag if
* colors should be used. It defaults to true, if set to false, the
* colorcodes will just be removed (And %% will be transformed into %)
*
* @param string $string String to convert
* @param bool $colored Should the string be colored?
*
* @return string
*/
public static function renderColoredString($string)
{
$colored = true;
static $conversions = array ( // static so the array doesn't get built
// everytime
// %y - yellow, and so on... {{{
'%y' => array('color' => 'yellow'),
'%g' => array('color' => 'green' ),
'%b' => array('color' => 'blue' ),
'%r' => array('color' => 'red' ),
'%p' => array('color' => 'purple'),
'%m' => array('color' => 'purple'),
'%c' => array('color' => 'cyan' ),
'%w' => array('color' => 'grey' ),
'%k' => array('color' => 'black' ),
'%n' => array('color' => 'reset' ),
'%Y' => array('color' => 'yellow', 'style' => 'light'),
'%G' => array('color' => 'green', 'style' => 'light'),
'%B' => array('color' => 'blue', 'style' => 'light'),
'%R' => array('color' => 'red', 'style' => 'light'),
'%P' => array('color' => 'purple', 'style' => 'light'),
'%M' => array('color' => 'purple', 'style' => 'light'),
'%C' => array('color' => 'cyan', 'style' => 'light'),
'%W' => array('color' => 'grey', 'style' => 'light'),
'%K' => array('color' => 'black', 'style' => 'light'),
'%N' => array('color' => 'reset', 'style' => 'light'),
'%3' => array('background' => 'yellow'),
'%2' => array('background' => 'green' ),
'%4' => array('background' => 'blue' ),
'%1' => array('background' => 'red' ),
'%5' => array('background' => 'purple'),
'%6' => array('background' => 'cyan' ),
'%7' => array('background' => 'grey' ),
'%0' => array('background' => 'black' ),
// Don't use this, I can't stand flashing text
'%F' => array('style' => 'blink'),
'%U' => array('style' => 'underline'),
'%8' => array('style' => 'inverse'),
'%9' => array('style' => 'bold'),
'%_' => array('style' => 'bold')
// }}}
);
if ($colored) {
$string = str_replace('%%', '% ', $string);
foreach ($conversions as $key => $value) {
$string = str_replace($key, Console_Color::color($value),
$string);
}
$string = str_replace('% ', '%', $string);
} else {
$string = preg_replace('/%((%)|.)/', '$2', $string);
}
return $string;
}
/**
* Escapes % so they don't get interpreted as color codes
*
* @param string $string String to escape
*
* @access public
* @return string
*/
public static function escape($string)
{
return str_replace('%', '%%', $string);
}
}

282
framework/widgets/Menu.php

@ -1,282 +0,0 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\widgets;
use yii\base\Widget;
use yii\helpers\Html;
/**
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Menu extends Widget
{
/**
* @var array list of menu items. Each menu item is specified as an array of name-value pairs.
* Possible option names include the following:
* <ul>
* <li>label: string, optional, specifies the menu item label. When {@link encodeLabel} is true, the label
* will be HTML-encoded. If the label is not specified, it defaults to an empty string.</li>
* <li>url: string or array, optional, specifies the URL of the menu item. It is passed to {@link Html::normalizeUrl}
* to generate a valid URL. If this is not set, the menu item will be rendered as a span text.</li>
* <li>visible: boolean, optional, whether this menu item is visible. Defaults to true.
* This can be used to control the visibility of menu items based on user permissions.</li>
* <li>items: array, optional, specifies the sub-menu items. Its format is the same as the parent items.</li>
* <li>active: boolean, optional, whether this menu item is in active state (currently selected).
* If a menu item is active and {@link activeClass} is not empty, its CSS class will be appended with {@link activeClass}.
* If this option is not set, the menu item will be set active automatically when the current request
* is triggered by {@link url}. Note that the GET parameters not specified in the 'url' option will be ignored.</li>
* <li>template: string, optional, the template used to render this menu item.
* When this option is set, it will override the global setting {@link itemTemplate}.
* Please see {@link itemTemplate} for more details. This option has been available since version 1.1.1.</li>
* <li>linkOptions: array, optional, additional HTML attributes to be rendered for the link or span tag of the menu item.</li>
* <li>itemOptions: array, optional, additional HTML attributes to be rendered for the container tag of the menu item.</li>
* <li>submenuOptions: array, optional, additional HTML attributes to be rendered for the container of the submenu if this menu item has one.
* When this option is set, the {@link submenuHtmlOptions} property will be ignored for this particular submenu.
* This option has been available since version 1.1.6.</li>
* </ul>
*/
public $items = array();
/**
* @var string the template used to render an individual menu item. In this template,
* the token "{menu}" will be replaced with the corresponding menu link or text.
* If this property is not set, each menu will be rendered without any decoration.
* This property will be overridden by the 'template' option set in individual menu items via {@items}.
* @since 1.1.1
*/
public $itemTemplate;
/**
* @var boolean whether the labels for menu items should be HTML-encoded. Defaults to true.
*/
public $encodeLabel = true;
/**
* @var string the CSS class to be appended to the active menu item. Defaults to 'active'.
* If empty, the CSS class of menu items will not be changed.
*/
public $activeCssClass = 'active';
/**
* @var boolean whether to automatically activate items according to whether their route setting
* matches the currently requested route. Defaults to true.
* @since 1.1.3
*/
public $activateItems = true;
/**
* @var boolean whether to activate parent menu items when one of the corresponding child menu items is active.
* The activated parent menu items will also have its CSS classes appended with {@link activeCssClass}.
* Defaults to false.
*/
public $activateParents = false;
/**
* @var boolean whether to hide empty menu items. An empty menu item is one whose 'url' option is not
* set and which doesn't contain visible child menu items. Defaults to true.
*/
public $hideEmptyItems = true;
/**
* @var array HTML attributes for the menu's root container tag
*/
public $options = array();
/**
* @var array HTML attributes for the submenu's container tag.
*/
public $submenuHtmlOptions = array();
/**
* @var string the HTML element name that will be used to wrap the label of all menu links.
* For example, if this property is set as 'span', a menu item may be rendered as
* &lt;li&gt;&lt;a href="url"&gt;&lt;span&gt;label&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
* This is useful when implementing menu items using the sliding window technique.
* Defaults to null, meaning no wrapper tag will be generated.
* @since 1.1.4
*/
public $linkLabelWrapper;
/**
* @var array HTML attributes for the links' wrap element specified in
* {@link linkLabelWrapper}.
* @since 1.1.13
*/
public $linkLabelWrapperHtmlOptions = array();
/**
* @var string the CSS class that will be assigned to the first item in the main menu or each submenu.
* Defaults to null, meaning no such CSS class will be assigned.
* @since 1.1.4
*/
public $firstItemCssClass;
/**
* @var string the CSS class that will be assigned to the last item in the main menu or each submenu.
* Defaults to null, meaning no such CSS class will be assigned.
* @since 1.1.4
*/
public $lastItemCssClass;
/**
* @var string the CSS class that will be assigned to every item.
* Defaults to null, meaning no such CSS class will be assigned.
* @since 1.1.9
*/
public $itemCssClass;
/**
* Initializes the menu widget.
* This method mainly normalizes the {@link items} property.
* If this method is overridden, make sure the parent implementation is invoked.
*/
public function init()
{
$route = $this->getController()->getRoute();
$this->items = $this->normalizeItems($this->items, $route, $hasActiveChild);
}
/**
* Calls {@link renderMenu} to render the menu.
*/
public function run()
{
if (count($this->items)) {
echo Html::beginTag('ul', $this->options) . "\n";
$this->renderItems($this->items);
echo Html::endTag('ul');
}
}
/**
* Recursively renders the menu items.
* @param array $items the menu items to be rendered recursively
*/
protected function renderItems($items)
{
$count = 0;
$n = count($items);
foreach ($items as $item) {
$count++;
$options = isset($item['itemOptions']) ? $item['itemOptions'] : array();
$class = array();
if ($item['active'] && $this->activeCssClass != '') {
$class[] = $this->activeCssClass;
}
if ($count === 1 && $this->firstItemCssClass !== null) {
$class[] = $this->firstItemCssClass;
}
if ($count === $n && $this->lastItemCssClass !== null) {
$class[] = $this->lastItemCssClass;
}
if ($this->itemCssClass !== null) {
$class[] = $this->itemCssClass;
}
if ($class !== array()) {
if (empty($options['class'])) {
$options['class'] = implode(' ', $class);
} else {
$options['class'] .= ' ' . implode(' ', $class);
}
}
echo Html::beginTag('li', $options);
$menu = $this->renderItem($item);
if (isset($this->itemTemplate) || isset($item['template'])) {
$template = isset($item['template']) ? $item['template'] : $this->itemTemplate;
echo strtr($template, array('{menu}' => $menu));
} else {
echo $menu;
}
if (isset($item['items']) && count($item['items'])) {
echo "\n" . Html::beginTag('ul', isset($item['submenuOptions']) ? $item['submenuOptions'] : $this->submenuHtmlOptions) . "\n";
$this->renderItems($item['items']);
echo Html::endTag('ul') . "\n";
}
echo Html::endTag('li') . "\n";
}
}
/**
* Renders the content of a menu item.
* Note that the container and the sub-menus are not rendered here.
* @param array $item the menu item to be rendered. Please see {@link items} on what data might be in the item.
* @return string
* @since 1.1.6
*/
protected function renderItem($item)
{
if (isset($item['url'])) {
$label = $this->linkLabelWrapper === null ? $item['label'] : Html::tag($this->linkLabelWrapper, $this->linkLabelWrapperHtmlOptions, $item['label']);
return Html::a($label, $item['url'], isset($item['linkOptions']) ? $item['linkOptions'] : array());
} else {
return Html::tag('span', isset($item['linkOptions']) ? $item['linkOptions'] : array(), $item['label']);
}
}
/**
* Normalizes the {@link items} property so that the 'active' state is properly identified for every menu item.
* @param array $items the items to be normalized.
* @param string $route the route of the current request.
* @param boolean $active whether there is an active child menu item.
* @return array the normalized menu items
*/
protected function normalizeItems($items, $route, &$active)
{
foreach ($items as $i => $item) {
if (isset($item['visible']) && !$item['visible']) {
unset($items[$i]);
continue;
}
if (!isset($item['label'])) {
$item['label'] = '';
}
if ($this->encodeLabel) {
$items[$i]['label'] = Html::encode($item['label']);
}
$hasActiveChild = false;
if (isset($item['items'])) {
$items[$i]['items'] = $this->normalizeItems($item['items'], $route, $hasActiveChild);
if (empty($items[$i]['items']) && $this->hideEmptyItems) {
unset($items[$i]['items']);
if (!isset($item['url'])) {
unset($items[$i]);
continue;
}
}
}
if (!isset($item['active'])) {
if ($this->activateParents && $hasActiveChild || $this->activateItems && $this->isItemActive($item, $route)) {
$active = $items[$i]['active'] = true;
} else {
$items[$i]['active'] = false;
}
} elseif ($item['active']) {
$active = true;
}
}
return array_values($items);
}
/**
* Checks whether a menu item is active.
* This is done by checking if the currently requested URL is generated by the 'url' option
* of the menu item. Note that the GET parameters not specified in the 'url' option will be ignored.
* @param array $item the menu item to be checked
* @param string $route the route of the current request
* @return boolean whether the menu item is active
*/
protected function isItemActive($item, $route)
{
if (isset($item['url']) && is_array($item['url']) && !strcasecmp(trim($item['url'][0], '/'), $route)) {
unset($item['url']['#']);
if (count($item['url']) > 1) {
foreach (array_splice($item['url'], 1) as $name => $value) {
if (!isset($_GET[$name]) || $_GET[$name] != $value) {
return false;
}
}
}
return true;
}
return false;
}
}

2
readme.md

@ -9,6 +9,8 @@ If you are looking for a production-ready PHP framework, please use
Yii 2.0 is still under heavy development. We may make significant changes
without prior notices. **Yii 2.0 is not ready for production use yet.**
[![Build Status](https://secure.travis-ci.org/yiisoft/yii2.png)](http://travis-ci.org/yiisoft/yii2)
DIRECTORY STRUCTURE
-------------------

51
tests/unit/DatabaseTestCase.php

@ -0,0 +1,51 @@
<?php
namespace yiiunit;
class DatabaseTestCase extends TestCase
{
protected $database;
protected $driverName = 'mysql';
protected $db;
protected function setUp()
{
parent::setUp();
$databases = $this->getParam('databases');
$this->database = $databases[$this->driverName];
$pdo_database = 'pdo_'.$this->driverName;
if (!extension_loaded('pdo') || !extension_loaded($pdo_database)) {
$this->markTestSkipped('pdo and pdo_'.$pdo_database.' extension are required.');
}
}
/**
* @param bool $reset whether to clean up the test database
* @param bool $open whether to open and populate test database
* @return \yii\db\Connection
*/
public function getConnection($reset = true, $open = true)
{
if (!$reset && $this->db) {
return $this->db;
}
$db = new \yii\db\Connection;
$db->dsn = $this->database['dsn'];
if (isset($this->database['username'])) {
$db->username = $this->database['username'];
$db->password = $this->database['password'];
}
if ($open) {
$db->open();
$lines = explode(';', file_get_contents($this->database['fixture']));
foreach ($lines as $line) {
if (trim($line) !== '') {
$db->pdo->exec($line);
}
}
}
$this->db = $db;
return $db;
}
}

1
tests/unit/MysqlTestCase.php

@ -6,6 +6,7 @@ class MysqlTestCase extends TestCase
{
protected function setUp()
{
parent::setUp();
if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql')) {
$this->markTestSkipped('pdo and pdo_mysql extensions are required.');
}

47
tests/unit/TestCase.php

@ -2,15 +2,58 @@
namespace yiiunit;
/**
* This is the base class for all yii framework unit tests.
*/
class TestCase extends \yii\test\TestCase
{
public static $params;
public function getParam($name)
/**
* Clean up after test.
* By default the application created with [[mockApplication]] will be destroyed.
*/
protected function tearDown()
{
parent::tearDown();
$this->destroyApplication();
}
/**
* Returns a test configuration param from /data/config.php
* @param string $name params name
* @param mixed $default default value to use when param is not set.
* @return mixed the value of the configuration param
*/
public function getParam($name, $default = null)
{
if (self::$params === null) {
self::$params = require(__DIR__ . '/data/config.php');
}
return isset(self::$params[$name]) ? self::$params[$name] : null;
return isset(self::$params[$name]) ? self::$params[$name] : $default;
}
/**
* Populates Yii::$app with a new application
* The application will be destroyed on tearDown() automatically.
* @param array $config The application configuration, if needed
*/
protected function mockApplication($config=array())
{
static $defaultConfig = array(
'id' => 'testapp',
'basePath' => __DIR__,
);
$appClass = $this->getParam( 'appClass', '\yii\web\Application' );
new $appClass(array_merge($defaultConfig,$config));
}
/**
* Destroys application in Yii::$app by setting it to null.
*/
protected function destroyApplication()
{
\Yii::$app = null;
}
}

4
tests/unit/bootstrap.php

@ -5,10 +5,8 @@ define('YII_DEBUG', true);
$_SERVER['SCRIPT_NAME'] = '/' . __DIR__;
$_SERVER['SCRIPT_FILENAME'] = __FILE__;
require_once(__DIR__ . '/../../framework/yii.php');
require_once(__DIR__ . '/../../yii/Yii.php');
Yii::setAlias('@yiiunit', __DIR__);
new \yii\web\Application(array('id' => 'testapp', 'basePath' => __DIR__));
require_once(__DIR__ . '/TestCase.php');

18
tests/unit/data/config.php

@ -1,11 +1,19 @@
<?php
return array(
'mysql' => array(
'dsn' => 'mysql:host=127.0.0.1;dbname=yiitest',
'username' => 'travis',
'password' => '',
'fixture' => __DIR__ . '/mysql.sql',
//'appClass' => '\yii\web\Application',
'appClass' => '\yii\console\Application',
'databases' => array(
'mysql' => array(
'dsn' => 'mysql:host=127.0.0.1;dbname=yiitest',
'username' => 'travis',
'password' => '',
'fixture' => __DIR__ . '/mysql.sql',
),
'sqlite' => array(
'dsn' => 'sqlite::memory:',
'fixture' => __DIR__ . '/sqlite.sql',
),
),
'redis' => array(
'dsn' => 'redis://localhost:6379/0',

BIN
tests/unit/data/i18n/test.mo

Binary file not shown.

64
tests/unit/data/i18n/test.po

@ -0,0 +1,64 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: resurtm <resurtm@gmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.5.5\n"
msgctxt "context1"
msgid ""
"Aliquam tempus elit vel purus molestie placerat. In sollicitudin tincidunt\n"
"aliquet. Integer tincidunt gravida tempor. In convallis blandit dui vel "
"malesuada.\n"
"Nunc vel sapien nunc, a pretium nulla."
msgstr ""
"Олицетворение однократно. Представленный лексико-семантический анализ "
"является\n"
"психолингвистическим в своей основе, но механизм сочленений полидисперсен. "
"Впечатление\n"
"однократно. Различное расположение выбирает сюжетный механизм сочленений."
msgctxt "context1"
msgid "String number two."
msgstr "Строка номер два."
msgctxt "context2"
msgid ""
"The other\n"
"\n"
"context.\n"
msgstr ""
"Другой\n"
"\n"
"контекст.\n"
msgctxt "context1"
msgid ""
"Missing\n"
"\r\t\"translation."
msgstr ""
msgctxt "context1"
msgid ""
"Nunc vel sapien nunc, a pretium nulla.\n"
"Pellentesque habitant morbi tristique senectus et netus et malesuada fames "
"ac turpis egestas."
msgstr "Короткий перевод."
msgid "contextless"
msgstr ""
msgctxt "context2"
msgid ""
"test1\\ntest2\n"
"\\\n"
"test3"
msgstr ""
"тест1\\nтест2\n"
"\\\n"
"тест3"

6
tests/unit/data/mysql.sql

@ -1,10 +1,6 @@
/**
* This is the database schema for testing MySQL support of Yii DAO and Active Record.
* The following database setup is required to perform then relevant tests:
* Database name: yiitest
* username: test
* password: test
* charset: utf8
* The database setup in config.php is required to perform then relevant tests:
*/
DROP TABLE IF EXISTS tbl_order_item CASCADE;

350
tests/unit/data/sqlite.sql

@ -1,262 +1,88 @@
CREATE TABLE users
(
id INTEGER NOT NULL PRIMARY KEY,
username VARCHAR(128) NOT NULL,
password VARCHAR(128) NOT NULL,
email VARCHAR(128) NOT NULL
);
INSERT INTO users(id,username,password,email) VALUES (1,'user1','pass1','email1');
INSERT INTO users(id,username,password,email) VALUES (2,'user2','pass2','email2');
INSERT INTO users(id,username,password,email) VALUES (3,'user3','pass3','email3');
INSERT INTO users(id,username,password,email) VALUES (4,'user4','pass4','email4');
CREATE TABLE groups
(
id INTEGER NOT NULL PRIMARY KEY,
name VARCHAR(128) NOT NULL
);
INSERT INTO groups(id,name) VALUES (1,'group1');
INSERT INTO groups(id,name) VALUES (2,'group2');
INSERT INTO groups(id,name) VALUES (3,'group3');
INSERT INTO groups(id,name) VALUES (4,'group4');
INSERT INTO groups(id,name) VALUES (5,'group5');
INSERT INTO groups(id,name) VALUES (6,'group6');
CREATE TABLE groups_descriptions
(
group_id INTEGER NOT NULL PRIMARY KEY,
name VARCHAR(128) NOT NULL
);
INSERT INTO groups_descriptions(group_id,name) VALUES (1,'room1');
INSERT INTO groups_descriptions(group_id,name) VALUES (2,'room2');
INSERT INTO groups_descriptions(group_id,name) VALUES (3,'room3');
INSERT INTO groups_descriptions(group_id,name) VALUES (4,'room4');
CREATE TABLE roles
(
user_id INTEGER NOT NULL,
group_id INTEGER NOT NULL,
name VARCHAR(128) NOT NULL,
PRIMARY KEY(user_id,group_id)
);
INSERT INTO roles(user_id,group_id,name) VALUES (1,1,'dev');
INSERT INTO roles(user_id,group_id,name) VALUES (1,2,'user');
INSERT INTO roles(user_id,group_id,name) VALUES (2,1,'dev');
INSERT INTO roles(user_id,group_id,name) VALUES (2,3,'user');
CREATE TABLE mentorships
(
teacher_id INTEGER NOT NULL,
student_id INTEGER NOT NULL,
progress VARCHAR(128) NOT NULL,
PRIMARY KEY(teacher_id,student_id)
);
INSERT INTO mentorships(teacher_id,student_id,progress) VALUES (1,3,'good');
INSERT INTO mentorships(teacher_id,student_id,progress) VALUES (2,4,'average');
CREATE TABLE profiles
(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
first_name VARCHAR(128) NOT NULL,
last_name VARCHAR(128) NOT NULL,
user_id INTEGER NOT NULL,
CONSTRAINT FK_profile_user FOREIGN KEY (user_id)
REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT
);
INSERT INTO profiles (first_name, last_name, user_id) VALUES ('first 1','last 1',1);
INSERT INTO profiles (first_name, last_name, user_id) VALUES ('first 2','last 2',2);
CREATE TABLE posts
(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
title VARCHAR(128) NOT NULL,
create_time TIMESTAMP NOT NULL,
author_id INTEGER NOT NULL,
content TEXT,
CONSTRAINT FK_post_author FOREIGN KEY (author_id)
REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT
);
INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 1',100000,1,'content 1');
INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 2',100001,2,'content 2');
INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 3',100002,2,'content 3');
INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 4',100003,2,'content 4');
INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 5',100004,3,'content 5');
CREATE TABLE posts_nofk
(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
title VARCHAR(128) NOT NULL,
create_time TIMESTAMP NOT NULL,
author_id INTEGER NOT NULL,
content TEXT
);
INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 1',100000,1,'content 1');
INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 2',100001,2,'content 2');
INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 3',100002,2,'content 3');
INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 4',100003,2,'content 4');
INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 5',100004,3,'content 5');
CREATE TABLE comments
(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
content TEXT NOT NULL,
post_id INTEGER NOT NULL,
author_id INTEGER NOT NULL,
CONSTRAINT FK_post_comment FOREIGN KEY (post_id)
REFERENCES posts (id) ON DELETE CASCADE ON UPDATE RESTRICT,
CONSTRAINT FK_user_comment FOREIGN KEY (author_id)
REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT
);
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 1',1, 2);
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 2',1, 2);
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 3',1, 2);
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 4',2, 2);
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 5',2, 2);
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 6',3, 2);
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 7',3, 2);
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 8',3, 2);
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 9',3, 2);
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 10',5, 3);
CREATE TABLE categories
(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name VARCHAR(128) NOT NULL,
parent_id INTEGER,
CONSTRAINT FK_category_category FOREIGN KEY (parent_id)
REFERENCES categories (id) ON DELETE CASCADE ON UPDATE RESTRICT
);
INSERT INTO categories (name, parent_id) VALUES ('cat 1',NULL);
INSERT INTO categories (name, parent_id) VALUES ('cat 2',NULL);
INSERT INTO categories (name, parent_id) VALUES ('cat 3',NULL);
INSERT INTO categories (name, parent_id) VALUES ('cat 4',1);
INSERT INTO categories (name, parent_id) VALUES ('cat 5',1);
INSERT INTO categories (name, parent_id) VALUES ('cat 6',5);
INSERT INTO categories (name, parent_id) VALUES ('cat 7',5);
CREATE TABLE post_category
(
category_id INTEGER NOT NULL,
post_id INTEGER NOT NULL,
PRIMARY KEY (category_id, post_id),
CONSTRAINT FK_post_category_post FOREIGN KEY (post_id)
REFERENCES posts (id) ON DELETE CASCADE ON UPDATE RESTRICT,
CONSTRAINT FK_post_category_category FOREIGN KEY (category_id)
REFERENCES categories (id) ON DELETE CASCADE ON UPDATE RESTRICT
);
INSERT INTO post_category (category_id, post_id) VALUES (1,1);
INSERT INTO post_category (category_id, post_id) VALUES (2,1);
INSERT INTO post_category (category_id, post_id) VALUES (3,1);
INSERT INTO post_category (category_id, post_id) VALUES (4,2);
INSERT INTO post_category (category_id, post_id) VALUES (1,2);
INSERT INTO post_category (category_id, post_id) VALUES (1,3);
CREATE TABLE orders
(
key1 INTEGER NOT NULL,
key2 INTEGER NOT NULL,
name VARCHAR(128),
PRIMARY KEY (key1, key2)
);
INSERT INTO orders (key1,key2,name) VALUES (1,2,'order 12');
INSERT INTO orders (key1,key2,name) VALUES (1,3,'order 13');
INSERT INTO orders (key1,key2,name) VALUES (2,1,'order 21');
INSERT INTO orders (key1,key2,name) VALUES (2,2,'order 22');
CREATE TABLE items
(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name VARCHAR(128),
col1 INTEGER NOT NULL,
col2 INTEGER NOT NULL,
CONSTRAINT FK_order_item FOREIGN KEY (col1,col2)
REFERENCES orders (key1,key2) ON DELETE CASCADE ON UPDATE RESTRICT
);
INSERT INTO items (name,col1,col2) VALUES ('item 1',1,2);
INSERT INTO items (name,col1,col2) VALUES ('item 2',1,2);
INSERT INTO items (name,col1,col2) VALUES ('item 3',1,3);
INSERT INTO items (name,col1,col2) VALUES ('item 4',2,2);
INSERT INTO items (name,col1,col2) VALUES ('item 5',2,2);
CREATE TABLE types
(
int_col INT NOT NULL,
int_col2 INTEGER DEFAULT 1,
char_col CHAR(100) NOT NULL,
char_col2 VARCHAR(100) DEFAULT 'something',
char_col3 TEXT,
float_col REAL(4,3) NOT NULL,
float_col2 DOUBLE DEFAULT 1.23,
blob_col BLOB,
numeric_col NUMERIC(5,2) DEFAULT 33.22,
time TIMESTAMP DEFAULT 123,
bool_col BOOL NOT NULL,
bool_col2 BOOLEAN DEFAULT 1,
null_col INTEGER DEFAULT NULL
);
CREATE TABLE Content
(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
class VARCHAR(128),
parentID INTEGER NOT NULL,
ownerID INTEGER NOT NULL,
title VARCHAR(100),
CONSTRAINT FK_content_user FOREIGN KEY (ownerID)
REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT
CONSTRAINT FK_content_parent FOREIGN KEY (parentID)
REFERENCES Content (id) ON DELETE CASCADE ON UPDATE RESTRICT
);
INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Article',-1,1,'article 1');
INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Article',-1,2,'article 2');
INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Comment',1,1,'comment 1');
INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Article',-1,2,'article 3');
INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Comment',4,2,'comment 2');
INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Comment',4,1,'comment 3');
CREATE TABLE Article
(
id INTEGER NOT NULL PRIMARY KEY,
authorID INTEGER NOT NULL,
body TEXT,
CONSTRAINT FK_article_content FOREIGN KEY (id)
REFERENCES Content (id) ON DELETE CASCADE ON UPDATE RESTRICT
CONSTRAINT FK_article_author FOREIGN KEY (authorID)
REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT
);
INSERT INTO Article (id,authorID,body) VALUES (1,1,'content for article 1');
INSERT INTO Article (id,authorID,body) VALUES (2,2,'content for article 2');
INSERT INTO Article (id,authorID,body) VALUES (4,1,'content for article 3');
CREATE TABLE Comment
(
id INTEGER NOT NULL PRIMARY KEY,
authorID INTEGER NOT NULL,
body TEXT,
CONSTRAINT FK_comment_content FOREIGN KEY (id)
REFERENCES Content (id) ON DELETE CASCADE ON UPDATE RESTRICT
CONSTRAINT FK_article_author FOREIGN KEY (authorID)
REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT
);
INSERT INTO Comment (id,authorID,body) VALUES (3,1,'content for comment 1');
INSERT INTO Comment (id,authorID,body) VALUES (5,1,'content for comment 2');
INSERT INTO Comment (id,authorID,body) VALUES (6,1,'content for comment 3');
/**
* This is the database schema for testing Sqlite support of Yii DAO and Active Record.
* The database setup in config.php is required to perform then relevant tests:
*/
DROP TABLE IF EXISTS tbl_order_item;
DROP TABLE IF EXISTS tbl_item;
DROP TABLE IF EXISTS tbl_order;
DROP TABLE IF EXISTS tbl_category;
DROP TABLE IF EXISTS tbl_customer;
DROP TABLE IF EXISTS tbl_type;
CREATE TABLE tbl_customer (
id INTEGER NOT NULL,
email varchar(128) NOT NULL,
name varchar(128) NOT NULL,
address text,
status INTEGER DEFAULT 0,
PRIMARY KEY (id)
);
CREATE TABLE tbl_category (
id INTEGER NOT NULL,
name varchar(128) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE tbl_item (
id INTEGER NOT NULL,
name varchar(128) NOT NULL,
category_id INTEGER NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE tbl_order (
id INTEGER NOT NULL,
customer_id INTEGER NOT NULL,
create_time INTEGER NOT NULL,
total decimal(10,0) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE tbl_order_item (
order_id INTEGER NOT NULL,
item_id INTEGER NOT NULL,
quantity INTEGER NOT NULL,
subtotal decimal(10,0) NOT NULL,
PRIMARY KEY (order_id, item_id)
);
CREATE TABLE tbl_type (
int_col INTEGER NOT NULL,
int_col2 INTEGER DEFAULT '1',
char_col char(100) NOT NULL,
char_col2 varchar(100) DEFAULT 'something',
char_col3 text,
float_col double(4,3) NOT NULL,
float_col2 double DEFAULT '1.23',
blob_col blob,
numeric_col decimal(5,2) DEFAULT '33.22',
time timestamp NOT NULL DEFAULT '2002-01-01 00:00:00',
bool_col tinyint(1) NOT NULL,
bool_col2 tinyint(1) DEFAULT '1'
);
INSERT INTO tbl_customer (email, name, address, status) VALUES ('user1@example.com', 'user1', 'address1', 1);
INSERT INTO tbl_customer (email, name, address, status) VALUES ('user2@example.com', 'user2', 'address2', 1);
INSERT INTO tbl_customer (email, name, address, status) VALUES ('user3@example.com', 'user3', 'address3', 2);
INSERT INTO tbl_category (name) VALUES ('Books');
INSERT INTO tbl_category (name) VALUES ('Movies');
INSERT INTO tbl_item (name, category_id) VALUES ('Agile Web Application Development with Yii1.1 and PHP5', 1);
INSERT INTO tbl_item (name, category_id) VALUES ('Yii 1.1 Application Development Cookbook', 1);
INSERT INTO tbl_item (name, category_id) VALUES ('Ice Age', 2);
INSERT INTO tbl_item (name, category_id) VALUES ('Toy Story', 2);
INSERT INTO tbl_item (name, category_id) VALUES ('Cars', 2);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (1, 1325282384, 110.0);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (2, 1325334482, 33.0);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (2, 1325502201, 40.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 1, 1, 30.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 2, 2, 40.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 4, 1, 10.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 5, 1, 15.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 3, 1, 8.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (3, 2, 1, 40.0);

7
tests/unit/framework/YiiBaseTest.php

@ -11,13 +11,15 @@ class YiiBaseTest extends TestCase
{
public $aliases;
public function setUp()
protected function setUp()
{
parent::setUp();
$this->aliases = Yii::$aliases;
}
public function tearDown()
protected function tearDown()
{
parent::tearDown();
Yii::$aliases = $this->aliases;
}
@ -47,7 +49,6 @@ class YiiBaseTest extends TestCase
public function testGetVersion()
{
echo Yii::getVersion();
$this->assertTrue((boolean)preg_match('~\d+\.\d+(?:\.\d+)?(?:-\w+)?~', \Yii::getVersion()));
}

6
tests/unit/framework/base/ComponentTest.php

@ -24,13 +24,15 @@ class ComponentTest extends TestCase
*/
protected $component;
public function setUp()
protected function setUp()
{
parent::setUp();
$this->component = new NewComponent();
}
public function tearDown()
protected function tearDown()
{
parent::tearDown();
$this->component = null;
}

129
tests/unit/framework/base/DictionaryTest.php

@ -15,52 +15,56 @@ class DictionaryTest extends \yiiunit\TestCase
* @var \yii\base\Dictionary
*/
protected $dictionary;
protected $item1,$item2,$item3;
protected $item1;
protected $item2;
protected $item3;
public function setUp()
protected function setUp()
{
$this->dictionary=new Dictionary;
$this->item1=new MapItem;
$this->item2=new MapItem;
$this->item3=new MapItem;
$this->dictionary->add('key1',$this->item1);
$this->dictionary->add('key2',$this->item2);
parent::setUp();
$this->dictionary = new Dictionary;
$this->item1 = new MapItem;
$this->item2 = new MapItem;
$this->item3 = new MapItem;
$this->dictionary->add('key1', $this->item1);
$this->dictionary->add('key2', $this->item2);
}
public function tearDown()
protected function tearDown()
{
$this->dictionary=null;
$this->item1=null;
$this->item2=null;
$this->item3=null;
parent::tearDown();
$this->dictionary = null;
$this->item1 = null;
$this->item2 = null;
$this->item3 = null;
}
public function testConstruct()
{
$a=array(1,2,'key3'=>3);
$dictionary=new Dictionary($a);
$this->assertEquals(3,$dictionary->getCount());
$a = array(1, 2, 'key3' => 3);
$dictionary = new Dictionary($a);
$this->assertEquals(3, $dictionary->getCount());
$dictionary2=new Dictionary($this->dictionary);
$this->assertEquals(2,$dictionary2->getCount());
$this->assertEquals(2, $dictionary2->getCount());
}
public function testGetCount()
{
$this->assertEquals(2,$this->dictionary->getCount());
$this->assertEquals(2, $this->dictionary->getCount());
}
public function testGetKeys()
{
$keys=$this->dictionary->getKeys();
$this->assertEquals(2,count($keys));
$this->assertEquals('key1',$keys[0]);
$this->assertEquals('key2',$keys[1]);
$keys = $this->dictionary->getKeys();
$this->assertEquals(2, count($keys));
$this->assertEquals('key1', $keys[0]);
$this->assertEquals('key2', $keys[1]);
}
public function testAdd()
{
$this->dictionary->add('key3',$this->item3);
$this->assertEquals(3,$this->dictionary->getCount());
$this->dictionary->add('key3', $this->item3);
$this->assertEquals(3, $this->dictionary->getCount());
$this->assertTrue($this->dictionary->has('key3'));
$this->dictionary[] = 'test';
@ -69,21 +73,21 @@ class DictionaryTest extends \yiiunit\TestCase
public function testRemove()
{
$this->dictionary->remove('key1');
$this->assertEquals(1,$this->dictionary->getCount());
$this->assertEquals(1, $this->dictionary->getCount());
$this->assertTrue(!$this->dictionary->has('key1'));
$this->assertTrue($this->dictionary->remove('unknown key')===null);
$this->assertTrue($this->dictionary->remove('unknown key') === null);
}
public function testRemoveAll()
{
$this->dictionary->add('key3',$this->item3);
$this->dictionary->add('key3', $this->item3);
$this->dictionary->removeAll();
$this->assertEquals(0,$this->dictionary->getCount());
$this->assertEquals(0, $this->dictionary->getCount());
$this->assertTrue(!$this->dictionary->has('key1') && !$this->dictionary->has('key2'));
$this->dictionary->add('key3',$this->item3);
$this->dictionary->add('key3', $this->item3);
$this->dictionary->removeAll(true);
$this->assertEquals(0,$this->dictionary->getCount());
$this->assertEquals(0, $this->dictionary->getCount());
$this->assertTrue(!$this->dictionary->has('key1') && !$this->dictionary->has('key2'));
}
@ -96,7 +100,7 @@ class DictionaryTest extends \yiiunit\TestCase
public function testFromArray()
{
$array=array('key3'=>$this->item3,'key4'=>$this->item1);
$array = array('key3' => $this->item3, 'key4' => $this->item1);
$this->dictionary->copyFrom($array);
$this->assertEquals(2, $this->dictionary->getCount());
@ -109,21 +113,21 @@ class DictionaryTest extends \yiiunit\TestCase
public function testMergeWith()
{
$a=array('a'=>'v1','v2',array('2'),'c'=>array('3','c'=>'a'));
$b=array('v22','a'=>'v11',array('2'),'c'=>array('c'=>'3','a'));
$c=array('a'=>'v11','v2',array('2'),'c'=>array('3','c'=>'3','a'),'v22',array('2'));
$dictionary=new Dictionary($a);
$dictionary2=new Dictionary($b);
$a = array('a' => 'v1', 'v2', array('2'), 'c' => array('3', 'c' => 'a'));
$b = array('v22', 'a' => 'v11', array('2'), 'c' => array('c' => '3', 'a'));
$c = array('a' => 'v11', 'v2', array('2'), 'c' => array('3', 'c' => '3', 'a'), 'v22', array('2'));
$dictionary = new Dictionary($a);
$dictionary2 = new Dictionary($b);
$dictionary->mergeWith($dictionary2);
$this->assertTrue($dictionary->toArray()===$c);
$this->assertTrue($dictionary->toArray() === $c);
$array=array('key2'=>$this->item1,'key3'=>$this->item3);
$this->dictionary->mergeWith($array,false);
$this->assertEquals(3,$this->dictionary->getCount());
$this->assertEquals($this->item1,$this->dictionary['key2']);
$this->assertEquals($this->item3,$this->dictionary['key3']);
$array = array('key2' => $this->item1, 'key3' => $this->item3);
$this->dictionary->mergeWith($array, false);
$this->assertEquals(3, $this->dictionary->getCount());
$this->assertEquals($this->item1, $this->dictionary['key2']);
$this->assertEquals($this->item3, $this->dictionary['key3']);
$this->setExpectedException('yii\base\InvalidParamException');
$this->dictionary->mergeWith($this,false);
$this->dictionary->mergeWith($this, false);
}
public function testRecursiveMergeWithTraversable(){
@ -135,7 +139,7 @@ class DictionaryTest extends \yiiunit\TestCase
'k4' => $this->item3,
))
));
$dictionary->mergeWith($obj,true);
$dictionary->mergeWith($obj, true);
$this->assertEquals(3, $dictionary->getCount());
$this->assertEquals($this->item1, $dictionary['k1']);
@ -145,23 +149,23 @@ class DictionaryTest extends \yiiunit\TestCase
public function testArrayRead()
{
$this->assertEquals($this->item1,$this->dictionary['key1']);
$this->assertEquals($this->item2,$this->dictionary['key2']);
$this->assertEquals(null,$this->dictionary['key3']);
$this->assertEquals($this->item1, $this->dictionary['key1']);
$this->assertEquals($this->item2, $this->dictionary['key2']);
$this->assertEquals(null, $this->dictionary['key3']);
}
public function testArrayWrite()
{
$this->dictionary['key3']=$this->item3;
$this->assertEquals(3,$this->dictionary->getCount());
$this->assertEquals($this->item3,$this->dictionary['key3']);
$this->dictionary['key3'] = $this->item3;
$this->assertEquals(3, $this->dictionary->getCount());
$this->assertEquals($this->item3, $this->dictionary['key3']);
$this->dictionary['key1']=$this->item3;
$this->assertEquals(3,$this->dictionary->getCount());
$this->assertEquals($this->item3,$this->dictionary['key1']);
$this->dictionary['key1'] = $this->item3;
$this->assertEquals(3, $this->dictionary->getCount());
$this->assertEquals($this->item3, $this->dictionary['key1']);
unset($this->dictionary['key2']);
$this->assertEquals(2,$this->dictionary->getCount());
$this->assertEquals(2, $this->dictionary->getCount());
$this->assertTrue(!$this->dictionary->has('key2'));
unset($this->dictionary['unknown key']);
@ -169,22 +173,23 @@ class DictionaryTest extends \yiiunit\TestCase
public function testArrayForeach()
{
$n=0;
$found=0;
foreach($this->dictionary as $index=>$item)
{
$n = 0;
$found = 0;
foreach ($this->dictionary as $index => $item) {
$n++;
if($index==='key1' && $item===$this->item1)
if ($index === 'key1' && $item === $this->item1) {
$found++;
if($index==='key2' && $item===$this->item2)
}
if ($index === 'key2' && $item === $this->item2) {
$found++;
}
}
$this->assertTrue($n==2 && $found==2);
$this->assertTrue($n == 2 && $found == 2);
}
public function testArrayMisc()
{
$this->assertEquals($this->dictionary->Count,count($this->dictionary));
$this->assertEquals($this->dictionary->Count, count($this->dictionary));
$this->assertTrue(isset($this->dictionary['key1']));
$this->assertFalse(isset($this->dictionary['unknown key']));
}

2
tests/unit/framework/base/ModelTest.php

@ -155,7 +155,7 @@ class ModelTest extends TestCase
// iteration
$attributes = array();
foreach($speaker as $key => $attribute) {
foreach ($speaker as $key => $attribute) {
$attributes[$key] = $attribute;
}
$this->assertEquals(array(

6
tests/unit/framework/base/ObjectTest.php

@ -14,13 +14,15 @@ class ObjectTest extends TestCase
*/
protected $object;
public function setUp()
protected function setUp()
{
parent::setUp();
$this->object = new NewObject;
}
public function tearDown()
protected function tearDown()
{
parent::tearDown();
$this->object = null;
}

139
tests/unit/framework/base/VectorTest.php

@ -15,39 +15,43 @@ class VectorTest extends \yiiunit\TestCase
* @var Vector
*/
protected $vector;
protected $item1, $item2, $item3;
protected $item1;
protected $item2;
protected $item3;
public function setUp()
protected function setUp()
{
$this->vector=new Vector;
$this->item1=new ListItem;
$this->item2=new ListItem;
$this->item3=new ListItem;
parent::setUp();
$this->vector = new Vector;
$this->item1 = new ListItem;
$this->item2 = new ListItem;
$this->item3 = new ListItem;
$this->vector->add($this->item1);
$this->vector->add($this->item2);
}
public function tearDown()
protected function tearDown()
{
$this->vector=null;
$this->item1=null;
$this->item2=null;
$this->item3=null;
parent::tearDown();
$this->vector = null;
$this->item1 = null;
$this->item2 = null;
$this->item3 = null;
}
public function testConstruct()
{
$a=array(1,2,3);
$vector=new Vector($a);
$this->assertEquals(3,$vector->getCount());
$vector2=new Vector($this->vector);
$this->assertEquals(2,$vector2->getCount());
$a = array(1, 2, 3);
$vector = new Vector($a);
$this->assertEquals(3, $vector->getCount());
$vector2 = new Vector($this->vector);
$this->assertEquals(2, $vector2->getCount());
}
public function testItemAt()
{
$a=array(1, 2, null, 4);
$vector=new Vector($a);
$a = array(1, 2, null, 4);
$vector = new Vector($a);
$this->assertEquals(1, $vector->itemAt(0));
$this->assertEquals(2, $vector->itemAt(1));
$this->assertNull($vector->itemAt(2));
@ -56,37 +60,37 @@ class VectorTest extends \yiiunit\TestCase
public function testGetCount()
{
$this->assertEquals(2,$this->vector->getCount());
$this->assertEquals(2,$this->vector->Count);
$this->assertEquals(2, $this->vector->getCount());
$this->assertEquals(2, $this->vector->Count);
}
public function testAdd()
{
$this->vector->add(null);
$this->vector->add($this->item3);
$this->assertEquals(4,$this->vector->getCount());
$this->assertEquals(3,$this->vector->indexOf($this->item3));
$this->assertEquals(4, $this->vector->getCount());
$this->assertEquals(3, $this->vector->indexOf($this->item3));
}
public function testInsertAt()
{
$this->vector->insertAt(0,$this->item3);
$this->assertEquals(3,$this->vector->getCount());
$this->assertEquals(2,$this->vector->indexOf($this->item2));
$this->assertEquals(0,$this->vector->indexOf($this->item3));
$this->assertEquals(1,$this->vector->indexOf($this->item1));
$this->vector->insertAt(0, $this->item3);
$this->assertEquals(3, $this->vector->getCount());
$this->assertEquals(2, $this->vector->indexOf($this->item2));
$this->assertEquals(0, $this->vector->indexOf($this->item3));
$this->assertEquals(1, $this->vector->indexOf($this->item1));
$this->setExpectedException('yii\base\InvalidParamException');
$this->vector->insertAt(4,$this->item3);
$this->vector->insertAt(4, $this->item3);
}
public function testRemove()
{
$this->vector->remove($this->item1);
$this->assertEquals(1,$this->vector->getCount());
$this->assertEquals(-1,$this->vector->indexOf($this->item1));
$this->assertEquals(0,$this->vector->indexOf($this->item2));
$this->assertEquals(1, $this->vector->getCount());
$this->assertEquals(-1, $this->vector->indexOf($this->item1));
$this->assertEquals(0, $this->vector->indexOf($this->item2));
$this->assertEquals(false,$this->vector->remove($this->item1));
$this->assertEquals(false, $this->vector->remove($this->item1));
}
@ -94,9 +98,9 @@ class VectorTest extends \yiiunit\TestCase
{
$this->vector->add($this->item3);
$this->vector->removeAt(1);
$this->assertEquals(-1,$this->vector->indexOf($this->item2));
$this->assertEquals(1,$this->vector->indexOf($this->item3));
$this->assertEquals(0,$this->vector->indexOf($this->item1));
$this->assertEquals(-1, $this->vector->indexOf($this->item2));
$this->assertEquals(1, $this->vector->indexOf($this->item3));
$this->assertEquals(0, $this->vector->indexOf($this->item1));
$this->setExpectedException('yii\base\InvalidParamException');
$this->vector->removeAt(2);
}
@ -105,15 +109,15 @@ class VectorTest extends \yiiunit\TestCase
{
$this->vector->add($this->item3);
$this->vector->removeAll();
$this->assertEquals(0,$this->vector->getCount());
$this->assertEquals(-1,$this->vector->indexOf($this->item1));
$this->assertEquals(-1,$this->vector->indexOf($this->item2));
$this->assertEquals(0, $this->vector->getCount());
$this->assertEquals(-1, $this->vector->indexOf($this->item1));
$this->assertEquals(-1, $this->vector->indexOf($this->item2));
$this->vector->add($this->item3);
$this->vector->removeAll(true);
$this->assertEquals(0,$this->vector->getCount());
$this->assertEquals(-1,$this->vector->indexOf($this->item1));
$this->assertEquals(-1,$this->vector->indexOf($this->item2));
$this->assertEquals(0, $this->vector->getCount());
$this->assertEquals(-1, $this->vector->indexOf($this->item1));
$this->assertEquals(-1, $this->vector->indexOf($this->item2));
}
public function testHas()
@ -125,30 +129,32 @@ class VectorTest extends \yiiunit\TestCase
public function testIndexOf()
{
$this->assertEquals(0,$this->vector->indexOf($this->item1));
$this->assertEquals(1,$this->vector->indexOf($this->item2));
$this->assertEquals(-1,$this->vector->indexOf($this->item3));
$this->assertEquals(0, $this->vector->indexOf($this->item1));
$this->assertEquals(1, $this->vector->indexOf($this->item2));
$this->assertEquals(-1, $this->vector->indexOf($this->item3));
}
public function testFromArray()
{
$array=array($this->item3,$this->item1);
$array = array($this->item3, $this->item1);
$this->vector->copyFrom($array);
$this->assertTrue(count($array)==2 && $this->vector[0]===$this->item3 && $this->vector[1]===$this->item1);
$this->assertTrue(count($array) == 2 && $this->vector[0] === $this->item3 && $this->vector[1] === $this->item1);
$this->setExpectedException('yii\base\InvalidParamException');
$this->vector->copyFrom($this);
}
public function testMergeWith()
{
$array=array($this->item3,$this->item1);
$array = array($this->item3, $this->item1);
$this->vector->mergeWith($array);
$this->assertTrue($this->vector->getCount()==4 && $this->vector[0]===$this->item1 && $this->vector[3]===$this->item1);
$this->assertTrue($this->vector->getCount() == 4 && $this->vector[0] === $this->item1 &&
$this->vector[3] === $this->item1);
$a=array(1);
$vector=new Vector($a);
$a = array(1);
$vector = new Vector($a);
$this->vector->mergeWith($vector);
$this->assertTrue($this->vector->getCount()==5 && $this->vector[0]===$this->item1 && $this->vector[3]===$this->item1 && $this->vector[4]===1);
$this->assertTrue($this->vector->getCount() == 5 && $this->vector[0] === $this->item1 &&
$this->vector[3] === $this->item1 && $this->vector[4] === 1);
$this->setExpectedException('yii\base\InvalidParamException');
$this->vector->mergeWith($this);
@ -156,37 +162,40 @@ class VectorTest extends \yiiunit\TestCase
public function testToArray()
{
$array=$this->vector->toArray();
$this->assertTrue(count($array)==2 && $array[0]===$this->item1 && $array[1]===$this->item2);
$array = $this->vector->toArray();
$this->assertTrue(count($array) == 2 && $array[0] === $this->item1 && $array[1] === $this->item2);
}
public function testArrayRead()
{
$this->assertTrue($this->vector[0]===$this->item1);
$this->assertTrue($this->vector[1]===$this->item2);
$this->assertTrue($this->vector[0] === $this->item1);
$this->assertTrue($this->vector[1] === $this->item2);
$this->setExpectedException('yii\base\InvalidParamException');
$a=$this->vector[2];
$a = $this->vector[2];
}
public function testGetIterator()
{
$n=0;
$found=0;
foreach($this->vector as $index=>$item)
{
foreach($this->vector as $a=>$b); // test of iterator
$n = 0;
$found = 0;
foreach ($this->vector as $index => $item) {
foreach ($this->vector as $a => $b) {
// test of iterator
}
$n++;
if($index===0 && $item===$this->item1)
if ($index === 0 && $item === $this->item1) {
$found++;
if($index===1 && $item===$this->item2)
}
if ($index === 1 && $item === $this->item2) {
$found++;
}
}
$this->assertTrue($n==2 && $found==2);
$this->assertTrue($n == 2 && $found == 2);
}
public function testArrayMisc()
{
$this->assertEquals($this->vector->Count,count($this->vector));
$this->assertEquals($this->vector->Count, count($this->vector));
$this->assertTrue(isset($this->vector[1]));
$this->assertFalse(isset($this->vector[2]));
}

6
tests/unit/framework/caching/ApcCacheTest.php

@ -15,9 +15,9 @@ class ApcCacheTest extends CacheTest
*/
protected function getCacheInstance()
{
if(!extension_loaded("apc")) {
if (!extension_loaded("apc")) {
$this->markTestSkipped("APC not installed. Skipping.");
} else if ('cli' === PHP_SAPI && !ini_get('apc.enable_cli')) {
} elseif ('cli' === PHP_SAPI && !ini_get('apc.enable_cli')) {
$this->markTestSkipped("APC cli is not enabled. Skipping.");
}
@ -25,7 +25,7 @@ class ApcCacheTest extends CacheTest
$this->markTestSkipped("APC is installed but not enabled. Skipping.");
}
if($this->_cacheInstance === null) {
if ($this->_cacheInstance === null) {
$this->_cacheInstance = new ApcCache();
}
return $this->_cacheInstance;

6
tests/unit/framework/caching/CacheTest.php

@ -13,6 +13,12 @@ abstract class CacheTest extends TestCase
*/
abstract protected function getCacheInstance();
protected function setUp()
{
parent::setUp();
$this->mockApplication();
}
/**
* @return Cache
*/

9
tests/unit/framework/caching/DbCacheTest.php

@ -17,6 +17,8 @@ class DbCacheTest extends CacheTest
$this->markTestSkipped('pdo and pdo_mysql extensions are required.');
}
parent::setUp();
$this->getConnection()->createCommand("
CREATE TABLE IF NOT EXISTS tbl_cache (
id char(128) NOT NULL,
@ -34,8 +36,9 @@ class DbCacheTest extends CacheTest
*/
function getConnection($reset = true)
{
if($this->_connection === null) {
$params = $this->getParam('mysql');
if ($this->_connection === null) {
$databases = $this->getParam('databases');
$params = $databases['mysql'];
$db = new \yii\db\Connection;
$db->dsn = $params['dsn'];
$db->username = $params['username'];
@ -60,7 +63,7 @@ class DbCacheTest extends CacheTest
*/
protected function getCacheInstance()
{
if($this->_cacheInstance === null) {
if ($this->_cacheInstance === null) {
$this->_cacheInstance = new DbCache(array(
'db' => $this->getConnection(),
));

2
tests/unit/framework/caching/FileCacheTest.php

@ -15,7 +15,7 @@ class FileCacheTest extends CacheTest
*/
protected function getCacheInstance()
{
if($this->_cacheInstance === null) {
if ($this->_cacheInstance === null) {
$this->_cacheInstance = new FileCache(array(
'cachePath' => '@yiiunit/runtime/cache',
));

4
tests/unit/framework/caching/MemCacheTest.php

@ -15,11 +15,11 @@ class MemCacheTest extends CacheTest
*/
protected function getCacheInstance()
{
if(!extension_loaded("memcache")) {
if (!extension_loaded("memcache")) {
$this->markTestSkipped("memcache not installed. Skipping.");
}
if($this->_cacheInstance === null) {
if ($this->_cacheInstance === null) {
$this->_cacheInstance = new MemCache();
}
return $this->_cacheInstance;

4
tests/unit/framework/caching/MemCachedTest.php

@ -15,11 +15,11 @@ class MemCachedTest extends CacheTest
*/
protected function getCacheInstance()
{
if(!extension_loaded("memcached")) {
if (!extension_loaded("memcached")) {
$this->markTestSkipped("memcached not installed. Skipping.");
}
if($this->_cacheInstance === null) {
if ($this->_cacheInstance === null) {
$this->_cacheInstance = new MemCache(array(
'useMemcached' => true,
));

6
tests/unit/framework/caching/WinCacheTest.php

@ -15,15 +15,15 @@ class WinCacheTest extends CacheTest
*/
protected function getCacheInstance()
{
if(!extension_loaded('wincache')) {
if (!extension_loaded('wincache')) {
$this->markTestSkipped("Wincache not installed. Skipping.");
}
if(!ini_get('wincache.ucenabled')) {
if (!ini_get('wincache.ucenabled')) {
$this->markTestSkipped("Wincache user cache disabled. Skipping.");
}
if($this->_cacheInstance === null) {
if ($this->_cacheInstance === null) {
$this->_cacheInstance = new WinCache();
}
return $this->_cacheInstance;

4
tests/unit/framework/caching/XCacheTest.php

@ -15,11 +15,11 @@ class XCacheTest extends CacheTest
*/
protected function getCacheInstance()
{
if(!function_exists("xcache_isset")) {
if (!function_exists("xcache_isset")) {
$this->markTestSkipped("XCache not installed. Skipping.");
}
if($this->_cacheInstance === null) {
if ($this->_cacheInstance === null) {
$this->_cacheInstance = new XCache();
}
return $this->_cacheInstance;

4
tests/unit/framework/caching/ZendDataCacheTest.php

@ -15,11 +15,11 @@ class ZendDataCacheTest extends CacheTest
*/
protected function getCacheInstance()
{
if(!function_exists("zend_shm_cache_store")) {
if (!function_exists("zend_shm_cache_store")) {
$this->markTestSkipped("Zend Data cache not installed. Skipping.");
}
if($this->_cacheInstance === null) {
if ($this->_cacheInstance === null) {
$this->_cacheInstance = new ZendDataCache();
}
return $this->_cacheInstance;

5
tests/unit/framework/db/ActiveRecordTest.php

@ -10,10 +10,11 @@ use yiiunit\data\ar\OrderItem;
use yiiunit\data\ar\Order;
use yiiunit\data\ar\Item;
class ActiveRecordTest extends \yiiunit\MysqlTestCase
class ActiveRecordTest extends \yiiunit\DatabaseTestCase
{
public function setUp()
protected function setUp()
{
parent::setUp();
ActiveRecord::$db = $this->getConnection();
}

4
tests/unit/framework/db/CommandTest.php

@ -7,7 +7,7 @@ use yii\db\Command;
use yii\db\Query;
use yii\db\DataReader;
class CommandTest extends \yiiunit\MysqlTestCase
class CommandTest extends \yiiunit\DatabaseTestCase
{
function testConstruct()
{
@ -140,7 +140,7 @@ class CommandTest extends \yiiunit\MysqlTestCase
$db = $this->getConnection();
// bindParam
$sql = 'INSERT INTO tbl_customer(email,name,address) VALUES (:email, :name, :address)';
$sql = 'INSERT INTO tbl_customer(email, name, address) VALUES (:email, :name, :address)';
$command = $db->createCommand($sql);
$email = 'user4@example.com';
$name = 'user4';

11
tests/unit/framework/db/ConnectionTest.php

@ -4,12 +4,12 @@ namespace yiiunit\framework\db;
use yii\db\Connection;
class ConnectionTest extends \yiiunit\MysqlTestCase
class ConnectionTest extends \yiiunit\DatabaseTestCase
{
function testConstruct()
{
$connection = $this->getConnection(false);
$params = $this->getParam('mysql');
$params = $this->database;
$this->assertEquals($params['dsn'], $connection->dsn);
$this->assertEquals($params['username'], $connection->username);
@ -18,7 +18,7 @@ class ConnectionTest extends \yiiunit\MysqlTestCase
function testOpenClose()
{
$connection = $this->getConnection(false);
$connection = $this->getConnection(false, false);
$this->assertFalse($connection->isActive);
$this->assertEquals(null, $connection->pdo);
@ -39,9 +39,8 @@ class ConnectionTest extends \yiiunit\MysqlTestCase
function testGetDriverName()
{
$connection = $this->getConnection(false);
$this->assertEquals('mysql', $connection->driverName);
$this->assertFalse($connection->isActive);
$connection = $this->getConnection(false, false);
$this->assertEquals($this->driverName, $connection->driverName);
}
function testQuoteValue()

4
tests/unit/framework/db/QueryTest.php

@ -7,7 +7,7 @@ use yii\db\Command;
use yii\db\Query;
use yii\db\DataReader;
class QueryTest extends \yiiunit\MysqlTestCase
class QueryTest extends \yiiunit\DatabaseTestCase
{
function testSelect()
{
@ -20,7 +20,7 @@ class QueryTest extends \yiiunit\MysqlTestCase
$query = new Query;
$query->select('id, name', 'something')->distinct(true);
$this->assertEquals(array('id','name'), $query->select);
$this->assertEquals(array('id', 'name'), $query->select);
$this->assertTrue($query->distinct);
$this->assertEquals('something', $query->selectOption);
}

12
tests/unit/framework/db/sqlite/SqliteActiveRecordTest.php

@ -0,0 +1,12 @@
<?php
namespace yiiunit\framework\db\sqlite;
class SqliteActiveRecordTest extends \yiiunit\framework\db\ActiveRecordTest
{
protected function setUp()
{
$this->driverName = 'sqlite';
parent::setUp();
}
}

21
tests/unit/framework/db/sqlite/SqliteCommandTest.php

@ -0,0 +1,21 @@
<?php
namespace yiiunit\framework\db\sqlite;
class SqliteCommandTest extends \yiiunit\framework\db\CommandTest
{
protected function setUp()
{
$this->driverName = 'sqlite';
parent::setUp();
}
public function testAutoQuoting()
{
$db = $this->getConnection(false);
$sql = 'SELECT [[id]], [[t.name]] FROM {{tbl_customer}} t';
$command = $db->createCommand($sql);
$this->assertEquals("SELECT \"id\", 't'.\"name\" FROM 'tbl_customer' t", $command->sql);
}
}

47
tests/unit/framework/db/sqlite/SqliteConnectionTest.php

@ -0,0 +1,47 @@
<?php
namespace yiiunit\framework\db\sqlite;
class SqliteConnectionTest extends \yiiunit\framework\db\ConnectionTest
{
protected function setUp()
{
$this->driverName = 'sqlite';
parent::setUp();
}
public function testConstruct()
{
$connection = $this->getConnection(false);
$params = $this->database;
$this->assertEquals($params['dsn'], $connection->dsn);
}
public function testQuoteValue()
{
$connection = $this->getConnection(false);
$this->assertEquals(123, $connection->quoteValue(123));
$this->assertEquals("'string'", $connection->quoteValue('string'));
$this->assertEquals("'It''s interesting'", $connection->quoteValue("It's interesting"));
}
public function testQuoteTableName()
{
$connection = $this->getConnection(false);
$this->assertEquals("'table'", $connection->quoteTableName('table'));
$this->assertEquals("'schema'.'table'", $connection->quoteTableName('schema.table'));
$this->assertEquals('{{table}}', $connection->quoteTableName('{{table}}'));
$this->assertEquals('(table)', $connection->quoteTableName('(table)'));
}
public function testQuoteColumnName()
{
$connection = $this->getConnection(false);
$this->assertEquals('"column"', $connection->quoteColumnName('column'));
$this->assertEquals("'table'.\"column\"", $connection->quoteColumnName('table.column'));
$this->assertEquals('[[column]]', $connection->quoteColumnName('[[column]]'));
$this->assertEquals('{{column}}', $connection->quoteColumnName('{{column}}'));
$this->assertEquals('(column)', $connection->quoteColumnName('(column)'));
}
}

20
tests/unit/framework/db/sqlite/SqliteQueryTest.php

@ -0,0 +1,20 @@
<?php
/**
* Created by JetBrains PhpStorm.
* User: RusMaxim
* Date: 09.05.13
* Time: 21:41
* To change this template use File | Settings | File Templates.
*/
namespace yiiunit\framework\db\sqlite;
class SqliteQueryTest extends \yiiunit\framework\db\QueryTest
{
protected function setUp()
{
$this->driverName = 'sqlite';
parent::setUp();
}
}

10
tests/unit/framework/helpers/ArrayHelperTest.php

@ -12,6 +12,16 @@ class ArrayHelperTest extends \yii\test\TestCase
}
public function testRemove()
{
$array = array('name' => 'b', 'age' => 3);
$name = ArrayHelper::remove($array, 'name');
$this->assertEquals($name, 'b');
$this->assertEquals($array, array('age' => 3));
}
public function testMultisort()
{
// single key

16
tests/unit/framework/helpers/HtmlTest.php

@ -4,15 +4,14 @@ namespace yiiunit\framework\helpers;
use Yii;
use yii\helpers\Html;
use yii\web\Application;
use yiiunit\TestCase;
class HtmlTest extends \yii\test\TestCase
class HtmlTest extends TestCase
{
public function setUp()
protected function setUp()
{
new Application(array(
'id' => 'test',
'basePath' => '@yiiunit/runtime',
parent::setUp();
$this->mockApplication(array(
'components' => array(
'request' => array(
'class' => 'yii\web\Request',
@ -30,11 +29,6 @@ class HtmlTest extends \yii\test\TestCase
$this->assertEquals($expected, $actual);
}
public function tearDown()
{
Yii::$app = null;
}
public function testEncode()
{
$this->assertEquals("a&lt;&gt;&amp;&quot;&#039;", Html::encode("a<>&\"'"));

2
tests/unit/framework/helpers/JsonTest.php

@ -4,7 +4,7 @@
namespace yiiunit\framework\helpers;
use yii\helpers\Json;
use yii\helpers\JsExpression;
use yii\web\JsExpression;
class JsonTest extends \yii\test\TestCase
{

46
tests/unit/framework/helpers/StringHelperTest.php

@ -40,7 +40,7 @@ class StringHelperTest extends \yii\test\TestCase
'car' => 'cars',
);
foreach($testData as $testIn => $testOut) {
foreach ($testData as $testIn => $testOut) {
$this->assertEquals($testOut, StringHelper::pluralize($testIn));
$this->assertEquals(ucfirst($testOut), ucfirst(StringHelper::pluralize($testIn)));
}
@ -70,4 +70,48 @@ class StringHelperTest extends \yii\test\TestCase
$this->assertEquals('PostTag', StringHelper::id2camel('post-tag'));
$this->assertEquals('PostTag', StringHelper::id2camel('post_tag', '_'));
}
public function testBasename()
{
$this->assertEquals('', StringHelper::basename(''));
$this->assertEquals('file', StringHelper::basename('file'));
$this->assertEquals('file.test', StringHelper::basename('file.test', '.test2'));
$this->assertEquals('file', StringHelper::basename('file.test', '.test'));
$this->assertEquals('file', StringHelper::basename('/file'));
$this->assertEquals('file.test', StringHelper::basename('/file.test', '.test2'));
$this->assertEquals('file', StringHelper::basename('/file.test', '.test'));
$this->assertEquals('file', StringHelper::basename('/path/to/file'));
$this->assertEquals('file.test', StringHelper::basename('/path/to/file.test', '.test2'));
$this->assertEquals('file', StringHelper::basename('/path/to/file.test', '.test'));
$this->assertEquals('file', StringHelper::basename('\file'));
$this->assertEquals('file.test', StringHelper::basename('\file.test', '.test2'));
$this->assertEquals('file', StringHelper::basename('\file.test', '.test'));
$this->assertEquals('file', StringHelper::basename('C:\file'));
$this->assertEquals('file.test', StringHelper::basename('C:\file.test', '.test2'));
$this->assertEquals('file', StringHelper::basename('C:\file.test', '.test'));
$this->assertEquals('file', StringHelper::basename('C:\path\to\file'));
$this->assertEquals('file.test', StringHelper::basename('C:\path\to\file.test', '.test2'));
$this->assertEquals('file', StringHelper::basename('C:\path\to\file.test', '.test'));
// mixed paths
$this->assertEquals('file.test', StringHelper::basename('/path\to/file.test'));
$this->assertEquals('file.test', StringHelper::basename('/path/to\file.test'));
$this->assertEquals('file.test', StringHelper::basename('\path/to\file.test'));
// \ and / in suffix
$this->assertEquals('file', StringHelper::basename('/path/to/filete/st', 'te/st'));
$this->assertEquals('st', StringHelper::basename('/path/to/filete/st', 'te\st'));
$this->assertEquals('file', StringHelper::basename('/path/to/filete\st', 'te\st'));
$this->assertEquals('st', StringHelper::basename('/path/to/filete\st', 'te/st'));
// http://www.php.net/manual/en/function.basename.php#72254
$this->assertEquals('foo', StringHelper::basename('/bar/foo/'));
$this->assertEquals('foo', StringHelper::basename('\\bar\\foo\\'));
}
}

3
tests/unit/framework/helpers/VarDumperTest.php

@ -7,6 +7,9 @@ class VarDumperTest extends \yii\test\TestCase
public function testDumpObject()
{
$obj = new \StdClass();
ob_start();
VarDumper::dump($obj);
$this->assertEquals("stdClass#1\n(\n)", ob_get_contents());
ob_end_clean();
}
}

14
tests/unit/framework/i18n/GettextMessageSourceTest.php

@ -0,0 +1,14 @@
<?php
namespace yiiunit\framework\i18n;
use yii\i18n\GettextMessageSource;
use yiiunit\TestCase;
class GettextMessageSourceTest extends TestCase
{
public function testLoadMessages()
{
$this->markTestSkipped();
}
}

95
tests/unit/framework/i18n/GettextMoFileTest.php

@ -0,0 +1,95 @@
<?php
namespace yiiunit\framework\i18n;
use yii\i18n\GettextMoFile;
use yiiunit\TestCase;
class GettextMoFileTest extends TestCase
{
public function testLoad()
{
$moFile = new GettextMoFile();
$moFilePath = __DIR__ . '/../../data/i18n/test.mo';
$context1 = $moFile->load($moFilePath, 'context1');
$context2 = $moFile->load($moFilePath, 'context2');
// item count
$this->assertCount(3, $context1);
$this->assertCount(2, $context2);
// original messages
$this->assertArrayNotHasKey("Missing\n\r\t\"translation.", $context1);
$this->assertArrayHasKey("Aliquam tempus elit vel purus molestie placerat. In sollicitudin tincidunt\naliquet. Integer tincidunt gravida tempor. In convallis blandit dui vel malesuada.\nNunc vel sapien nunc, a pretium nulla.", $context1);
$this->assertArrayHasKey("String number two.", $context1);
$this->assertArrayHasKey("Nunc vel sapien nunc, a pretium nulla.\nPellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.", $context1);
$this->assertArrayHasKey("The other\n\ncontext.\n", $context2);
$this->assertArrayHasKey("test1\\ntest2\n\\\ntest3", $context2);
// translated messages
$this->assertFalse(in_array("", $context1));
$this->assertTrue(in_array("Олицетворение однократно. Представленный лексико-семантический анализ является\nпсихолингвистическим в своей основе, но механизм сочленений полидисперсен. Впечатление\nоднократно. Различное расположение выбирает сюжетный механизм сочленений.", $context1));
$this->assertTrue(in_array('Строка номер два.', $context1));
$this->assertTrue(in_array('Короткий перевод.', $context1));
$this->assertTrue(in_array("Другой\n\nконтекст.\n", $context2));
$this->assertTrue(in_array("тест1\\nтест2\n\\\nтест3", $context2));
}
public function testSave()
{
// initial data
$s = chr(4);
$messages = array(
'Hello!' => 'Привет!',
"context1{$s}Hello?" => 'Привет?',
'Hello!?' => '',
"context1{$s}Hello!?!" => '',
"context2{$s}\"Quotes\"" => '"Кавычки"',
"context2{$s}\nNew lines\n" => "\nПереносы строк\n",
"context2{$s}\tTabs\t" => "\tТабы\t",
"context2{$s}\rCarriage returns\r" => "\rВозвраты кареток\r",
);
// create temporary directory and dump messages
$poFileDirectory = __DIR__ . '/../../runtime/i18n';
if (!is_dir($poFileDirectory)) {
mkdir($poFileDirectory);
}
if (is_file($poFileDirectory . '/test.mo')) {
unlink($poFileDirectory . '/test.mo');
}
$moFile = new GettextMoFile();
$moFile->save($poFileDirectory . '/test.mo', $messages);
// load messages
$context1 = $moFile->load($poFileDirectory . '/test.mo', 'context1');
$context2 = $moFile->load($poFileDirectory . '/test.mo', 'context2');
// context1
$this->assertCount(2, $context1);
$this->assertArrayHasKey('Hello?', $context1);
$this->assertTrue(in_array('Привет?', $context1));
$this->assertArrayHasKey('Hello!?!', $context1);
$this->assertTrue(in_array('', $context1));
// context2
$this->assertCount(4, $context2);
$this->assertArrayHasKey("\"Quotes\"", $context2);
$this->assertTrue(in_array('"Кавычки"', $context2));
$this->assertArrayHasKey("\nNew lines\n", $context2);
$this->assertTrue(in_array("\nПереносы строк\n", $context2));
$this->assertArrayHasKey("\tTabs\t", $context2);
$this->assertTrue(in_array("\tТабы\t", $context2));
$this->assertArrayHasKey("\rCarriage returns\r", $context2);
$this->assertTrue(in_array("\rВозвраты кареток\r", $context2));
}
}

95
tests/unit/framework/i18n/GettextPoFileTest.php

@ -0,0 +1,95 @@
<?php
namespace yiiunit\framework\i18n;
use yii\i18n\GettextPoFile;
use yiiunit\TestCase;
class GettextPoFileTest extends TestCase
{
public function testLoad()
{
$poFile = new GettextPoFile();
$poFilePath = __DIR__ . '/../../data/i18n/test.po';
$context1 = $poFile->load($poFilePath, 'context1');
$context2 = $poFile->load($poFilePath, 'context2');
// item count
$this->assertCount(4, $context1);
$this->assertCount(2, $context2);
// original messages
$this->assertArrayHasKey("Missing\n\r\t\"translation.", $context1);
$this->assertArrayHasKey("Aliquam tempus elit vel purus molestie placerat. In sollicitudin tincidunt\naliquet. Integer tincidunt gravida tempor. In convallis blandit dui vel malesuada.\nNunc vel sapien nunc, a pretium nulla.", $context1);
$this->assertArrayHasKey("String number two.", $context1);
$this->assertArrayHasKey("Nunc vel sapien nunc, a pretium nulla.\nPellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.", $context1);
$this->assertArrayHasKey("The other\n\ncontext.\n", $context2);
$this->assertArrayHasKey("test1\\\ntest2\n\\\\\ntest3", $context2);
// translated messages
$this->assertTrue(in_array("", $context1));
$this->assertTrue(in_array("Олицетворение однократно. Представленный лексико-семантический анализ является\nпсихолингвистическим в своей основе, но механизм сочленений полидисперсен. Впечатление\nоднократно. Различное расположение выбирает сюжетный механизм сочленений.", $context1));
$this->assertTrue(in_array('Строка номер два.', $context1));
$this->assertTrue(in_array('Короткий перевод.', $context1));
$this->assertTrue(in_array("Другой\n\nконтекст.\n", $context2));
$this->assertTrue(in_array("тест1\\\nтест2\n\\\\\nтест3", $context2));
}
public function testSave()
{
// initial data
$s = chr(4);
$messages = array(
'Hello!' => 'Привет!',
"context1{$s}Hello?" => 'Привет?',
'Hello!?' => '',
"context1{$s}Hello!?!" => '',
"context2{$s}\"Quotes\"" => '"Кавычки"',
"context2{$s}\nNew lines\n" => "\nПереносы строк\n",
"context2{$s}\tTabs\t" => "\tТабы\t",
"context2{$s}\rCarriage returns\r" => "\rВозвраты кареток\r",
);
// create temporary directory and dump messages
$poFileDirectory = __DIR__ . '/../../runtime/i18n';
if (!is_dir($poFileDirectory)) {
mkdir($poFileDirectory);
}
if (is_file($poFileDirectory . '/test.po')) {
unlink($poFileDirectory . '/test.po');
}
$poFile = new GettextPoFile();
$poFile->save($poFileDirectory . '/test.po', $messages);
// load messages
$context1 = $poFile->load($poFileDirectory . '/test.po', 'context1');
$context2 = $poFile->load($poFileDirectory . '/test.po', 'context2');
// context1
$this->assertCount(2, $context1);
$this->assertArrayHasKey('Hello?', $context1);
$this->assertTrue(in_array('Привет?', $context1));
$this->assertArrayHasKey('Hello!?!', $context1);
$this->assertTrue(in_array('', $context1));
// context2
$this->assertCount(4, $context2);
$this->assertArrayHasKey("\"Quotes\"", $context2);
$this->assertTrue(in_array('"Кавычки"', $context2));
$this->assertArrayHasKey("\nNew lines\n", $context2);
$this->assertTrue(in_array("\nПереносы строк\n", $context2));
$this->assertArrayHasKey("\tTabs\t", $context2);
$this->assertTrue(in_array("\tТабы\t", $context2));
$this->assertArrayHasKey("\rCarriage returns\r", $context2);
$this->assertTrue(in_array("\rВозвраты кареток\r", $context2));
}
}

248
tests/unit/framework/rbac/ManagerTestBase.php

@ -0,0 +1,248 @@
<?php
namespace yiiunit\framework\rbac;
use yii\rbac\Assignment;
use yii\rbac\Item;
use yiiunit\TestCase;
abstract class ManagerTestBase extends TestCase
{
/** @var \yii\rbac\PhpManager|\yii\rbac\DbManager */
protected $auth;
public function testCreateItem()
{
$type = Item::TYPE_TASK;
$name = 'editUser';
$description = 'edit a user';
$bizRule = 'checkUserIdentity()';
$data = array(1, 2, 3);
$item = $this->auth->createItem($name, $type, $description, $bizRule, $data);
$this->assertTrue($item instanceof Item);
$this->assertEquals($item->type, $type);
$this->assertEquals($item->name, $name);
$this->assertEquals($item->description, $description);
$this->assertEquals($item->bizRule, $bizRule);
$this->assertEquals($item->data, $data);
// test shortcut
$name2 = 'createUser';
$item2 = $this->auth->createRole($name2, $description, $bizRule, $data);
$this->assertEquals($item2->type, Item::TYPE_ROLE);
// test adding an item with the same name
$this->setExpectedException('Exception');
$this->auth->createItem($name, $type, $description, $bizRule, $data);
}
public function testGetItem()
{
$this->assertTrue($this->auth->getItem('readPost') instanceof Item);
$this->assertTrue($this->auth->getItem('reader') instanceof Item);
$this->assertNull($this->auth->getItem('unknown'));
}
public function testRemoveAuthItem()
{
$this->assertTrue($this->auth->getItem('updatePost') instanceof Item);
$this->assertTrue($this->auth->removeItem('updatePost'));
$this->assertNull($this->auth->getItem('updatePost'));
$this->assertFalse($this->auth->removeItem('updatePost'));
}
public function testChangeItemName()
{
$item = $this->auth->getItem('readPost');
$this->assertTrue($item instanceof Item);
$this->assertTrue($this->auth->hasItemChild('reader', 'readPost'));
$item->name = 'readPost2';
$this->assertNull($this->auth->getItem('readPost'));
$this->assertEquals($this->auth->getItem('readPost2'), $item);
$this->assertFalse($this->auth->hasItemChild('reader', 'readPost'));
$this->assertTrue($this->auth->hasItemChild('reader', 'readPost2'));
}
public function testAddItemChild()
{
$this->auth->addItemChild('createPost', 'updatePost');
// test adding upper level item to lower one
$this->setExpectedException('Exception');
$this->auth->addItemChild('readPost', 'reader');
}
public function testAddItemChild2()
{
// test adding inexistent items
$this->setExpectedException('Exception');
$this->assertFalse($this->auth->addItemChild('createPost2', 'updatePost'));
}
public function testRemoveItemChild()
{
$this->assertTrue($this->auth->hasItemChild('reader', 'readPost'));
$this->assertTrue($this->auth->removeItemChild('reader', 'readPost'));
$this->assertFalse($this->auth->hasItemChild('reader', 'readPost'));
$this->assertFalse($this->auth->removeItemChild('reader', 'readPost'));
}
public function testGetItemChildren()
{
$this->assertEquals(array(), $this->auth->getItemChildren('readPost'));
$children = $this->auth->getItemChildren('author');
$this->assertEquals(3, count($children));
$this->assertTrue(reset($children) instanceof Item);
}
public function testAssign()
{
$auth = $this->auth->assign('new user', 'createPost', 'rule', 'data');
$this->assertTrue($auth instanceof Assignment);
$this->assertEquals($auth->userId, 'new user');
$this->assertEquals($auth->itemName, 'createPost');
$this->assertEquals($auth->bizRule, 'rule');
$this->assertEquals($auth->data, 'data');
$this->setExpectedException('Exception');
$this->auth->assign('new user', 'createPost2', 'rule', 'data');
}
public function testRevoke()
{
$this->assertTrue($this->auth->isAssigned('author B', 'author'));
$auth = $this->auth->getAssignment('author B', 'author');
$this->assertTrue($auth instanceof Assignment);
$this->assertTrue($this->auth->revoke('author B', 'author'));
$this->assertFalse($this->auth->isAssigned('author B', 'author'));
$this->assertFalse($this->auth->revoke('author B', 'author'));
}
public function testGetAssignments()
{
$this->auth->assign('author B', 'deletePost');
$auths = $this->auth->getAssignments('author B');
$this->assertEquals(2, count($auths));
$this->assertTrue(reset($auths) instanceof Assignment);
}
public function testGetItems()
{
$this->assertEquals(count($this->auth->getRoles()), 4);
$this->assertEquals(count($this->auth->getOperations()), 4);
$this->assertEquals(count($this->auth->getTasks()), 1);
$this->assertEquals(count($this->auth->getItems()), 9);
$this->assertEquals(count($this->auth->getItems('author B', null)), 1);
$this->assertEquals(count($this->auth->getItems('author C', null)), 0);
$this->assertEquals(count($this->auth->getItems('author B', Item::TYPE_ROLE)), 1);
$this->assertEquals(count($this->auth->getItems('author B', Item::TYPE_OPERATION)), 0);
}
public function testClearAll()
{
$this->auth->clearAll();
$this->assertEquals(count($this->auth->getRoles()), 0);
$this->assertEquals(count($this->auth->getOperations()), 0);
$this->assertEquals(count($this->auth->getTasks()), 0);
$this->assertEquals(count($this->auth->getItems()), 0);
$this->assertEquals(count($this->auth->getAssignments('author B')), 0);
}
public function testClearAssignments()
{
$this->auth->clearAssignments();
$this->assertEquals(count($this->auth->getAssignments('author B')), 0);
}
public function testDetectLoop()
{
$this->setExpectedException('Exception');
$this->auth->addItemChild('readPost', 'readPost');
}
public function testExecuteBizRule()
{
$this->assertTrue($this->auth->executeBizRule(null, array(), null));
$this->assertTrue($this->auth->executeBizRule('return 1 == true;', array(), null));
$this->assertTrue($this->auth->executeBizRule('return $params[0] == $params[1];', array(1, '1'), null));
$this->assertFalse($this->auth->executeBizRule('invalid', array(), null));
}
public function testCheckAccess()
{
$results = array(
'reader A' => array(
'createPost' => false,
'readPost' => true,
'updatePost' => false,
'updateOwnPost' => false,
'deletePost' => false,
),
'author B' => array(
'createPost' => true,
'readPost' => true,
'updatePost' => true,
'updateOwnPost' => true,
'deletePost' => false,
),
'editor C' => array(
'createPost' => false,
'readPost' => true,
'updatePost' => true,
'updateOwnPost' => false,
'deletePost' => false,
),
'admin D' => array(
'createPost' => true,
'readPost' => true,
'updatePost' => true,
'updateOwnPost' => false,
'deletePost' => true,
),
);
$params = array('authorID' => 'author B');
foreach (array('reader A', 'author B', 'editor C', 'admin D') as $user) {
$params['userID'] = $user;
foreach (array('createPost', 'readPost', 'updatePost', 'updateOwnPost', 'deletePost') as $operation) {
$result = $this->auth->checkAccess($user, $operation, $params);
$this->assertEquals($results[$user][$operation], $result);
}
}
}
protected function prepareData()
{
$this->auth->createOperation('createPost', 'create a post');
$this->auth->createOperation('readPost', 'read a post');
$this->auth->createOperation('updatePost', 'update a post');
$this->auth->createOperation('deletePost', 'delete a post');
$task = $this->auth->createTask('updateOwnPost', 'update a post by author himself', 'return $params["authorID"] == $params["userID"];');
$task->addChild('updatePost');
$role = $this->auth->createRole('reader');
$role->addChild('readPost');
$role = $this->auth->createRole('author');
$role->addChild('reader');
$role->addChild('createPost');
$role->addChild('updateOwnPost');
$role = $this->auth->createRole('editor');
$role->addChild('reader');
$role->addChild('updatePost');
$role = $this->auth->createRole('admin');
$role->addChild('editor');
$role->addChild('author');
$role->addChild('deletePost');
$this->auth->assign('reader A', 'reader');
$this->auth->assign('author B', 'author');
$this->auth->assign('editor C', 'editor');
$this->auth->assign('admin D', 'admin');
}
}

37
tests/unit/framework/rbac/PhpManagerTest.php

@ -0,0 +1,37 @@
<?php
namespace yiiunit\framework\rbac;
use Yii;
use yii\rbac\PhpManager;
//require_once(__DIR__ . '/ManagerTestBase.php');
class PhpManagerTest extends ManagerTestBase
{
protected function setUp()
{
parent::setUp();
$this->mockApplication();
$authFile = Yii::$app->getRuntimePath() . '/rbac.php';
@unlink($authFile);
$this->auth = new PhpManager;
$this->auth->authFile = $authFile;
$this->auth->init();
$this->prepareData();
}
protected function tearDown()
{
parent::tearDown();
@unlink($this->auth->authFile);
}
public function testSaveLoad()
{
$this->auth->save();
$this->auth->clearAll();
$this->auth->load();
$this->testCheckAccess();
}
}

2
tests/web/app/index.php

@ -1,6 +1,6 @@
<?php
require(__DIR__ . '/../../../framework/yii.php');
require(__DIR__ . '/../../../yii/Yii.php');
$application = new yii\web\Application('test', __DIR__ . '/protected');
$application->run();

0
framework/.htaccess → yii/.htaccess

2
framework/yii.php → yii/Yii.php

@ -18,7 +18,7 @@ require(__DIR__ . '/YiiBase.php');
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Yii extends YiiBase
class Yii extends \yii\YiiBase
{
}

15
framework/YiiBase.php → yii/YiiBase.php

@ -4,6 +4,8 @@
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii;
use yii\base\Exception;
use yii\base\InvalidConfigException;
use yii\base\InvalidParamException;
@ -60,7 +62,7 @@ class YiiBase
*/
public static $enableIncludePath = true;
/**
* @var yii\console\Application|yii\web\Application the application instance
* @var \yii\console\Application|\yii\web\Application the application instance
*/
public static $app;
/**
@ -156,8 +158,8 @@ class YiiBase
{
foreach ($namespaces as $name => $path) {
if ($name !== '') {
$name = '@' . str_replace('\\', '/', $name);
static::setAlias($name, $path);
$name = trim(strtr($name, array('\\' => '/', '_' => '/')), '/');
static::setAlias('@' . $name, rtrim($path, '/\\') . '/' . $name);
}
}
}
@ -368,7 +370,8 @@ class YiiBase
include($classFile);
if (class_exists($className, false) || interface_exists($className, false)) {
if (class_exists($className, false) || interface_exists($className, false) ||
function_exists('trait_exists') && trait_exists($className, false)) {
return true;
} else {
throw new UnknownClassException("Unable to find '$className' in file: $classFile");
@ -449,12 +452,12 @@ class YiiBase
}
$args = func_get_args();
array_shift($args); // remove $config
if ($config !== array()) {
if (!empty($config)) {
$args[] = $config;
}
return $reflection->newInstanceArgs($args);
} else {
return $config === array() ? new $class : new $class($config);
return empty($config) ? new $class : new $class($config);
}
}

14
framework/assets.php → yii/assets.php

@ -28,4 +28,18 @@ return array(
),
'depends' => array('yii', 'yii/validation'),
),
'yii/captcha' => array(
'sourcePath' => __DIR__ . '/assets',
'js' => array(
'yii.captcha.js',
),
'depends' => array('yii'),
),
'yii/debug' => array(
'sourcePath' => __DIR__ . '/assets',
'js' => array(
'yii.debug.js',
),
'depends' => array('yii'),
),
);

0
framework/assets/jquery.min.js → yii/assets/jquery.min.js vendored

4
framework/assets/yii.activeForm.js → yii/assets/yii.activeForm.js

@ -116,8 +116,8 @@
});
},
options: function() {
return this.data('yiiActiveForm').settings;
data: function() {
return this.data('yiiActiveForm');
},
submitForm: function () {

72
yii/assets/yii.captcha.js

@ -0,0 +1,72 @@
/**
* Yii Captcha widget.
*
* This is the JavaScript widget used by the yii\widgets\Captcha widget.
*
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
(function ($) {
$.fn.yiiCaptcha = function (method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.yiiCaptcha');
return false;
}
};
var defaults = {
refreshUrl: undefined,
hashKey: undefined
};
var methods = {
init: function (options) {
return this.each(function () {
var $e = $(this);
var settings = $.extend({}, defaults, options || {});
$e.data('yiiCaptcha', {
settings: settings
});
$e.on('click.yiiCaptcha', function() {
methods.refresh.apply($e);
return false;
});
});
},
refresh: function () {
var $e = this,
settings = this.data('yiiCaptcha').settings;
$.ajax({
url: $e.data('yiiCaptcha').settings.refreshUrl,
dataType: 'json',
cache: false,
success: function(data) {
$e.attr('src', data['url']);
$('body').data(settings.hashKey, [data['hash1'], data['hash2']]);
}
});
},
destroy: function () {
return this.each(function () {
$(window).unbind('.yiiCaptcha');
$(this).removeData('yiiCaptcha');
});
},
data: function() {
return this.data('yiiCaptcha');
}
};
})(window.jQuery);

26
yii/assets/yii.debug.js

@ -0,0 +1,26 @@
/**
* Yii debug module.
*
* This JavaScript module provides the functions needed by the Yii debug toolbar.
*
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
yii.debug = (function ($) {
return {
load: function (id, url) {
$.ajax({
url: url,
//dataType: 'json',
success: function(data) {
var $e = $('#' + id);
$e.html(data);
}
});
}
};
})(jQuery);

0
framework/assets/yii.js → yii/assets/yii.js

2
framework/assets/yii.validation.js → yii/assets/yii.validation.js

@ -1,7 +1,7 @@
/**
* Yii validation module.
*
* This JavaScript module provides the validation methods for the built-in validaotrs.
* This JavaScript module provides the validation methods for the built-in validators.
*
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC

0
framework/base/Action.php → yii/base/Action.php

0
framework/base/ActionEvent.php → yii/base/ActionEvent.php

0
framework/base/ActionFilter.php → yii/base/ActionFilter.php

16
framework/base/Application.php → yii/base/Application.php

@ -8,7 +8,6 @@
namespace yii\base;
use Yii;
use yii\helpers\FileHelper;
/**
* Application is the base class for all application classes.
@ -86,6 +85,13 @@ class Application extends Module
throw new InvalidConfigException('The "basePath" configuration is required.');
}
if (isset($config['timeZone'])) {
$this->setTimeZone($config['timeZone']);
unset($config['timeZone']);
} elseif (!ini_get('date.timezone')) {
$this->setTimeZone('UTC');
}
$this->registerErrorHandlers();
$this->registerCoreComponents();
@ -223,6 +229,8 @@ class Application extends Module
/**
* Returns the time zone used by this application.
* This is a simple wrapper of PHP function date_default_timezone_get().
* If time zone is not configured in php.ini or application config,
* it will be set to UTC by default.
* @return string the time zone used by this application.
* @see http://php.net/manual/en/function.date-default-timezone-get.php
*/
@ -306,12 +314,12 @@ class Application extends Module
}
/**
* @return null|Component
* @todo
* Returns the auth manager for this application.
* @return \yii\rbac\Manager the auth manager for this application.
*/
public function getAuthManager()
{
return $this->getComponent('auth');
return $this->getComponent('authManager');
}
/**

0
framework/base/Behavior.php → yii/base/Behavior.php

0
framework/base/Component.php → yii/base/Component.php

11
framework/base/Controller.php → yii/base/Controller.php

@ -8,7 +8,6 @@
namespace yii\base;
use Yii;
use yii\helpers\FileHelper;
use yii\helpers\StringHelper;
/**
@ -183,7 +182,7 @@ class Controller extends Component
}
}
if ($missing !== array()) {
if (!empty($missing)) {
throw new InvalidRequestException(Yii::t('yii|Missing required parameters: {params}', array(
'{params}' => implode(', ', $missing),
)));
@ -204,7 +203,7 @@ class Controller extends Component
public function forward($route, $params = array())
{
$status = $this->run($route, $params);
exit($status);
Yii::$app->end($status);
}
/**
@ -279,7 +278,7 @@ class Controller extends Component
* Child classes may override this method to throw exceptions when there are missing and/or unknown parameters.
* @param Action $action the currently requested action
* @param array $missingParams the names of the missing parameters
* @param array $unknownParams the unknown parameters (name=>value)
* @param array $unknownParams the unknown parameters (name => value)
*/
public function validateActionParams($action, $missingParams, $unknownParams)
{
@ -319,10 +318,10 @@ class Controller extends Component
/** @var Model $model */
$scope = $model->formName();
if ($scope == '') {
$model->attributes = $data;
$model->setAttributes($data);
$success = true;
} elseif (isset($data[$scope])) {
$model->attributes = $data[$scope];
$model->setAttributes($data[$scope]);
$success = true;
}
}

6
framework/base/Dictionary.php → yii/base/Dictionary.php

@ -24,7 +24,7 @@ use yii\helpers\ArrayHelper;
* $dictionary[$key] = $value; // add a key-value pair
* unset($dictionary[$key]); // remove the value with the specified key
* if (isset($dictionary[$key])) // if the dictionary contains the key
* foreach ($dictionary as $key=>$value) // traverse the items in the dictionary
* foreach ($dictionary as $key => $value) // traverse the items in the dictionary
* $n = count($dictionary); // returns the number of items in the dictionary
* ~~~
*
@ -51,7 +51,7 @@ class Dictionary extends Object implements \IteratorAggregate, \ArrayAccess, \Co
*/
public function __construct($data = array(), $config = array())
{
if ($data !== array()) {
if (!empty($data)) {
$this->copyFrom($data);
}
parent::__construct($config);
@ -187,7 +187,7 @@ class Dictionary extends Object implements \IteratorAggregate, \ArrayAccess, \Co
public function copyFrom($data)
{
if (is_array($data) || $data instanceof \Traversable) {
if ($this->_d !== array()) {
if (!empty($this->_d)) {
$this->removeAll();
}
if ($data instanceof self) {

0
framework/base/DictionaryIterator.php → yii/base/DictionaryIterator.php

0
framework/base/ErrorException.php → yii/base/ErrorException.php

4
framework/base/ErrorHandler.php → yii/base/ErrorHandler.php

@ -83,7 +83,7 @@ class ErrorHandler extends Component
} else {
// if there is an error during error rendering it's useful to
// display PHP error in debug mode instead of a blank screen
if(YII_DEBUG) {
if (YII_DEBUG) {
ini_set('display_errors', 1);
}
@ -231,7 +231,7 @@ class ErrorHandler extends Component
echo '<div class="plus">+</div><div class="minus">-</div>';
}
echo '&nbsp;';
if(isset($t['file'])) {
if (isset($t['file'])) {
echo $this->htmlEncode($t['file']) . '(' . $t['line'] . '): ';
}
if (!empty($t['class'])) {

0
framework/base/Event.php → yii/base/Event.php

0
framework/base/Exception.php → yii/base/Exception.php

5
framework/base/HttpException.php → yii/base/HttpException.php

@ -100,9 +100,10 @@ class HttpException extends UserException
509 => 'Bandwidth Limit Exceeded',
);
if(isset($httpCodes[$this->statusCode]))
if (isset($httpCodes[$this->statusCode])) {
return $httpCodes[$this->statusCode];
else
} else {
return \Yii::t('yii|Error');
}
}
}

0
framework/base/InlineAction.php → yii/base/InlineAction.php

0
framework/base/InvalidCallException.php → yii/base/InvalidCallException.php

0
framework/base/InvalidConfigException.php → yii/base/InvalidConfigException.php

0
framework/base/InvalidParamException.php → yii/base/InvalidParamException.php

0
framework/base/InvalidRequestException.php → yii/base/InvalidRequestException.php

0
framework/base/InvalidRouteException.php → yii/base/InvalidRouteException.php

16
framework/base/Model.php → yii/base/Model.php

@ -33,7 +33,7 @@ use yii\validators\Validator;
* @property Vector $validators All the validators declared in the model.
* @property array $activeValidators The validators applicable to the current [[scenario]].
* @property array $errors Errors for all attributes or the specified attribute. Empty array is returned if no error.
* @property array $attributes Attribute values (name=>value).
* @property array $attributes Attribute values (name => value).
* @property string $scenario The scenario that this model is in.
*
* @author Qiang Xue <qiang.xue@gmail.com>
@ -76,7 +76,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
* array(
* 'attribute list',
* 'validator type',
* 'on'=>'scenario name',
* 'on' => 'scenario name',
* ...other parameters...
* )
* ~~~
@ -109,11 +109,11 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
* // built-in "required" validator
* array('username', 'required'),
* // built-in "length" validator customized with "min" and "max" properties
* array('username', 'length', 'min'=>3, 'max'=>12),
* array('username', 'length', 'min' => 3, 'max' => 12),
* // built-in "compare" validator that is used in "register" scenario only
* array('password', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'),
* array('password', 'compare', 'compareAttribute' => 'password2', 'on' => 'register'),
* // an inline validator defined via the "authenticate()" method in the model class
* array('password', 'authenticate', 'on'=>'login'),
* array('password', 'authenticate', 'on' => 'login'),
* // a validator of class "CaptchaValidator"
* array('captcha', 'CaptchaValidator'),
* );
@ -220,7 +220,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
* Note, in order to inherit labels defined in the parent class, a child class needs to
* merge the parent labels with child labels using functions such as `array_merge()`.
*
* @return array attribute labels (name=>label)
* @return array attribute labels (name => label)
* @see generateAttributeLabel
*/
public function attributeLabels()
@ -511,7 +511,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
* Defaults to null, meaning all attributes listed in [[attributes()]] will be returned.
* If it is an array, only the attributes in the array will be returned.
* @param array $except list of attributes whose value should NOT be returned.
* @return array attribute values (name=>value).
* @return array attribute values (name => value).
*/
public function getAttributes($names = null, $except = array())
{
@ -531,7 +531,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
/**
* Sets the attribute values in a massive way.
* @param array $values attribute values (name=>value) to be assigned to the model.
* @param array $values attribute values (name => value) to be assigned to the model.
* @param boolean $safeOnly whether the assignments should only be done to the safe attributes.
* A safe attribute is one that is associated with a validation rule in the current [[scenario]].
* @see safeAttributes()

0
framework/base/ModelEvent.php → yii/base/ModelEvent.php

0
framework/base/Module.php → yii/base/Module.php

0
framework/base/NotSupportedException.php → yii/base/NotSupportedException.php

8
framework/base/Object.php → yii/base/Object.php

@ -15,6 +15,14 @@ namespace yii\base;
class Object
{
/**
* @return string the fully qualified name of this class.
*/
public static function className()
{
return get_called_class();
}
/**
* Constructor.
* The default implementation does two things:
*

0
framework/base/Request.php → yii/base/Request.php

0
framework/base/Response.php → yii/base/Response.php

0
framework/base/Theme.php → yii/base/Theme.php

0
framework/base/UnknownClassException.php → yii/base/UnknownClassException.php

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save