Browse Source

Merge branch 'master' into bootstrap

* master: (84 commits)
  remove from master (update track)
  minor fixes
  updated the README file of the bootstrap app.
  Inflector class
  readjusting files for the framework package.
  renamed files.
  Added punycode as part of the framework.
  Renamed core asset bundles.
  fixed wrong dependency name.
  Added bootstrap bundles.
  Reorganized the bootstrap app to prepare it as a separate composer package.
  Update Pagination.php
  "ManagerTestBase::testExecuteBizRule()" has been fixed to not produce side effects on other tests.
  changed back to "create_time".
  Always enable profiling for DB queries.
  Fixed SiteController about Yii.
  changed to insert_time.
  Minor doc fix.
  refactored AutoTimestamp.
  Added UrlRule::host.
  ...

Conflicts:
	yii/assets.php
tags/2.0.0-beta
Antonio Ramirez 12 years ago
parent
commit
105e277f8d
  1. 5
      .gitignore
  2. 2
      LICENSE.md
  3. 0
      README.md
  4. 32
      apps/bootstrap/LICENSE.md
  5. 61
      apps/bootstrap/README.md
  6. 29
      apps/bootstrap/commands/HelloController.php
  7. 23
      apps/bootstrap/composer.json
  8. 3
      apps/bootstrap/config/assets.php
  9. 26
      apps/bootstrap/config/console.php
  10. 18
      apps/bootstrap/config/main.php
  11. 5
      apps/bootstrap/config/params.php
  12. 3
      apps/bootstrap/controllers/SiteController.php
  13. 9
      apps/bootstrap/css/bootstrap-responsive.min.css
  14. 9
      apps/bootstrap/css/bootstrap.min.css
  15. 14
      apps/bootstrap/index.php
  16. 6
      apps/bootstrap/js/bootstrap.min.js
  17. 0
      apps/bootstrap/models/ContactForm.php
  18. 0
      apps/bootstrap/models/LoginForm.php
  19. 0
      apps/bootstrap/models/User.php
  20. 1
      apps/bootstrap/protected/.htaccess
  21. 4
      apps/bootstrap/requirements.php
  22. 0
      apps/bootstrap/runtime/.gitignore
  23. 0
      apps/bootstrap/vendor/.gitignore
  24. 6
      apps/bootstrap/views/layouts/main.php
  25. 0
      apps/bootstrap/views/site/about.php
  26. 4
      apps/bootstrap/views/site/contact.php
  27. 0
      apps/bootstrap/views/site/index.php
  28. 2
      apps/bootstrap/views/site/login.php
  29. 1
      apps/bootstrap/www/assets/.gitignore
  30. 0
      apps/bootstrap/www/css/site.css
  31. 12
      apps/bootstrap/www/index.php
  32. 23
      apps/bootstrap/yii
  33. 8
      apps/bootstrap/yii.bat
  34. 2
      build/build
  35. 2
      build/build.xml
  36. 2
      build/controllers/LocaleController.php
  37. 212
      composer.lock
  38. 4
      docs/api/db/ActiveRecord.md
  39. 9
      docs/guide/bootstrap.md
  40. 28
      docs/guide/migration.md
  41. 10
      docs/guide/upgrade-from-v1.md
  42. 25
      docs/internals/ar.md
  43. 3
      tests/unit/framework/YiiBaseTest.php
  44. 209
      tests/unit/framework/base/DictionaryTest.php
  45. 2
      tests/unit/framework/base/ModelTest.php
  46. 230
      tests/unit/framework/base/VectorTest.php
  47. 237
      tests/unit/framework/console/controllers/AssetControllerTest.php
  48. 41
      tests/unit/framework/helpers/ArrayHelperTest.php
  49. 2
      tests/unit/framework/rbac/ManagerTestBase.php
  50. 86
      tests/unit/framework/web/ResponseTest.php
  51. 26
      tests/unit/framework/web/UrlManagerTest.php
  52. 28
      tests/unit/framework/web/UrlRuleTest.php
  53. 46
      upgrade.md
  54. 0
      yii/CHANGELOG.md
  55. 32
      yii/LICENSE.md
  56. 21
      yii/README.md
  57. 9
      yii/UPGRADE.md
  58. 21
      yii/YiiBase.php
  59. 36
      yii/assets.php
  60. 0
      yii/assets/bootstrap/css/bootstrap-responsive.css
  61. 0
      yii/assets/bootstrap/css/bootstrap.css
  62. 0
      yii/assets/bootstrap/img/glyphicons-halflings-white.png
  63. 0
      yii/assets/bootstrap/img/glyphicons-halflings.png
  64. 0
      yii/assets/bootstrap/js/bootstrap.js
  65. 278
      yii/assets/punycode/LICENSE-GPL.txt
  66. 20
      yii/assets/punycode/LICENSE-MIT.txt
  67. 2
      yii/assets/punycode/punycode.min.js
  68. 2
      yii/assets/yii.activeForm.js
  69. 4
      yii/assets/yii.captcha.js
  70. 2
      yii/assets/yii.debug.js
  71. 28
      yii/assets/yii.validation.js
  72. 3
      yii/base/Controller.php
  73. 297
      yii/base/Dictionary.php
  74. 92
      yii/base/DictionaryIterator.php
  75. 28
      yii/base/ErrorException.php
  76. 7
      yii/base/ErrorHandler.php
  77. 2
      yii/base/Exception.php
  78. 2
      yii/base/HttpException.php
  79. 2
      yii/base/InvalidCallException.php
  80. 2
      yii/base/InvalidConfigException.php
  81. 2
      yii/base/InvalidParamException.php
  82. 2
      yii/base/InvalidRequestException.php
  83. 2
      yii/base/InvalidRouteException.php
  84. 63
      yii/base/Model.php
  85. 2
      yii/base/Module.php
  86. 2
      yii/base/NotSupportedException.php
  87. 2
      yii/base/UnknownClassException.php
  88. 2
      yii/base/UnknownMethodException.php
  89. 2
      yii/base/UnknownPropertyException.php
  90. 341
      yii/base/Vector.php
  91. 92
      yii/base/VectorIterator.php
  92. 13
      yii/base/View.php
  93. 66
      yii/base/Widget.php
  94. 106
      yii/behaviors/AutoTimestamp.php
  95. 6
      yii/composer.json
  96. 8
      yii/console/Application.php
  97. 8
      yii/console/Controller.php
  98. 2
      yii/console/Exception.php
  99. 2
      yii/console/Request.php
  100. 203
      yii/console/controllers/AssetController.php
  101. Some files were not shown because too many files have changed in this diff Show More

5
.gitignore vendored

@ -13,4 +13,7 @@ nbproject
Thumbs.db Thumbs.db
# composer vendor dir # composer vendor dir
/yii/vendor /yii/vendor
# composer itself is not needed
composer.phar

2
license.md → LICENSE.md

@ -1,7 +1,7 @@
The Yii framework is free software. It is released under the terms of The Yii framework is free software. It is released under the terms of
the following BSD License. the following BSD License.
Copyright © 2008-2012 by Yii Software LLC (http://www.yiisoft.com) Copyright © 2008-2013 by Yii Software LLC (http://www.yiisoft.com)
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

0
readme.md → README.md

32
apps/bootstrap/LICENSE.md

@ -0,0 +1,32 @@
The Yii framework is free software. It is released under the terms of
the following BSD License.
Copyright © 2008-2013 by Yii Software LLC (http://www.yiisoft.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Yii Software LLC nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

61
apps/bootstrap/README.md

@ -0,0 +1,61 @@
Yii 2 Bootstrap Application
===========================
**NOTE** Yii 2 and the relevant applications and extensions are still under heavy
development. We may make significant changes without prior notices. Please do not
use them for production. Please consider using [Yii v1.1](https://github.com/yiisoft/yii)
if you have a project to be deployed for production soon.
Thank you for choosing Yii 2 - the new generation of high-performance PHP framework.
The Yii 2 Bootstrap Application is a Web application template that you can easily customize
to fit for your needs. It is particularly suitable for small Websites which mainly contain
a few informational pages.
DIRECTORY STRUCTURE
-------------------
commands/ contains console commands (controllers)
config/ contains application configurations
controllers/ contains Web controller classes
models/ contains model classes
runtime/ contains files generated during runtime
vendor/ contains dependent 3rd-party packages
views/ contains view files for the Web application
www/ contains the entry script and Web resources
REQUIREMENTS
------------
The minimum requirement by Yii is that your Web server supports PHP 5.3.?.
INSTALLATION
------------
### Install via Composer
If you do not have [Composer](http://getcomposer.org/), you may download it from
[http://getcomposer.org/](http://getcomposer.org/) or run the following command on Linux/Unix/MacOS:
~~~
curl -s http://getcomposer.org/installer | php
~~~
You can then install the Bootstrap Application using the following command:
~~~
php composer.phar create-project --stability=dev yiisoft/yii2-bootstrap bootstrap
~~~
Now you should be able to access the Bootstrap Application using the URL `http://localhost/bootstrap/www/`,
assuming `bootstrap` is directly under the document root of your Web server.
### Install from an Archive File
This is not currently available. We will provide it when Yii 2 is formally released.

29
apps/bootstrap/commands/HelloController.php

@ -0,0 +1,29 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace app\commands;
use yii\console\Controller;
/**
* This command echos what the first argument that you have entered.
*
* This command is provided as an example for you to learn how to create console commands.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class HelloController extends Controller
{
/**
* This command echos what you have entered as the message.
* @param string $message the message to be echoed.
*/
public function actionIndex($message = 'hello world')
{
echo $message;
}
}

23
apps/bootstrap/composer.json

@ -0,0 +1,23 @@
{
"name": "yiisoft/yii2-bootstrap",
"description": "Yii 2 Bootstrap Application",
"keywords": ["yii", "framework", "bootstrap"],
"homepage": "http://www.yiiframework.com/",
"type": "project",
"license": "BSD-3-Clause",
"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": "vendor"
},
"minimum-stability": "dev",
"require": {
"php": ">=5.3.0",
"yiisoft/yii2": "dev-master"
}
}

3
apps/bootstrap/protected/config/assets.php → apps/bootstrap/config/assets.php

@ -5,8 +5,6 @@ return array(
'basePath' => '@wwwroot', 'basePath' => '@wwwroot',
'baseUrl' => '@www', 'baseUrl' => '@www',
'css' => array( 'css' => array(
'css/bootstrap.min.css',
'css/bootstrap-responsive.min.css',
'css/site.css', 'css/site.css',
), ),
'js' => array( 'js' => array(
@ -14,6 +12,7 @@ return array(
), ),
'depends' => array( 'depends' => array(
'yii', 'yii',
'yii/bootstrap-responsive',
), ),
), ),
); );

26
apps/bootstrap/config/console.php

@ -0,0 +1,26 @@
<?php
return array(
'id' => 'bootstrap-console',
'basePath' => dirname(__DIR__),
'preload' => array('log'),
'controllerPath' => dirname(__DIR__) . '/commands',
'controllerNamespace' => 'app\commands',
'modules' => array(
),
'components' => array(
'cache' => array(
'class' => 'yii\caching\FileCache',
),
'log' => array(
'class' => 'yii\logging\Router',
'targets' => array(
array(
'class' => 'yii\logging\FileTarget',
'levels' => array('error', 'warning'),
),
),
),
),
'params' => require(__DIR__ . '/params.php'),
);

18
apps/bootstrap/protected/config/main.php → apps/bootstrap/config/main.php

@ -1,13 +1,14 @@
<?php <?php
return array( return array(
'id' => 'hello', 'id' => 'bootstrap',
'basePath' => dirname(__DIR__), 'basePath' => dirname(__DIR__),
'preload' => array('log'), 'preload' => array('log'),
'controllerNamespace' => 'app\controllers',
'modules' => array( 'modules' => array(
'debug' => array( // 'debug' => array(
'class' => 'yii\debug\Module', // 'class' => 'yii\debug\Module',
) // )
), ),
'components' => array( 'components' => array(
'cache' => array( 'cache' => array(
@ -23,14 +24,15 @@ return array(
'log' => array( 'log' => array(
'class' => 'yii\logging\Router', 'class' => 'yii\logging\Router',
'targets' => array( 'targets' => array(
'file' => array( array(
'class' => 'yii\logging\FileTarget', 'class' => 'yii\logging\FileTarget',
'levels' => array('error', 'warning'), 'levels' => array('error', 'warning'),
), ),
// array(
// 'class' => 'yii\logging\DebugTarget',
// )
), ),
), ),
), ),
'params' => array( 'params' => require(__DIR__ . '/params.php'),
'adminEmail' => 'admin@example.com',
),
); );

5
apps/bootstrap/config/params.php

@ -0,0 +1,5 @@
<?php
return array(
'adminEmail' => 'admin@example.com',
);

3
apps/bootstrap/protected/controllers/SiteController.php → apps/bootstrap/controllers/SiteController.php

@ -1,5 +1,8 @@
<?php <?php
namespace app\controllers;
use Yii;
use yii\web\Controller; use yii\web\Controller;
use app\models\LoginForm; use app\models\LoginForm;
use app\models\ContactForm; use app\models\ContactForm;

9
apps/bootstrap/css/bootstrap-responsive.min.css vendored

File diff suppressed because one or more lines are too long

9
apps/bootstrap/css/bootstrap.min.css vendored

File diff suppressed because one or more lines are too long

14
apps/bootstrap/index.php

@ -1,14 +0,0 @@
<?php
// comment out the following line to disable debug mode
defined('YII_DEBUG') or define('YII_DEBUG', true);
$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);
$application->run();

6
apps/bootstrap/js/bootstrap.min.js vendored

File diff suppressed because one or more lines are too long

0
apps/bootstrap/protected/models/ContactForm.php → apps/bootstrap/models/ContactForm.php

0
apps/bootstrap/protected/models/LoginForm.php → apps/bootstrap/models/LoginForm.php

0
apps/bootstrap/protected/models/User.php → apps/bootstrap/models/User.php

1
apps/bootstrap/protected/.htaccess

@ -1 +0,0 @@
deny from all

4
apps/bootstrap/protected/requirements.php → apps/bootstrap/requirements.php

@ -11,7 +11,7 @@
*/ */
// you may need to adjust this path to the correct Yii framework path // you may need to adjust this path to the correct Yii framework path
$frameworkPath = dirname(__FILE__) . '/../../../yii'; $frameworkPath = dirname(__FILE__) . '/../../yii';
require_once($frameworkPath . '/requirements/YiiRequirementChecker.php'); require_once($frameworkPath . '/requirements/YiiRequirementChecker.php');
$requirementsChecker = new YiiRequirementChecker(); $requirementsChecker = new YiiRequirementChecker();
@ -93,4 +93,4 @@ $requirements = array(
'memo' => 'PHP mail SMTP server required', 'memo' => 'PHP mail SMTP server required',
), ),
); );
$requirementsChecker->checkYii()->check($requirements)->render(); $requirementsChecker->checkYii()->check($requirements)->render();

0
apps/bootstrap/assets/.gitignore → apps/bootstrap/runtime/.gitignore vendored

0
apps/bootstrap/protected/runtime/.gitignore → apps/bootstrap/vendor/.gitignore vendored

6
apps/bootstrap/protected/views/layouts/main.php → apps/bootstrap/views/layouts/main.php

@ -27,7 +27,7 @@ $this->registerAssetBundle('app');
<div class="navbar"> <div class="navbar">
<div class="navbar-inner"> <div class="navbar-inner">
<div class="container"> <div class="container">
<?php echo Menu::widget($this, array( <?php echo Menu::widget(array(
'options' => array('class' => 'nav'), 'options' => array('class' => 'nav'),
'items' => array( 'items' => array(
array('label' => 'Home', 'url' => array('/site/index')), array('label' => 'Home', 'url' => array('/site/index')),
@ -44,7 +44,7 @@ $this->registerAssetBundle('app');
<!-- /.navbar --> <!-- /.navbar -->
</div> </div>
<?php echo Breadcrumbs::widget($this, array( <?php echo Breadcrumbs::widget(array(
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : array(), 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : array(),
)); ?> )); ?>
<?php echo $content; ?> <?php echo $content; ?>
@ -60,7 +60,7 @@ $this->registerAssetBundle('app');
</div> </div>
<?php $this->endBody(); ?> <?php $this->endBody(); ?>
</div> </div>
<?php echo Toolbar::widget($this); ?> <?php echo Toolbar::widget(); ?>
</body> </body>
</html> </html>
<?php $this->endPage(); ?> <?php $this->endPage(); ?>

0
apps/bootstrap/protected/views/site/about.php → apps/bootstrap/views/site/about.php

4
apps/bootstrap/protected/views/site/contact.php → apps/bootstrap/views/site/contact.php

@ -23,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. If you have business inquiries or other questions, please fill out the following form to contact us. Thank you.
</p> </p>
<?php $form = ActiveForm::begin($this, array( <?php $form = ActiveForm::begin(array(
'options' => array('class' => 'form-horizontal'), 'options' => array('class' => 'form-horizontal'),
'fieldConfig' => array('inputOptions' => array('class' => 'input-xlarge')), 'fieldConfig' => array('inputOptions' => array('class' => 'input-xlarge')),
)); ?> )); ?>
@ -35,7 +35,7 @@ $this->params['breadcrumbs'][] = $this->title;
$field = $form->field($model, 'verifyCode'); $field = $form->field($model, 'verifyCode');
echo $field->begin() echo $field->begin()
. $field->label() . $field->label()
. Captcha::widget($this) . Captcha::widget()
. Html::activeTextInput($model, 'verifyCode', array('class' => 'input-medium')) . Html::activeTextInput($model, 'verifyCode', array('class' => 'input-medium'))
. $field->error() . $field->error()
. $field->end(); . $field->end();

0
apps/bootstrap/protected/views/site/index.php → apps/bootstrap/views/site/index.php

2
apps/bootstrap/protected/views/site/login.php → apps/bootstrap/views/site/login.php

@ -14,7 +14,7 @@ $this->params['breadcrumbs'][] = $this->title;
<p>Please fill out the following fields to login:</p> <p>Please fill out the following fields to login:</p>
<?php $form = ActiveForm::begin($this, array('options' => array('class' => 'form-horizontal'))); ?> <?php $form = ActiveForm::begin(array('options' => array('class' => 'form-horizontal'))); ?>
<?php echo $form->field($model, 'username')->textInput(); ?> <?php echo $form->field($model, 'username')->textInput(); ?>
<?php echo $form->field($model, 'password')->passwordInput(); ?> <?php echo $form->field($model, 'password')->passwordInput(); ?>
<?php echo $form->field($model, 'rememberMe')->checkbox(); ?> <?php echo $form->field($model, 'rememberMe')->checkbox(); ?>

1
apps/bootstrap/www/assets/.gitignore vendored

@ -0,0 +1 @@
*

0
apps/bootstrap/css/site.css → apps/bootstrap/www/css/site.css vendored

12
apps/bootstrap/www/index.php

@ -0,0 +1,12 @@
<?php
// comment out the following line to disable debug mode
defined('YII_DEBUG') or define('YII_DEBUG', true);
require(__DIR__ . '/../vendor/yiisoft/yii2/yii.php');
require(__DIR__ . '/../vendor/autoload.php');
$config = require(__DIR__ . '/../config/main.php');
$application = new yii\web\Application($config);
$application->run();

23
apps/bootstrap/yii

@ -0,0 +1,23 @@
#!/usr/bin/env php
<?php
/**
* Yii console bootstrap file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
defined('YII_DEBUG') or define('YII_DEBUG', true);
// fcgi doesn't have STDIN defined by default
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
$frameworkPath = __DIR__ . '/../../yii';
require($frameworkPath . '/Yii.php');
$config = require(__DIR__ . '/config/console.php');
$application = new yii\console\Application($config);
$application->run();

8
yii/yiic.bat → apps/bootstrap/yii.bat

@ -1,9 +1,7 @@
@echo off @echo off
rem ------------------------------------------------------------- rem -------------------------------------------------------------
rem Yii command line script for Windows. rem Yii command line bootstrap script for Windows.
rem
rem This is the bootstrap script for running yiic on Windows.
rem rem
rem @author Qiang Xue <qiang.xue@gmail.com> rem @author Qiang Xue <qiang.xue@gmail.com>
rem @link http://www.yiiframework.com/ rem @link http://www.yiiframework.com/
@ -17,6 +15,6 @@ set YII_PATH=%~dp0
if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
"%PHP_COMMAND%" "%YII_PATH%yiic" %* "%PHP_COMMAND%" "%YII_PATH%yii" %*
@endlocal @endlocal

2
build/build

@ -13,7 +13,7 @@ defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
require(__DIR__ . '/../yii/Yii.php'); require(__DIR__ . '/../yii/Yii.php');
$id = 'yiic-build'; $id = 'yii-build';
$basePath = __DIR__; $basePath = __DIR__;
$application = new yii\console\Application(array('id' => $id, 'basePath' => $basePath)); $application = new yii\console\Application(array('id' => $id, 'basePath' => $basePath));

2
build/build.xml

@ -94,7 +94,7 @@
</fileset> </fileset>
<fileset dir="." id="executables"> <fileset dir="." id="executables">
<include name="${build.src.dir}/**/yiic" /> <include name="${build.src.dir}/**/yii" />
</fileset> </fileset>
<target name="src" depends="sync"> <target name="src" depends="sync">

2
build/controllers/LocaleController.php

@ -93,7 +93,7 @@ class LocaleController extends Controller
/** /**
* Plural rules. * Plural rules.
* *
* This file is automatically generated by the "yiic locale/plural" command under the "build" folder. * This file is automatically generated by the "yii locale/plural" command under the "build" folder.
* Do not modify it directly. * Do not modify it directly.
* *
* The original plural rule data used for generating this file has the following copyright terms: * The original plural rule data used for generating this file has the following copyright terms:

212
composer.lock generated

@ -1,212 +0,0 @@
{
"_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": [
]
}

4
docs/api/db/ActiveRecord.md

@ -446,3 +446,7 @@ $customers = Customer::find()->olderThan(50)->all();
The parameters should follow after the `$query` parameter when defining the scope method, and they The parameters should follow after the `$query` parameter when defining the scope method, and they
can take default values like shown above. can take default values like shown above.
### Atomic operations and scenarios
TBD

9
docs/guide/bootstrap.md

@ -28,10 +28,9 @@ app/
img/ containing image files img/ containing image files
themes/ containing application themes themes/ containing application themes
protected/ containing protected application files protected/ containing protected application files
yiic yiic command line script for Unix/Linux yii yii command line script for Unix/Linux
yiic.bat yiic command line script for Windows yii.bat yii command line script for Windows
yiic.php yiic command line PHP script commands/ containing customized yii console commands
commands/ containing customized 'yiic' commands
components/ containing reusable user components components/ containing reusable user components
config/ containing configuration files config/ containing configuration files
console.php the console application configuration console.php the console application configuration
@ -60,4 +59,4 @@ app/
~~~ ~~~
TBD TBD

28
docs/guide/migration.md

@ -19,7 +19,7 @@ The following steps show how we can use database migration during development:
4. Doug applies the migration to his local development database 4. Doug applies the migration to his local development database
Yii supports database migration via the `yiic migrate` command line tool. This Yii supports database migration via the `yii migrate` command line tool. This
tool supports creating new migrations, applying/reverting/redoing migrations, and tool supports creating new migrations, applying/reverting/redoing migrations, and
showing migration history and new migrations. showing migration history and new migrations.
@ -29,7 +29,7 @@ Creating Migrations
To create a new migration (e.g. create a news table), we run the following command: To create a new migration (e.g. create a news table), we run the following command:
~~~ ~~~
yiic migrate/create <name> yii migrate/create <name>
~~~ ~~~
The required `name` parameter specifies a very brief description of the migration The required `name` parameter specifies a very brief description of the migration
@ -38,7 +38,7 @@ is used as part of a PHP class name. Therefore, it should only contain letters,
digits and/or underscore characters. digits and/or underscore characters.
~~~ ~~~
yiic migrate/create create_news_table yii migrate/create create_news_table
~~~ ~~~
The above command will create under the `protected/migrations` directory a new The above command will create under the `protected/migrations` directory a new
@ -153,7 +153,7 @@ To apply all available new migrations (i.e., make the local database up-to-date)
run the following command: run the following command:
~~~ ~~~
yiic migrate yii migrate
~~~ ~~~
The command will show the list of all new migrations. If you confirm to apply The command will show the list of all new migrations. If you confirm to apply
@ -170,7 +170,7 @@ Sometimes, we may only want to apply one or a few new migrations. We can use the
following command: following command:
~~~ ~~~
yiic migrate/up 3 yii migrate/up 3
~~~ ~~~
This command will apply the 3 new migrations. Changing the value 3 will allow This command will apply the 3 new migrations. Changing the value 3 will allow
@ -179,7 +179,7 @@ us to change the number of migrations to be applied.
We can also migrate the database to a specific version with the following command: We can also migrate the database to a specific version with the following command:
~~~ ~~~
yiic migrate/to 101129_185401 yii migrate/to 101129_185401
~~~ ~~~
That is, we use the timestamp part of a migration name to specify the version That is, we use the timestamp part of a migration name to specify the version
@ -196,7 +196,7 @@ To revert the last one or several applied migrations, we can use the following
command: command:
~~~ ~~~
yiic migrate/down [step] yii migrate/down [step]
~~~ ~~~
where the optional `step` parameter specifies how many migrations to be reverted where the optional `step` parameter specifies how many migrations to be reverted
@ -213,7 +213,7 @@ Redoing migrations means first reverting and then applying the specified migrati
This can be done with the following command: This can be done with the following command:
~~~ ~~~
yiic migrate/redo [step] yii migrate/redo [step]
~~~ ~~~
where the optional `step` parameter specifies how many migrations to be redone. where the optional `step` parameter specifies how many migrations to be redone.
@ -227,8 +227,8 @@ Besides applying and reverting migrations, the migration tool can also display
the migration history and the new migrations to be applied. the migration history and the new migrations to be applied.
~~~ ~~~
yiic migrate/history [limit] yii migrate/history [limit]
yiic migrate/new [limit] yii migrate/new [limit]
~~~ ~~~
where the optional parameter `limit` specifies the number of migrations to be where the optional parameter `limit` specifies the number of migrations to be
@ -247,10 +247,10 @@ often happens when developing a new migration. We can use the following command
to achieve this goal. to achieve this goal.
~~~ ~~~
yiic migrate/mark 101129_185401 yii migrate/mark 101129_185401
~~~ ~~~
This command is very similar to `yiic migrate/to` command, except that it only This command is very similar to `yii migrate/to` command, except that it only
modifies the migration history table to the specified version without applying modifies the migration history table to the specified version without applying
or reverting the migrations. or reverting the migrations.
@ -291,7 +291,7 @@ line:
To specify these options, execute the migrate command using the following format To specify these options, execute the migrate command using the following format
~~~ ~~~
yiic migrate/up --option1=value1 --option2=value2 ... yii migrate/up --option1=value1 --option2=value2 ...
~~~ ~~~
For example, if we want to migrate for a `forum` module whose migration files For example, if we want to migrate for a `forum` module whose migration files
@ -299,7 +299,7 @@ are located within the module's `migrations` directory, we can use the following
command: command:
~~~ ~~~
yiic migrate/up --migrationPath=ext.forum.migrations yii migrate/up --migrationPath=ext.forum.migrations
~~~ ~~~

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

@ -152,7 +152,7 @@ with significant improvements. For more details, please see the "assets" subsect
While Yii 2.0 continues to use PHP as its main template language, it comes with built-in While Yii 2.0 continues to use PHP as its main template language, it comes with built-in
support for two popular template engines: Smarty and Twig. The Prado template engine is support for two popular template engines: Smarty and Twig. The Prado template engine is
no longer supported. To use these template engines, you just need to use `tpl` as the file no longer supported. To use these template engines, you just need to use `tpl` as the file
extension for your Smarty views, or `twig` for Twig views. You may also configure the extension for your Smarty views, or `twig` for Twig views. You may also configure the
`View::renderers` property to use other template engines. `View::renderers` property to use other template engines.
@ -218,7 +218,7 @@ methods of the `Widget` class. For example,
```php ```php
// $this refers to the View object // $this refers to the View object
// Note that you have to "echo" the result to display it // Note that you have to "echo" the result to display it
echo \yii\widgets\Menu::widget($this, array('items' => $items)); echo \yii\widgets\Menu::widget(array('items' => $items));
// $this refers to the View object // $this refers to the View object
$form = \yii\widgets\ActiveForm::begin($this); $form = \yii\widgets\ActiveForm::begin($this);
@ -252,7 +252,7 @@ Console applications are now composed by controllers, too, like Web applications
console controllers and Web controllers share the same base controller class. console controllers and Web controllers share the same base controller class.
Each console controller is like `CConsoleCommand` in 1.1. It consists of one or several Each console controller is like `CConsoleCommand` in 1.1. It consists of one or several
actions. You use the `yiic <route>` command to execute a console command, where `<route>` actions. You use the `yii <route>` command to execute a console command, where `<route>`
stands for a controller route (e.g. `sitemap/index`). Additional anonymous arguments stands for a controller route (e.g. `sitemap/index`). Additional anonymous arguments
are passed as the parameters to the corresponding controller action method, and named arguments are passed as the parameters to the corresponding controller action method, and named arguments
are treated as global options declared in `globalOptions()`. are treated as global options declared in `globalOptions()`.
@ -269,10 +269,6 @@ Message translation is still supported, but managed via the "i18n" application c
The component manages a set of message sources, which allows you to use different message The component manages a set of message sources, which allows you to use different message
sources based on message categories. For more information, see the class documentation for `I18N`. sources based on message categories. For more information, see the class documentation for `I18N`.
The message translation method is changed by merging the message category into the message being
translated. For example, `Yii::t('yii|message to be translated')`.
Action Filters Action Filters
-------------- --------------

25
docs/internals/ar.md

@ -1,15 +1,32 @@
ActiveRecord ActiveRecord
============ ============
Scenarios
---------
Possible scenario formats supported by ActiveRecord:
```php
public function scenarios()
{
return array(
// attributes array, all operations won't be wrapped with transaction
'scenario1' => array('attribute1', 'attribute2'),
// insert and update operations will be wrapped with transaction, delete won't be wrapped
'scenario2' => array(
'attributes' => array('attribute1', 'attribute2'),
'atomic' => array(self::OP_INSERT, self::OP_UPDATE),
),
);
}
```
Query Query
----- -----
### Basic Queries ### Basic Queries
### Relational Queries ### Relational Queries
### Scopes ### Scopes

3
tests/unit/framework/YiiBaseTest.php

@ -45,6 +45,9 @@ class YiiBaseTest extends TestCase
Yii::setAlias('@yii', null); Yii::setAlias('@yii', null);
$this->assertFalse(Yii::getAlias('@yii', false)); $this->assertFalse(Yii::getAlias('@yii', false));
$this->assertEquals('/yii/gii/file', Yii::getAlias('@yii/gii/file')); $this->assertEquals('/yii/gii/file', Yii::getAlias('@yii/gii/file'));
Yii::setAlias('@some/alias', '/www');
$this->assertEquals('/www', Yii::getAlias('@some/alias'));
} }
public function testGetVersion() public function testGetVersion()

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

@ -1,209 +0,0 @@
<?php
namespace yiiunit\framework\base;
use yii\base\Dictionary;
class MapItem
{
public $data='data';
}
class DictionaryTest extends \yiiunit\TestCase
{
/**
* @var \yii\base\Dictionary
*/
protected $dictionary;
protected $item1;
protected $item2;
protected $item3;
protected function setUp()
{
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);
}
protected function tearDown()
{
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());
$dictionary2=new Dictionary($this->dictionary);
$this->assertEquals(2, $dictionary2->getCount());
}
public function testGetCount()
{
$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]);
}
public function testAdd()
{
$this->dictionary->add('key3', $this->item3);
$this->assertEquals(3, $this->dictionary->getCount());
$this->assertTrue($this->dictionary->has('key3'));
$this->dictionary[] = 'test';
}
public function testRemove()
{
$this->dictionary->remove('key1');
$this->assertEquals(1, $this->dictionary->getCount());
$this->assertTrue(!$this->dictionary->has('key1'));
$this->assertTrue($this->dictionary->remove('unknown key') === null);
}
public function testRemoveAll()
{
$this->dictionary->add('key3', $this->item3);
$this->dictionary->removeAll();
$this->assertEquals(0, $this->dictionary->getCount());
$this->assertTrue(!$this->dictionary->has('key1') && !$this->dictionary->has('key2'));
$this->dictionary->add('key3', $this->item3);
$this->dictionary->removeAll(true);
$this->assertEquals(0, $this->dictionary->getCount());
$this->assertTrue(!$this->dictionary->has('key1') && !$this->dictionary->has('key2'));
}
public function testHas()
{
$this->assertTrue($this->dictionary->has('key1'));
$this->assertTrue($this->dictionary->has('key2'));
$this->assertFalse($this->dictionary->has('key3'));
}
public function testFromArray()
{
$array = array('key3' => $this->item3, 'key4' => $this->item1);
$this->dictionary->copyFrom($array);
$this->assertEquals(2, $this->dictionary->getCount());
$this->assertEquals($this->item3, $this->dictionary['key3']);
$this->assertEquals($this->item1, $this->dictionary['key4']);
$this->setExpectedException('yii\base\InvalidParamException');
$this->dictionary->copyFrom($this);
}
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);
$dictionary->mergeWith($dictionary2);
$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']);
$this->setExpectedException('yii\base\InvalidParamException');
$this->dictionary->mergeWith($this, false);
}
public function testRecursiveMergeWithTraversable(){
$dictionary = new Dictionary();
$obj = new \ArrayObject(array(
'k1' => $this->item1,
'k2' => $this->item2,
'k3' => new \ArrayObject(array(
'k4' => $this->item3,
))
));
$dictionary->mergeWith($obj, true);
$this->assertEquals(3, $dictionary->getCount());
$this->assertEquals($this->item1, $dictionary['k1']);
$this->assertEquals($this->item2, $dictionary['k2']);
$this->assertEquals($this->item3, $dictionary['k3']['k4']);
}
public function testArrayRead()
{
$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['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->assertTrue(!$this->dictionary->has('key2'));
unset($this->dictionary['unknown key']);
}
public function testArrayForeach()
{
$n = 0;
$found = 0;
foreach ($this->dictionary as $index => $item) {
$n++;
if ($index === 'key1' && $item === $this->item1) {
$found++;
}
if ($index === 'key2' && $item === $this->item2) {
$found++;
}
}
$this->assertTrue($n == 2 && $found == 2);
}
public function testArrayMisc()
{
$this->assertEquals($this->dictionary->Count, count($this->dictionary));
$this->assertTrue(isset($this->dictionary['key1']));
$this->assertFalse(isset($this->dictionary['unknown key']));
}
public function testToArray()
{
$dictionary = new Dictionary(array('key' => 'value'));
$this->assertEquals(array('key' => 'value'), $dictionary->toArray());
}
public function testIteratorCurrent()
{
$dictionary = new Dictionary(array('key1' => 'value1', 'key2' => 'value2'));
$val = $dictionary->getIterator()->current();
$this->assertEquals('value1', $val);
}
}

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

@ -12,7 +12,7 @@ use yiiunit\data\base\InvalidRulesModel;
*/ */
class ModelTest extends TestCase class ModelTest extends TestCase
{ {
public function testGetAttributeLalel() public function testGetAttributeLabel()
{ {
$speaker = new Speaker(); $speaker = new Speaker();
$this->assertEquals('First Name', $speaker->getAttributeLabel('firstName')); $this->assertEquals('First Name', $speaker->getAttributeLabel('firstName'));

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

@ -1,230 +0,0 @@
<?php
namespace yiiunit\framework\base;
use yii\base\Vector;
class ListItem
{
public $data='data';
}
class VectorTest extends \yiiunit\TestCase
{
/**
* @var Vector
*/
protected $vector;
protected $item1;
protected $item2;
protected $item3;
protected function setUp()
{
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);
}
protected function tearDown()
{
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());
}
public function testItemAt()
{
$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));
$this->assertEquals(4, $vector->itemAt(3));
}
public function testGetCount()
{
$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));
}
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->setExpectedException('yii\base\InvalidParamException');
$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(false, $this->vector->remove($this->item1));
}
public function testRemoveAt()
{
$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->setExpectedException('yii\base\InvalidParamException');
$this->vector->removeAt(2);
}
public function testRemoveAll()
{
$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->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));
}
public function testHas()
{
$this->assertTrue($this->vector->has($this->item1));
$this->assertTrue($this->vector->has($this->item2));
$this->assertFalse($this->vector->has($this->item3));
}
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));
}
public function testFromArray()
{
$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->setExpectedException('yii\base\InvalidParamException');
$this->vector->copyFrom($this);
}
public function testMergeWith()
{
$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);
$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->setExpectedException('yii\base\InvalidParamException');
$this->vector->mergeWith($this);
}
public function testToArray()
{
$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->setExpectedException('yii\base\InvalidParamException');
$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++;
if ($index === 0 && $item === $this->item1) {
$found++;
}
if ($index === 1 && $item === $this->item2) {
$found++;
}
}
$this->assertTrue($n == 2 && $found == 2);
}
public function testArrayMisc()
{
$this->assertEquals($this->vector->Count, count($this->vector));
$this->assertTrue(isset($this->vector[1]));
$this->assertFalse(isset($this->vector[2]));
}
public function testOffsetSetAdd()
{
$vector = new Vector(array(1, 2, 3));
$vector->offsetSet(null, 4);
$this->assertEquals(array(1, 2, 3, 4), $vector->toArray());
}
public function testOffsetSetReplace()
{
$vector = new Vector(array(1, 2, 3));
$vector->offsetSet(1, 4);
$this->assertEquals(array(1, 4, 3), $vector->toArray());
}
public function testOffsetUnset()
{
$vector = new Vector(array(1, 2, 3));
$vector->offsetUnset(1);
$this->assertEquals(array(1, 3), $vector->toArray());
}
public function testIteratorCurrent()
{
$vector = new Vector(array('value1', 'value2'));
$val = $vector->getIterator()->current();
$this->assertEquals('value1', $val);
}
}

237
tests/unit/framework/console/controllers/AssetControllerTest.php

@ -0,0 +1,237 @@
<?php
use yiiunit\TestCase;
use yii\console\controllers\AssetController;
/**
* Unit test for [[\yii\console\controllers\AssetController]].
* @see AssetController
*/
class AssetControllerTest extends TestCase
{
/**
* @var string path for the test files.
*/
protected $testFilePath = '';
/**
* @var string test assets path.
*/
protected $testAssetsBasePath = '';
public function setUp()
{
$this->testFilePath = Yii::getAlias('@yiiunit/runtime') . DIRECTORY_SEPARATOR . get_class($this);
$this->createDir($this->testFilePath);
$this->testAssetsBasePath = $this->testFilePath . DIRECTORY_SEPARATOR . 'assets';
$this->createDir($this->testAssetsBasePath);
}
public function tearDown()
{
$this->removeDir($this->testFilePath);
}
/**
* Creates directory.
* @param $dirName directory full name.
*/
protected function createDir($dirName)
{
if (!file_exists($dirName)) {
mkdir($dirName, 0777, true);
}
}
/**
* Removes directory.
* @param $dirName directory full name
*/
protected function removeDir($dirName)
{
if (!empty($dirName) && file_exists($dirName)) {
exec("rm -rf {$dirName}");
}
}
/**
* Creates test asset controller instance.
* @return AssetController
*/
protected function createAssetController()
{
$module = $this->getMock('yii\\base\\Module', array('fake'), array('console'));
$assetController = new AssetController('asset', $module);
$assetController->interactive = false;
$assetController->jsCompressor = 'cp {from} {to}';
$assetController->cssCompressor = 'cp {from} {to}';
return $assetController;
}
/**
* Emulates running of the asset controller action.
* @param string $actionId id of action to be run.
* @param array $args action arguments.
* @return string command output.
*/
protected function runAssetControllerAction($actionId, array $args=array())
{
$controller = $this->createAssetController();
ob_start();
ob_implicit_flush(false);
$params = array(
\yii\console\Request::ANONYMOUS_PARAMS => $args
);
$controller->run($actionId, $params);
return ob_get_clean();
}
/**
* Creates test compress config.
* @param array[] $bundles asset bundles config.
* @return array config array.
*/
protected function createCompressConfig(array $bundles)
{
$baseUrl = '/test';
$config = array(
'bundles' => $this->createBundleConfig($bundles),
'targets' => array(
'all' => array(
'basePath' => $this->testAssetsBasePath,
'baseUrl' => $baseUrl,
'js' => 'all.js',
'css' => 'all.css',
),
),
'assetManager' => array(
'basePath' => $this->testAssetsBasePath,
'baseUrl' => $baseUrl,
),
);
return $config;
}
/**
* Creates test bundle configuration.
* @param array[] $bundles asset bundles config.
* @return array bundle config.
*/
protected function createBundleConfig(array $bundles)
{
foreach ($bundles as $name => $config) {
if (!array_key_exists('basePath', $config)) {
$bundles[$name]['basePath'] = $this->testFilePath;
}
if (!array_key_exists('baseUrl', $config)) {
$bundles[$name]['baseUrl'] = '';
}
}
return $bundles;
}
/**
* Creates test compress config file.
* @param string $fileName output file name.
* @param array[] $bundles asset bundles config.
* @throws Exception on failure.
*/
protected function createCompressConfigFile($fileName, array $bundles)
{
$content = '<?php return '.var_export($this->createCompressConfig($bundles), true).';';
if (file_put_contents($fileName, $content) <= 0) {
throw new \Exception("Unable to create file '{$fileName}'!");
}
}
/**
* Creates test asset file.
* @param string $fileRelativeName file name relative to [[testFilePath]]
* @param string $content file content
* @throws Exception on failure.
*/
protected function createAssetSourceFile($fileRelativeName, $content)
{
$fileFullName = $this->testFilePath.DIRECTORY_SEPARATOR.$fileRelativeName;
$this->createDir(dirname($fileFullName));
if (file_put_contents($fileFullName, $content)<=0) {
throw new \Exception("Unable to create file '{$fileFullName}'!");
}
}
/**
* Creates a list of asset source files.
* @param array $files assert source files in format: file/relative/name => fileContent
*/
protected function createAssertSourceFiles(array $files)
{
foreach ($files as $name => $content) {
$this->createAssetSourceFile($name, $content);
}
}
// Tests :
public function testActionTemplate()
{
$configFileName = $this->testFilePath . DIRECTORY_SEPARATOR . 'config.php';
$this->runAssetControllerAction('template', array($configFileName));
$this->assertTrue(file_exists($configFileName), 'Unable to create config file template!');
}
public function testActionCompress()
{
// Given :
$cssFiles = array(
'css/test_body.css' => 'body {
padding-top: 20px;
padding-bottom: 60px;
}',
'css/test_footer.css' => '.footer {
margin: 20px;
display: block;
}',
);
$this->createAssertSourceFiles($cssFiles);
$jsFiles = array(
'js/test_alert.js' => "function test() {
alert('Test message');
}",
'js/test_sum_ab.js' => "function sumAB(a, b) {
return a + b;
}",
);
$this->createAssertSourceFiles($jsFiles);
$bundles = array(
'app' => array(
'css' => array_keys($cssFiles),
'js' => array_keys($jsFiles),
),
);;
$bundleFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'bundle.php';
$configFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'config.php';
$this->createCompressConfigFile($configFile, $bundles);
// When :
$this->runAssetControllerAction('compress', array($configFile, $bundleFile));
// Then :
$this->assertTrue(file_exists($bundleFile), 'Unable to create output bundle file!');
$compressedCssFileName = $this->testAssetsBasePath . DIRECTORY_SEPARATOR . 'all.css';
$this->assertTrue(file_exists($compressedCssFileName), 'Unable to compress CSS files!');
$compressedJsFileName = $this->testAssetsBasePath . DIRECTORY_SEPARATOR . 'all.js';
$this->assertTrue(file_exists($compressedJsFileName), 'Unable to compress JS files!');
$compressedCssFileContent = file_get_contents($compressedCssFileName);
foreach ($cssFiles as $name => $content) {
$this->assertContains($content, $compressedCssFileContent, "Source of '{$name}' is missing in combined file!");
}
$compressedJsFileContent = file_get_contents($compressedJsFileName);
foreach ($jsFiles as $name => $content) {
$this->assertContains($content, $compressedJsFileContent, "Source of '{$name}' is missing in combined file!");
}
}
}

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

@ -3,6 +3,8 @@
namespace yiiunit\framework\helpers; namespace yiiunit\framework\helpers;
use yii\helpers\ArrayHelper; use yii\helpers\ArrayHelper;
use yii\helpers\VarDumper;
use yii\web\Sort;
class ArrayHelperTest extends \yii\test\TestCase class ArrayHelperTest extends \yii\test\TestCase
{ {
@ -54,16 +56,51 @@ class ArrayHelperTest extends \yii\test\TestCase
array('name' => 'A', 'age' => 1), array('name' => 'A', 'age' => 1),
); );
ArrayHelper::multisort($array, array('name', 'age'), SORT_ASC, array(SORT_STRING, SORT_REGULAR)); ArrayHelper::multisort($array, array('name', 'age'), false, array(SORT_STRING, SORT_REGULAR));
$this->assertEquals(array('name' => 'A', 'age' => 1), $array[0]); $this->assertEquals(array('name' => 'A', 'age' => 1), $array[0]);
$this->assertEquals(array('name' => 'B', 'age' => 4), $array[1]); $this->assertEquals(array('name' => 'B', 'age' => 4), $array[1]);
$this->assertEquals(array('name' => 'a', 'age' => 3), $array[2]); $this->assertEquals(array('name' => 'a', 'age' => 3), $array[2]);
$this->assertEquals(array('name' => 'b', 'age' => 2), $array[3]); $this->assertEquals(array('name' => 'b', 'age' => 2), $array[3]);
ArrayHelper::multisort($array, array('name', 'age'), SORT_ASC, array(SORT_STRING, SORT_REGULAR), false); ArrayHelper::multisort($array, array('name', 'age'), false, array(SORT_STRING, SORT_REGULAR), false);
$this->assertEquals(array('name' => 'A', 'age' => 1), $array[0]); $this->assertEquals(array('name' => 'A', 'age' => 1), $array[0]);
$this->assertEquals(array('name' => 'a', 'age' => 3), $array[1]); $this->assertEquals(array('name' => 'a', 'age' => 3), $array[1]);
$this->assertEquals(array('name' => 'b', 'age' => 2), $array[2]); $this->assertEquals(array('name' => 'b', 'age' => 2), $array[2]);
$this->assertEquals(array('name' => 'B', 'age' => 4), $array[3]); $this->assertEquals(array('name' => 'B', 'age' => 4), $array[3]);
} }
public function testMultisortUseSort()
{
// single key
$sort = new Sort();
$sort->attributes = array('name', 'age');
$sort->defaults = array('name' => Sort::ASC);
$orders = $sort->getOrders();
$array = array(
array('name' => 'b', 'age' => 3),
array('name' => 'a', 'age' => 1),
array('name' => 'c', 'age' => 2),
);
ArrayHelper::multisort($array, array_keys($orders), array_values($orders));
$this->assertEquals(array('name' => 'a', 'age' => 1), $array[0]);
$this->assertEquals(array('name' => 'b', 'age' => 3), $array[1]);
$this->assertEquals(array('name' => 'c', 'age' => 2), $array[2]);
// multiple keys
$sort = new Sort();
$sort->attributes = array('name', 'age');
$sort->defaults = array('name' => Sort::ASC, 'age' => Sort::DESC);
$orders = $sort->getOrders();
$array = array(
array('name' => 'b', 'age' => 3),
array('name' => 'a', 'age' => 2),
array('name' => 'a', 'age' => 1),
);
ArrayHelper::multisort($array, array_keys($orders), array_values($orders));
$this->assertEquals(array('name' => 'a', 'age' => 2), $array[0]);
$this->assertEquals(array('name' => 'a', 'age' => 1), $array[1]);
$this->assertEquals(array('name' => 'b', 'age' => 3), $array[2]);
}
} }

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

@ -167,7 +167,7 @@ abstract class ManagerTestBase extends TestCase
$this->assertTrue($this->auth->executeBizRule(null, array(), null)); $this->assertTrue($this->auth->executeBizRule(null, array(), null));
$this->assertTrue($this->auth->executeBizRule('return 1 == true;', 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->assertTrue($this->auth->executeBizRule('return $params[0] == $params[1];', array(1, '1'), null));
$this->assertFalse($this->auth->executeBizRule('invalid', array(), null)); $this->assertFalse($this->auth->executeBizRule('invalid;', array(), null));
} }
public function testCheckAccess() public function testCheckAccess()

86
tests/unit/framework/web/ResponseTest.php

@ -0,0 +1,86 @@
<?php
namespace yii\web;
use yiiunit\framework\web\ResponseTest;
/**
* Mock PHP header function to check for sent headers
* @param string $string
* @param bool $replace
* @param int $httpResponseCode
*/
function header($string, $replace = true, $httpResponseCode = null) {
ResponseTest::$headers[] = $string;
// TODO implement replace
if ($httpResponseCode !== null) {
ResponseTest::$httpResponseCode = $httpResponseCode;
}
}
namespace yiiunit\framework\web;
use yii\helpers\StringHelper;
use yii\web\Response;
class ResponseTest extends \yiiunit\TestCase
{
public static $headers = array();
public static $httpResponseCode = 200;
protected function setUp()
{
parent::setUp();
$this->reset();
}
protected function reset()
{
static::$headers = array();
static::$httpResponseCode = 200;
}
public function ranges()
{
// TODO test more cases for range requests and check for rfc compatibility
// http://www.w3.org/Protocols/rfc2616/rfc2616.txt
return array(
array('0-5', '0-5', 6, '12ёж'),
array('2-', '2-66', 65, 'ёжик3456798áèabcdefghijklmnopqrstuvwxyz!"§$%&/(ёжик)=?'),
array('-12', '55-66', 12, '(ёжик)=?'),
);
}
/**
* @dataProvider ranges
*/
public function testSendFileRanges($rangeHeader, $expectedHeader, $length, $expectedFile)
{
$content = $this->generateTestFileContent();
$_SERVER['HTTP_RANGE'] = 'bytes=' . $rangeHeader;
$sent = $this->runSendFile('testFile.txt', $content, null);
$this->assertEquals($expectedFile, $sent);
$this->assertTrue(in_array('HTTP/1.1 206 Partial Content', static::$headers));
$this->assertTrue(in_array('Accept-Ranges: bytes', static::$headers));
$this->assertArrayHasKey('Content-Range: bytes ' . $expectedHeader . '/' . StringHelper::strlen($content), array_flip(static::$headers));
$this->assertTrue(in_array('Content-Type: text/plain', static::$headers));
$this->assertTrue(in_array('Content-Length: ' . $length, static::$headers));
}
protected function generateTestFileContent()
{
return '12ёжик3456798áèabcdefghijklmnopqrstuvwxyz!"§$%&/(ёжик)=?';
}
protected function runSendFile($fileName, $content, $mimeType)
{
ob_start();
ob_implicit_flush(false);
$response = new Response();
$response->sendFile($fileName, $content, $mimeType, false);
$file = ob_get_clean();
return $file;
}
}

26
tests/unit/framework/web/UrlManagerTest.php

@ -14,9 +14,9 @@ class UrlManagerTest extends \yiiunit\TestCase
'cache' => null, 'cache' => null,
)); ));
$url = $manager->createUrl('post/view'); $url = $manager->createUrl('post/view');
$this->assertEquals('/?r=post/view', $url); $this->assertEquals('?r=post/view', $url);
$url = $manager->createUrl('post/view', array('id' => 1, 'title' => 'sample post')); $url = $manager->createUrl('post/view', array('id' => 1, 'title' => 'sample post'));
$this->assertEquals('/?r=post/view&id=1&title=sample+post', $url); $this->assertEquals('?r=post/view&id=1&title=sample+post', $url);
// default setting with '/test/' as base url // default setting with '/test/' as base url
$manager = new UrlManager(array( $manager = new UrlManager(array(
@ -24,7 +24,7 @@ class UrlManagerTest extends \yiiunit\TestCase
'cache' => null, 'cache' => null,
)); ));
$url = $manager->createUrl('post/view', array('id' => 1, 'title' => 'sample post')); $url = $manager->createUrl('post/view', array('id' => 1, 'title' => 'sample post'));
$this->assertEquals('/test/?r=post/view&id=1&title=sample+post', $url); $this->assertEquals('/test?r=post/view&id=1&title=sample+post', $url);
// pretty URL without rules // pretty URL without rules
$manager = new UrlManager(array( $manager = new UrlManager(array(
@ -85,6 +85,24 @@ class UrlManagerTest extends \yiiunit\TestCase
$this->assertEquals('/post/1/sample+post.html', $url); $this->assertEquals('/post/1/sample+post.html', $url);
$url = $manager->createUrl('post/index', array('page' => 1)); $url = $manager->createUrl('post/index', array('page' => 1));
$this->assertEquals('/post/index.html?page=1', $url); $this->assertEquals('/post/index.html?page=1', $url);
// pretty URL with rules that have host info
$manager = new UrlManager(array(
'enablePrettyUrl' => true,
'cache' => null,
'rules' => array(
array(
'pattern' => 'post/<id>/<title>',
'route' => 'post/view',
'host' => 'http://<lang:en|fr>.example.com',
),
),
'baseUrl' => '/test',
));
$url = $manager->createUrl('post/view', array('id' => 1, 'title' => 'sample post', 'lang' => 'en'));
$this->assertEquals('http://en.example.com/test/post/1/sample+post', $url);
$url = $manager->createUrl('post/index', array('page' => 1));
$this->assertEquals('/test/post/index?page=1', $url);
} }
public function testCreateAbsoluteUrl() public function testCreateAbsoluteUrl()
@ -95,7 +113,7 @@ class UrlManagerTest extends \yiiunit\TestCase
'cache' => null, 'cache' => null,
)); ));
$url = $manager->createAbsoluteUrl('post/view', array('id' => 1, 'title' => 'sample post')); $url = $manager->createAbsoluteUrl('post/view', array('id' => 1, 'title' => 'sample post'));
$this->assertEquals('http://www.example.com/?r=post/view&id=1&title=sample+post', $url); $this->assertEquals('http://www.example.com?r=post/view&id=1&title=sample+post', $url);
} }
public function testParseRequest() public function testParseRequest()

28
tests/unit/framework/web/UrlRuleTest.php

@ -26,7 +26,7 @@ class UrlRuleTest extends \yiiunit\TestCase
public function testParseRequest() public function testParseRequest()
{ {
$manager = new UrlManager(array('cache' => null)); $manager = new UrlManager(array('cache' => null));
$request = new Request; $request = new Request(array('hostInfo' => 'http://en.example.com'));
$suites = $this->getTestsForParseRequest(); $suites = $this->getTestsForParseRequest();
foreach ($suites as $i => $suite) { foreach ($suites as $i => $suite) {
list ($name, $config, $tests) = $suite; list ($name, $config, $tests) = $suite;
@ -327,6 +327,19 @@ class UrlRuleTest extends \yiiunit\TestCase
array('post/index', array('page' => 1), 'posts/?page=1'), array('post/index', array('page' => 1), 'posts/?page=1'),
), ),
), ),
array(
'with host info',
array(
'pattern' => 'post/<page:\d+>/<tag>',
'route' => 'post/index',
'defaults' => array('page' => 1),
'host' => 'http://<lang:en|fr>.example.com',
),
array(
array('post/index', array('page' => 1, 'tag' => 'a'), false),
array('post/index', array('page' => 1, 'tag' => 'a', 'lang' => 'en'), 'http://en.example.com/post/a'),
),
),
); );
} }
@ -610,6 +623,19 @@ class UrlRuleTest extends \yiiunit\TestCase
array('a', false), array('a', false),
), ),
), ),
array(
'with host info',
array(
'pattern' => 'post/<page:\d+>',
'route' => 'post/index',
'host' => 'http://<lang:en|fr>.example.com',
),
array(
array('post/1', 'post/index', array('page' => '1', 'lang' => 'en')),
array('post/a', false),
array('post/1/a', false),
),
),
); );
} }
} }

46
upgrade.md

@ -1,46 +0,0 @@
Upgrading Instructions for Yii Framework v2
===========================================
!!!IMPORTANT!!!
The following upgrading instructions are cumulative. That is,
if you want to upgrade from version A to version C and there is
version B between A and C, you need to following the instructions
for both A and B.
General upgrade instructions
----------------------------
- Make a backup.
- Clean up your 'assets' folder.
- Replace 'framework' dir with the new one or point Git to a fresh
release tag and checkout.
- Check if everything is OK, if not — revert to previous stable version and post
issues to [Yii issue tracker](https://github.com/yiisoft/yii2/issues).
Upgrading from v1.1.x
---------------------
- All framework classes are now namespaced, and the name prefix `C` is removed.
- The format of path alias is changed to `@yii/base/Component`.
In 1.x, this would be `system.base.CComponent`. See guide for more details.
- The root alias `@yii` now represents the framework installation directory.
In 1.x, this is named as `system`. We also removed `zii` root alias.
- `Object` serves as the base class that supports properties. And `Component` extends
from `Object` and supports events and behaviors. Behaviors declared in
`Component::behaviors()` are attached on demand.
- `CList` is renamed to `Vector`, and `CMap` is renamed to `Dictionary`.
Other collection classes are dropped in favor of SPL classes.
- `CFormModel` is removed. Please use `yii\base\Model` instead.
- `CDbCriteria` is replaced by `yii\db\Query` which includes methods for
building a query. `CDbCommandBuilder` is replaced by `yii\db\QueryBuilder`
which has cleaner and more complete support of query building capabilities.

0
changelog.md → yii/CHANGELOG.md

32
yii/LICENSE.md

@ -0,0 +1,32 @@
The Yii framework is free software. It is released under the terms of
the following BSD License.
Copyright © 2008-2013 by Yii Software LLC (http://www.yiisoft.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Yii Software LLC nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

21
yii/README.md

@ -0,0 +1,21 @@
Yii 2.0 Public Preview
======================
Thank you for choosing Yii - a high-performance component-based PHP framework.
If you are looking for a production-ready PHP framework, please use
[Yii v1.1](https://github.com/yiisoft/yii).
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)
REQUIREMENTS
------------
The minimum requirement by Yii is that your Web server supports PHP 5.3.?.

9
yii/UPGRADE.md

@ -0,0 +1,9 @@
Upgrading Instructions for Yii Framework v2
===========================================
!!!IMPORTANT!!!
The following upgrading instructions are cumulative. That is,
if you want to upgrade from version A to version C and there is
version B between A and C, you need to following the instructions
for both A and B.

21
yii/YiiBase.php

@ -287,7 +287,11 @@ class YiiBase
if ($path !== null) { if ($path !== null) {
$path = strncmp($path, '@', 1) ? rtrim($path, '\\/') : static::getAlias($path); $path = strncmp($path, '@', 1) ? rtrim($path, '\\/') : static::getAlias($path);
if (!isset(self::$aliases[$root])) { if (!isset(self::$aliases[$root])) {
self::$aliases[$root] = $path; if ($pos === false) {
self::$aliases[$root] = $path;
} else {
self::$aliases[$root] = array($alias => $path);
}
} elseif (is_string(self::$aliases[$root])) { } elseif (is_string(self::$aliases[$root])) {
if ($pos === false) { if ($pos === false) {
self::$aliases[$root] = $path; self::$aliases[$root] = $path;
@ -579,10 +583,9 @@ class YiiBase
/** /**
* Translates a message to the specified language. * Translates a message to the specified language.
* *
* The translation will be conducted according to the message category and the target language. * This is a shortcut method of [[\yii\i18n\I18N::translate()]].
* To specify the category of the message, prefix the message with the category name and separate it *
* with "|". For example, "app|hello world". If the category is not specified, the default category "app" * The translation will be conducted according to the message category and the target language will be used.
* will be used. The actual message translation is done by a [[\yii\i18n\MessageSource|message source]].
* *
* In case when a translated message has different plural forms (separated by "|"), this method * In case when a translated message has different plural forms (separated by "|"), this method
* will also attempt to choose an appropriate one according to a given numeric value which is * will also attempt to choose an appropriate one according to a given numeric value which is
@ -595,20 +598,18 @@ class YiiBase
* For more details on how plural rules are applied, please refer to: * For more details on how plural rules are applied, please refer to:
* [[http://www.unicode.org/cldr/charts/supplemental/language_plural_rules.html]] * [[http://www.unicode.org/cldr/charts/supplemental/language_plural_rules.html]]
* *
* @param string $category the message category.
* @param string $message the message to be translated. * @param string $message the message to be translated.
* @param array $params the parameters that will be used to replace the corresponding placeholders in the message. * @param array $params the parameters that will be used to replace the corresponding placeholders in the message.
* @param string $language the language code (e.g. `en_US`, `en`). If this is null, the current * @param string $language the language code (e.g. `en_US`, `en`). If this is null, the current
* [[\yii\base\Application::language|application language]] will be used. * [[\yii\base\Application::language|application language]] will be used.
* @return string the translated message. * @return string the translated message.
*/ */
public static function t($message, $params = array(), $language = null) public static function t($category, $message, $params = array(), $language = null)
{ {
if (self::$app !== null) { if (self::$app !== null) {
return self::$app->getI18N()->translate($message, $params, $language); return self::$app->getI18N()->translate($category, $message, $params, $language);
} else { } else {
if (strpos($message, '|') !== false && preg_match('/^([\w\-\\/\.\\\\]+)\|(.*)/', $message, $matches)) {
$message = $matches[2];
}
return is_array($params) ? strtr($message, $params) : $message; return is_array($params) ? strtr($message, $params) : $message;
} }
} }

36
yii/assets.php

@ -1,18 +1,18 @@
<?php <?php
return array( return array(
'jquery' => array( 'yii' => array(
'sourcePath' => __DIR__ . '/assets', 'sourcePath' => __DIR__ . '/assets',
'js' => array( 'js' => array(
'jquery.min.js', 'yii.js',
), ),
'depends' => array('yii/jquery'),
), ),
'yii' => array( 'yii/jquery' => array(
'sourcePath' => __DIR__ . '/assets', 'sourcePath' => __DIR__ . '/assets',
'js' => array( 'js' => array(
'yii.js', 'jquery.min.js',
), ),
'depends' => array('jquery'),
), ),
'yii/validation' => array( 'yii/validation' => array(
'sourcePath' => __DIR__ . '/assets', 'sourcePath' => __DIR__ . '/assets',
@ -26,7 +26,7 @@ return array(
'js' => array( 'js' => array(
'yii.activeForm.js', 'yii.activeForm.js',
), ),
'depends' => array('yii', 'yii/validation'), 'depends' => array('yii'),
), ),
'yii/captcha' => array( 'yii/captcha' => array(
'sourcePath' => __DIR__ . '/assets', 'sourcePath' => __DIR__ . '/assets',
@ -43,20 +43,26 @@ return array(
'depends' => array('yii'), 'depends' => array('yii'),
), ),
'yii/bootstrap' => array( 'yii/bootstrap' => array(
'sourcePath' => __DIR__ . '/assets/bootstrap', 'sourcePath' => __DIR__ . '/assets',
'js' => array(
'/js/bootstrap.min.js',
),
'css' => array( 'css' => array(
'css/bootstrap.css' 'bootstrap/css/bootstrap.css',
), ),
'depends' => array('yii'), 'js' => array(
'bootstrap/js/bootstrap.js',
),
'depends' => array('yii/jquery'),
), ),
'yii/bootstrap-responsive' => array( 'yii/bootstrap-responsive' => array(
'sourcePath' => __DIR__ . '/assets/bootstrap', 'sourcePath' => __DIR__ . '/assets',
'css' => array( 'css' => array(
'css/bootstrap-responsive.css' 'bootstrap/css/bootstrap-responsive.css',
), ),
'depends' => array('yii/bootstrap'), 'depends' => array('yii/bootstrap'),
) ),
'yii/punycode' => array(
'sourcePath' => __DIR__ . '/assets',
'js' => array(
'punycode/punycode.min.js',
),
),
); );

0
apps/bootstrap/css/bootstrap-responsive.css → yii/assets/bootstrap/css/bootstrap-responsive.css vendored

0
apps/bootstrap/css/bootstrap.css → yii/assets/bootstrap/css/bootstrap.css vendored

0
apps/bootstrap/img/glyphicons-halflings-white.png → yii/assets/bootstrap/img/glyphicons-halflings-white.png

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

0
apps/bootstrap/img/glyphicons-halflings.png → yii/assets/bootstrap/img/glyphicons-halflings.png

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

0
apps/bootstrap/js/bootstrap.js → yii/assets/bootstrap/js/bootstrap.js vendored

278
yii/assets/punycode/LICENSE-GPL.txt

@ -0,0 +1,278 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

20
yii/assets/punycode/LICENSE-MIT.txt

@ -0,0 +1,20 @@
Copyright Mathias Bynens <http://mathiasbynens.be/>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

2
yii/assets/punycode/punycode.min.js vendored

@ -0,0 +1,2 @@
/*! http://mths.be/punycode v1.2.1 by @mathias */
(function(o){function e(o){throw RangeError(L[o])}function n(o,e){for(var n=o.length;n--;)o[n]=e(o[n]);return o}function t(o,e){return n(o.split(S),e).join(".")}function r(o){for(var e,n,t=[],r=0,u=o.length;u>r;)e=o.charCodeAt(r++),55296==(63488&e)&&u>r?(n=o.charCodeAt(r++),56320==(64512&n)?t.push(((1023&e)<<10)+(1023&n)+65536):t.push(e,n)):t.push(e);return t}function u(o){return n(o,function(o){var e="";return o>65535&&(o-=65536,e+=R(55296|1023&o>>>10),o=56320|1023&o),e+=R(o)}).join("")}function i(o){return 10>o-48?o-22:26>o-65?o-65:26>o-97?o-97:x}function f(o,e){return o+22+75*(26>o)-((0!=e)<<5)}function c(o,e,n){var t=0;for(o=n?P(o/m):o>>1,o+=P(o/e);o>M*y>>1;t+=x)o=P(o/M);return P(t+(M+1)*o/(o+j))}function l(o){var n,t,r,f,l,d,s,a,p,h,v=[],g=o.length,w=0,j=I,m=A;for(t=o.lastIndexOf(F),0>t&&(t=0),r=0;t>r;++r)o.charCodeAt(r)>=128&&e("not-basic"),v.push(o.charCodeAt(r));for(f=t>0?t+1:0;g>f;){for(l=w,d=1,s=x;f>=g&&e("invalid-input"),a=i(o.charCodeAt(f++)),(a>=x||a>P((b-w)/d))&&e("overflow"),w+=a*d,p=m>=s?C:s>=m+y?y:s-m,!(p>a);s+=x)h=x-p,d>P(b/h)&&e("overflow"),d*=h;n=v.length+1,m=c(w-l,n,0==l),P(w/n)>b-j&&e("overflow"),j+=P(w/n),w%=n,v.splice(w++,0,j)}return u(v)}function d(o){var n,t,u,i,l,d,s,a,p,h,v,g,w,j,m,E=[];for(o=r(o),g=o.length,n=I,t=0,l=A,d=0;g>d;++d)v=o[d],128>v&&E.push(R(v));for(u=i=E.length,i&&E.push(F);g>u;){for(s=b,d=0;g>d;++d)v=o[d],v>=n&&s>v&&(s=v);for(w=u+1,s-n>P((b-t)/w)&&e("overflow"),t+=(s-n)*w,n=s,d=0;g>d;++d)if(v=o[d],n>v&&++t>b&&e("overflow"),v==n){for(a=t,p=x;h=l>=p?C:p>=l+y?y:p-l,!(h>a);p+=x)m=a-h,j=x-h,E.push(R(f(h+m%j,0))),a=P(m/j);E.push(R(f(a,0))),l=c(t,w,u==i),t=0,++u}++t,++n}return E.join("")}function s(o){return t(o,function(o){return E.test(o)?l(o.slice(4).toLowerCase()):o})}function a(o){return t(o,function(o){return O.test(o)?"xn--"+d(o):o})}var p="object"==typeof exports&&exports,h="object"==typeof module&&module&&module.exports==p&&module,v="object"==typeof global&&global;(v.global===v||v.window===v)&&(o=v);var g,w,b=2147483647,x=36,C=1,y=26,j=38,m=700,A=72,I=128,F="-",E=/^xn--/,O=/[^ -~]/,S=/\x2E|\u3002|\uFF0E|\uFF61/g,L={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},M=x-C,P=Math.floor,R=String.fromCharCode;if(g={version:"1.2.1",ucs2:{decode:r,encode:u},decode:l,encode:d,toASCII:a,toUnicode:s},"function"==typeof define&&"object"==typeof define.amd&&define.amd)define(function(){return g});else if(p&&!p.nodeType)if(h)h.exports=g;else for(w in g)g.hasOwnProperty(w)&&(p[w]=g[w]);else o.punycode=g})(this);

2
yii/assets/yii.activeForm.js

@ -57,7 +57,7 @@
// whether to perform validation when a change is detected on the input // whether to perform validation when a change is detected on the input
validateOnChange: false, validateOnChange: false,
// whether to perform validation when the user is typing. // whether to perform validation when the user is typing.
validateOnType: false, validateOnType: false,
// number of milliseconds that the validation should be delayed when a user is typing in the input field. // number of milliseconds that the validation should be delayed when a user is typing in the input field.
validationDelay: 200, validationDelay: 200,
// whether to enable AJAX-based validation. // whether to enable AJAX-based validation.

4
yii/assets/yii.captcha.js

@ -51,8 +51,8 @@
dataType: 'json', dataType: 'json',
cache: false, cache: false,
success: function(data) { success: function(data) {
$e.attr('src', data['url']); $e.attr('src', data.url);
$('body').data(settings.hashKey, [data['hash1'], data['hash2']]); $('body').data(settings.hashKey, [data.hash1, data.hash2]);
} }
}); });
}, },

2
yii/assets/yii.debug.js

@ -18,7 +18,7 @@ yii.debug = (function ($) {
//dataType: 'json', //dataType: 'json',
success: function(data) { success: function(data) {
var $e = $('#' + id); var $e = $('#' + id);
$e.html(data); $e.html(data).show();
} }
}); });
} }

28
yii/assets/yii.validation.js

@ -110,9 +110,19 @@ yii.validation = (function ($) {
return; return;
} }
var valid = value.match(options.pattern) && (!options.allowName || value.match(options.fullPattern)); var valid = true;
if (options.enableIDN) {
var regexp = /^(.*)@(.*)$/,
matches = regexp.exec(value);
if (matches === null) {
valid = false;
} else {
value = punycode.toASCII(matches[1]) + '@' + punycode.toASCII(matches[2]);
}
}
if (!valid) { if (!valid || !(value.match(options.pattern) && (!options.allowName || value.match(options.fullPattern)))) {
messages.push(options.message); messages.push(options.message);
} }
}, },
@ -126,7 +136,19 @@ yii.validation = (function ($) {
value = options.defaultScheme + '://' + value; value = options.defaultScheme + '://' + value;
} }
if (!value.match(options.pattern)) { var valid = true;
if (options.enableIDN) {
var regexp = /^([^:]+):\/\/([^\/]+)(.*)$/,
matches = regexp.exec(value);
if (matches === null) {
valid = false;
} else {
value = matches[1] + '://' + punycode.toASCII(matches[2]) + matches[3];
}
}
if (!valid || !value.match(options.pattern)) {
messages.push(options.message); messages.push(options.message);
} }
}, },

3
yii/base/Controller.php

@ -183,7 +183,7 @@ class Controller extends Component
} }
if (!empty($missing)) { if (!empty($missing)) {
throw new InvalidRequestException(Yii::t('yii|Missing required parameters: {params}', array( throw new InvalidRequestException(Yii::t('yii', 'Missing required parameters: {params}', array(
'{params}' => implode(', ', $missing), '{params}' => implode(', ', $missing),
))); )));
} }
@ -410,6 +410,7 @@ class Controller extends Component
* Returns the view object that can be used to render views or view files. * Returns the view object that can be used to render views or view files.
* The [[render()]], [[renderPartial()]] and [[renderFile()]] methods will use * The [[render()]], [[renderPartial()]] and [[renderFile()]] methods will use
* this view object to implement the actual view rendering. * this view object to implement the actual view rendering.
* If not set, it will default to the "view" application component.
* @return View the view object that can be used to render views or view files. * @return View the view object that can be used to render views or view files.
*/ */
public function getView() public function getView()

297
yii/base/Dictionary.php

@ -1,297 +0,0 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
use yii\helpers\ArrayHelper;
/**
* Dictionary implements a collection that stores key-value pairs.
*
* You can access, add or remove an item with a key by using
* [[itemAt()]], [[add()]], and [[remove()]].
*
* To get the number of the items in the dictionary, use [[getCount()]].
*
* Because Dictionary implements a set of SPL interfaces, it can be used
* like a regular PHP array as follows,
*
* ~~~
* $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
* $n = count($dictionary); // returns the number of items in the dictionary
* ~~~
*
* @property integer $count the number of items in the dictionary
* @property array $keys The keys in the dictionary
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Dictionary extends Object implements \IteratorAggregate, \ArrayAccess, \Countable
{
/**
* @var array internal data storage
*/
private $_d = array();
/**
* Constructor.
* Initializes the dictionary with an array or an iterable object.
* @param mixed $data the initial data to be populated into the dictionary.
* This can be an array or an iterable object.
* @param array $config name-value pairs that will be used to initialize the object properties
* @throws Exception if data is not well formed (neither an array nor an iterable object)
*/
public function __construct($data = array(), $config = array())
{
if (!empty($data)) {
$this->copyFrom($data);
}
parent::__construct($config);
}
/**
* Returns an iterator for traversing the items in the dictionary.
* This method is required by the SPL interface `IteratorAggregate`.
* It will be implicitly called when you use `foreach` to traverse the dictionary.
* @return DictionaryIterator an iterator for traversing the items in the dictionary.
*/
public function getIterator()
{
return new DictionaryIterator($this->_d);
}
/**
* Returns the number of items in the dictionary.
* This method is required by the SPL `Countable` interface.
* It will be implicitly called when you use `count($dictionary)`.
* @return integer number of items in the dictionary.
*/
public function count()
{
return $this->getCount();
}
/**
* Returns the number of items in the dictionary.
* @return integer the number of items in the dictionary
*/
public function getCount()
{
return count($this->_d);
}
/**
* Returns the keys stored in the dictionary.
* @return array the key list
*/
public function getKeys()
{
return array_keys($this->_d);
}
/**
* Returns the item with the specified key.
* @param mixed $key the key
* @return mixed the element with the specified key.
* Null if the key cannot be found in the dictionary.
*/
public function itemAt($key)
{
return isset($this->_d[$key]) ? $this->_d[$key] : null;
}
/**
* Adds an item into the dictionary.
* Note, if the specified key already exists, the old value will be overwritten.
* @param mixed $key key
* @param mixed $value value
* @throws Exception if the dictionary is read-only
*/
public function add($key, $value)
{
if ($key === null) {
$this->_d[] = $value;
} else {
$this->_d[$key] = $value;
}
}
/**
* Removes an item from the dictionary by its key.
* @param mixed $key the key of the item to be removed
* @return mixed the removed value, null if no such key exists.
* @throws Exception if the dictionary is read-only
*/
public function remove($key)
{
if (isset($this->_d[$key])) {
$value = $this->_d[$key];
unset($this->_d[$key]);
return $value;
} else { // the value is null
unset($this->_d[$key]);
return null;
}
}
/**
* Removes all items from the dictionary.
* @param boolean $safeClear whether to clear every item by calling [[remove]].
* Defaults to false, meaning all items in the dictionary will be cleared directly
* without calling [[remove]].
*/
public function removeAll($safeClear = false)
{
if ($safeClear) {
foreach (array_keys($this->_d) as $key) {
$this->remove($key);
}
} else {
$this->_d = array();
}
}
/**
* Returns a value indicating whether the dictionary contains the specified key.
* @param mixed $key the key
* @return boolean whether the dictionary contains an item with the specified key
*/
public function has($key)
{
return isset($this->_d[$key]) || array_key_exists($key, $this->_d);
}
/**
* Returns the dictionary as a PHP array.
* @return array the list of items in array
*/
public function toArray()
{
return $this->_d;
}
/**
* Copies iterable data into the dictionary.
* Note, existing data in the dictionary will be cleared first.
* @param mixed $data the data to be copied from, must be an array or an object implementing `Traversable`
* @throws InvalidParamException if data is neither an array nor an iterator.
*/
public function copyFrom($data)
{
if (is_array($data) || $data instanceof \Traversable) {
if (!empty($this->_d)) {
$this->removeAll();
}
if ($data instanceof self) {
$data = $data->_d;
}
foreach ($data as $key => $value) {
$this->add($key, $value);
}
} else {
throw new InvalidParamException('Data must be either an array or an object implementing Traversable.');
}
}
/**
* Merges iterable data into the dictionary.
*
* Existing elements in the dictionary will be overwritten if their keys are the same as those in the source.
* If the merge is recursive, the following algorithm is performed:
*
* - the dictionary data is saved as $a, and the source data is saved as $b;
* - if $a and $b both have an array indexed at the same string key, the arrays will be merged using this algorithm;
* - any integer-indexed elements in $b will be appended to $a;
* - any string-indexed elements in $b will overwrite elements in $a with the same index;
*
* @param array|\Traversable $data the data to be merged with. It must be an array or object implementing Traversable
* @param boolean $recursive whether the merging should be recursive.
* @throws InvalidParamException if data is neither an array nor an object implementing `Traversable`.
*/
public function mergeWith($data, $recursive = true)
{
if (is_array($data) || $data instanceof \Traversable) {
if ($data instanceof self) {
$data = $data->_d;
}
if ($recursive) {
if ($data instanceof \Traversable) {
$d = array();
foreach ($data as $key => $value) {
$d[$key] = $value;
}
$this->_d = ArrayHelper::merge($this->_d, $d);
} else {
$this->_d = ArrayHelper::merge($this->_d, $data);
}
} else {
foreach ($data as $key => $value) {
$this->add($key, $value);
}
}
} else {
throw new InvalidParamException('The data to be merged with must be an array or an object implementing Traversable.');
}
}
/**
* Returns whether there is an element at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `isset($dictionary[$offset])`.
* This is equivalent to [[contains]].
* @param mixed $offset the offset to check on
* @return boolean
*/
public function offsetExists($offset)
{
return $this->has($offset);
}
/**
* Returns the element at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `$value = $dictionary[$offset];`.
* This is equivalent to [[itemAt]].
* @param mixed $offset the offset to retrieve element.
* @return mixed the element at the offset, null if no element is found at the offset
*/
public function offsetGet($offset)
{
return $this->itemAt($offset);
}
/**
* Sets the element at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `$dictionary[$offset] = $item;`.
* If the offset is null, the new item will be appended to the dictionary.
* Otherwise, the existing item at the offset will be replaced with the new item.
* This is equivalent to [[add]].
* @param mixed $offset the offset to set element
* @param mixed $item the element value
*/
public function offsetSet($offset, $item)
{
$this->add($offset, $item);
}
/**
* Unsets the element at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `unset($dictionary[$offset])`.
* This is equivalent to [[remove]].
* @param mixed $offset the offset to unset element
*/
public function offsetUnset($offset)
{
$this->remove($offset);
}
}

92
yii/base/DictionaryIterator.php

@ -1,92 +0,0 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* DictionaryIterator implements the SPL `Iterator` interface for [[Dictionary]].
*
* It allows [[Dictionary]] to return a new iterator for data traversing purpose.
* You normally do not use this class directly.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class DictionaryIterator implements \Iterator
{
/**
* @var array the data to be iterated through
*/
private $_d;
/**
* @var array list of keys in the map
*/
private $_keys;
/**
* @var mixed current key
*/
private $_key;
/**
* Constructor.
* @param array $data the data to be iterated through
*/
public function __construct(&$data)
{
$this->_d = &$data;
$this->_keys = array_keys($data);
$this->_key = reset($this->_keys);
}
/**
* Rewinds the index of the current item.
* This method is required by the SPL interface `Iterator`.
*/
public function rewind()
{
$this->_key = reset($this->_keys);
}
/**
* Returns the key of the current array element.
* This method is required by the SPL interface `Iterator`.
* @return mixed the key of the current array element
*/
public function key()
{
return $this->_key;
}
/**
* Returns the current array element.
* This method is required by the SPL interface `Iterator`.
* @return mixed the current array element
*/
public function current()
{
return $this->_d[$this->_key];
}
/**
* Moves the internal pointer to the next element.
* This method is required by the SPL interface `Iterator`.
*/
public function next()
{
$this->_key = next($this->_keys);
}
/**
* Returns whether there is an element at current position.
* This method is required by the SPL interface `Iterator`.
* @return boolean whether there is an item at current position.
*/
public function valid()
{
return $this->_key !== false;
}
}

28
yii/base/ErrorException.php

@ -90,20 +90,20 @@ class ErrorException extends Exception
public function getName() public function getName()
{ {
$names = array( $names = array(
E_ERROR => Yii::t('yii|Fatal Error'), E_ERROR => Yii::t('yii', 'Fatal Error'),
E_PARSE => Yii::t('yii|Parse Error'), E_PARSE => Yii::t('yii', 'Parse Error'),
E_CORE_ERROR => Yii::t('yii|Core Error'), E_CORE_ERROR => Yii::t('yii', 'Core Error'),
E_COMPILE_ERROR => Yii::t('yii|Compile Error'), E_COMPILE_ERROR => Yii::t('yii', 'Compile Error'),
E_USER_ERROR => Yii::t('yii|User Error'), E_USER_ERROR => Yii::t('yii', 'User Error'),
E_WARNING => Yii::t('yii|Warning'), E_WARNING => Yii::t('yii', 'Warning'),
E_CORE_WARNING => Yii::t('yii|Core Warning'), E_CORE_WARNING => Yii::t('yii', 'Core Warning'),
E_COMPILE_WARNING => Yii::t('yii|Compile Warning'), E_COMPILE_WARNING => Yii::t('yii', 'Compile Warning'),
E_USER_WARNING => Yii::t('yii|User Warning'), E_USER_WARNING => Yii::t('yii', 'User Warning'),
E_STRICT => Yii::t('yii|Strict'), E_STRICT => Yii::t('yii', 'Strict'),
E_NOTICE => Yii::t('yii|Notice'), E_NOTICE => Yii::t('yii', 'Notice'),
E_RECOVERABLE_ERROR => Yii::t('yii|Recoverable Error'), E_RECOVERABLE_ERROR => Yii::t('yii', 'Recoverable Error'),
E_DEPRECATED => Yii::t('yii|Deprecated'), E_DEPRECATED => Yii::t('yii', 'Deprecated'),
); );
return isset($names[$this->getCode()]) ? $names[$this->getCode()] : Yii::t('yii|Error'); return isset($names[$this->getCode()]) ? $names[$this->getCode()] : Yii::t('yii', 'Error');
} }
} }

7
yii/base/ErrorHandler.php

@ -75,8 +75,11 @@ class ErrorHandler extends Component
\Yii::$app->runAction($this->errorAction); \Yii::$app->runAction($this->errorAction);
} elseif (\Yii::$app instanceof \yii\web\Application) { } elseif (\Yii::$app instanceof \yii\web\Application) {
if (!headers_sent()) { if (!headers_sent()) {
$errorCode = $exception instanceof HttpException ? $exception->statusCode : 500; if ($exception instanceof HttpException) {
header("HTTP/1.0 $errorCode " . get_class($exception)); header('HTTP/1.0 ' . $exception->statusCode . ' ' . $exception->getName());
} else {
header('HTTP/1.0 500 ' . get_class($exception));
}
} }
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') { if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
\Yii::$app->renderException($exception); \Yii::$app->renderException($exception);

2
yii/base/Exception.php

@ -20,6 +20,6 @@ class Exception extends \Exception
*/ */
public function getName() public function getName()
{ {
return \Yii::t('yii|Exception'); return \Yii::t('yii', 'Exception');
} }
} }

2
yii/base/HttpException.php

@ -103,7 +103,7 @@ class HttpException extends UserException
if (isset($httpCodes[$this->statusCode])) { if (isset($httpCodes[$this->statusCode])) {
return $httpCodes[$this->statusCode]; return $httpCodes[$this->statusCode];
} else { } else {
return \Yii::t('yii|Error'); return \Yii::t('yii', 'Error');
} }
} }
} }

2
yii/base/InvalidCallException.php

@ -20,7 +20,7 @@ class InvalidCallException extends Exception
*/ */
public function getName() public function getName()
{ {
return \Yii::t('yii|Invalid Call'); return \Yii::t('yii', 'Invalid Call');
} }
} }

2
yii/base/InvalidConfigException.php

@ -20,7 +20,7 @@ class InvalidConfigException extends Exception
*/ */
public function getName() public function getName()
{ {
return \Yii::t('yii|Invalid Configuration'); return \Yii::t('yii', 'Invalid Configuration');
} }
} }

2
yii/base/InvalidParamException.php

@ -20,7 +20,7 @@ class InvalidParamException extends Exception
*/ */
public function getName() public function getName()
{ {
return \Yii::t('yii|Invalid Parameter'); return \Yii::t('yii', 'Invalid Parameter');
} }
} }

2
yii/base/InvalidRequestException.php

@ -20,7 +20,7 @@ class InvalidRequestException extends UserException
*/ */
public function getName() public function getName()
{ {
return \Yii::t('yii|Invalid Request'); return \Yii::t('yii', 'Invalid Request');
} }
} }

2
yii/base/InvalidRouteException.php

@ -20,7 +20,7 @@ class InvalidRouteException extends UserException
*/ */
public function getName() public function getName()
{ {
return \Yii::t('yii|Invalid Route'); return \Yii::t('yii', 'Invalid Route');
} }
} }

63
yii/base/Model.php

@ -7,6 +7,8 @@
namespace yii\base; namespace yii\base;
use ArrayObject;
use ArrayIterator;
use yii\helpers\StringHelper; use yii\helpers\StringHelper;
use yii\validators\RequiredValidator; use yii\validators\RequiredValidator;
use yii\validators\Validator; use yii\validators\Validator;
@ -30,7 +32,7 @@ use yii\validators\Validator;
* You may directly use Model to store model data, or extend it with customization. * You may directly use Model to store model data, or extend it with customization.
* You may also customize Model by attaching [[ModelBehavior|model behaviors]]. * You may also customize Model by attaching [[ModelBehavior|model behaviors]].
* *
* @property Vector $validators All the validators declared in the model. * @property ArrayObject $validators All the validators declared in the model.
* @property array $activeValidators The validators applicable to the current [[scenario]]. * @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 $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).
@ -56,7 +58,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
*/ */
private $_errors; private $_errors;
/** /**
* @var Vector vector of validators * @var ArrayObject list of validators
*/ */
private $_validators; private $_validators;
/** /**
@ -300,15 +302,15 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
* This method differs from [[getActiveValidators()]] in that the latter * This method differs from [[getActiveValidators()]] in that the latter
* only returns the validators applicable to the current [[scenario]]. * only returns the validators applicable to the current [[scenario]].
* *
* Because this method returns a [[Vector]] object, you may * Because this method returns an ArrayObject object, you may
* manipulate it by inserting or removing validators (useful in model behaviors). * manipulate it by inserting or removing validators (useful in model behaviors).
* For example, * For example,
* *
* ~~~ * ~~~
* $model->validators->add($newValidator); * $model->validators[] = $newValidator;
* ~~~ * ~~~
* *
* @return Vector all the validators declared in the model. * @return ArrayObject all the validators declared in the model.
*/ */
public function getValidators() public function getValidators()
{ {
@ -340,18 +342,18 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
/** /**
* Creates validator objects based on the validation rules specified in [[rules()]]. * Creates validator objects based on the validation rules specified in [[rules()]].
* Unlike [[getValidators()]], each time this method is called, a new list of validators will be returned. * Unlike [[getValidators()]], each time this method is called, a new list of validators will be returned.
* @return Vector validators * @return ArrayObject validators
* @throws InvalidConfigException if any validation rule configuration is invalid * @throws InvalidConfigException if any validation rule configuration is invalid
*/ */
public function createValidators() public function createValidators()
{ {
$validators = new Vector; $validators = new ArrayObject;
foreach ($this->rules() as $rule) { foreach ($this->rules() as $rule) {
if ($rule instanceof Validator) { if ($rule instanceof Validator) {
$validators->add($rule); $validators->append($rule);
} elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type } elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type
$validator = Validator::createValidator($rule[1], $this, $rule[0], array_slice($rule, 2)); $validator = Validator::createValidator($rule[1], $this, $rule[0], array_slice($rule, 2));
$validators->add($validator); $validators->append($validator);
} else { } else {
throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.'); throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.');
} }
@ -590,18 +592,22 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
/** /**
* Returns the attribute names that are safe to be massively assigned in the current scenario. * Returns the attribute names that are safe to be massively assigned in the current scenario.
* @return array safe attribute names * @return string[] safe attribute names
*/ */
public function safeAttributes() public function safeAttributes()
{ {
$scenario = $this->getScenario(); $scenario = $this->getScenario();
$scenarios = $this->scenarios(); $scenarios = $this->scenarios();
if (!isset($scenarios[$scenario])) {
return array();
}
$attributes = array(); $attributes = array();
if (isset($scenarios[$scenario])) { if (isset($scenarios[$scenario]['attributes']) && is_array($scenarios[$scenario]['attributes'])) {
foreach ($scenarios[$scenario] as $attribute) { $scenarios[$scenario] = $scenarios[$scenario]['attributes'];
if ($attribute[0] !== '!') { }
$attributes[] = $attribute; foreach ($scenarios[$scenario] as $attribute) {
} if ($attribute[0] !== '!') {
$attributes[] = $attribute;
} }
} }
return $attributes; return $attributes;
@ -609,34 +615,37 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
/** /**
* Returns the attribute names that are subject to validation in the current scenario. * Returns the attribute names that are subject to validation in the current scenario.
* @return array safe attribute names * @return string[] safe attribute names
*/ */
public function activeAttributes() public function activeAttributes()
{ {
$scenario = $this->getScenario(); $scenario = $this->getScenario();
$scenarios = $this->scenarios(); $scenarios = $this->scenarios();
if (isset($scenarios[$scenario])) { if (!isset($scenarios[$scenario])) {
$attributes = $scenarios[$this->getScenario()];
foreach ($attributes as $i => $attribute) {
if ($attribute[0] === '!') {
$attributes[$i] = substr($attribute, 1);
}
}
return $attributes;
} else {
return array(); return array();
} }
if (isset($scenarios[$scenario]['attributes']) && is_array($scenarios[$scenario]['attributes'])) {
$attributes = $scenarios[$scenario]['attributes'];
} else {
$attributes = $scenarios[$scenario];
}
foreach ($attributes as $i => $attribute) {
if ($attribute[0] === '!') {
$attributes[$i] = substr($attribute, 1);
}
}
return $attributes;
} }
/** /**
* Returns an iterator for traversing the attributes in the model. * Returns an iterator for traversing the attributes in the model.
* This method is required by the interface IteratorAggregate. * This method is required by the interface IteratorAggregate.
* @return DictionaryIterator an iterator for traversing the items in the list. * @return ArrayIterator an iterator for traversing the items in the list.
*/ */
public function getIterator() public function getIterator()
{ {
$attributes = $this->getAttributes(); $attributes = $this->getAttributes();
return new DictionaryIterator($attributes); return new ArrayIterator($attributes);
} }
/** /**

2
yii/base/Module.php

@ -449,7 +449,7 @@ abstract class Module extends Component
public function getComponent($id, $load = true) public function getComponent($id, $load = true)
{ {
if (isset($this->_components[$id])) { if (isset($this->_components[$id])) {
if ($this->_components[$id] instanceof Component) { if ($this->_components[$id] instanceof Object) {
return $this->_components[$id]; return $this->_components[$id];
} elseif ($load) { } elseif ($load) {
Yii::trace("Loading component: $id", __METHOD__); Yii::trace("Loading component: $id", __METHOD__);

2
yii/base/NotSupportedException.php

@ -20,7 +20,7 @@ class NotSupportedException extends Exception
*/ */
public function getName() public function getName()
{ {
return \Yii::t('yii|Not Supported'); return \Yii::t('yii', 'Not Supported');
} }
} }

2
yii/base/UnknownClassException.php

@ -20,7 +20,7 @@ class UnknownClassException extends Exception
*/ */
public function getName() public function getName()
{ {
return \Yii::t('yii|Unknown Class'); return \Yii::t('yii', 'Unknown Class');
} }
} }

2
yii/base/UnknownMethodException.php

@ -20,7 +20,7 @@ class UnknownMethodException extends Exception
*/ */
public function getName() public function getName()
{ {
return \Yii::t('yii|Unknown Method'); return \Yii::t('yii', 'Unknown Method');
} }
} }

2
yii/base/UnknownPropertyException.php

@ -20,7 +20,7 @@ class UnknownPropertyException extends Exception
*/ */
public function getName() public function getName()
{ {
return \Yii::t('yii|Unknown Property'); return \Yii::t('yii', 'Unknown Property');
} }
} }

341
yii/base/Vector.php

@ -1,341 +0,0 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* Vector implements an integer-indexed collection class.
*
* You can access, append, insert, remove an item from the vector
* by calling methods such as [[itemAt()]], [[add()]], [[insertAt()]],
* [[remove()]] and [[removeAt()]].
*
* To get the number of the items in the vector, use [[getCount()]].
*
* Because Vector implements a set of SPL interfaces, it can be used
* like a regular PHP array as follows,
*
* ~~~
* $vector[] = $item; // append new item at the end
* $vector[$index] = $item; // set new item at $index
* unset($vector[$index]); // remove the item at $index
* if (isset($vector[$index])) // if the vector has an item at $index
* foreach ($vector as $index => $item) // traverse each item in the vector
* $n = count($vector); // count the number of items
* ~~~
*
* Note that if you plan to extend Vector by performing additional operations
* with each addition or removal of an item (e.g. performing type check),
* please make sure you override [[insertAt()]] and [[removeAt()]].
*
* @property integer $count the number of items in the vector
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Countable
{
/**
* @var array internal data storage
*/
private $_d = array();
/**
* @var integer number of items
*/
private $_c = 0;
/**
* Constructor.
* Initializes the vector with an array or an iterable object.
* @param mixed $data the initial data to be populated into the vector.
* This can be an array or an iterable object.
* @param array $config name-value pairs that will be used to initialize the object properties
* @throws Exception if data is not well formed (neither an array nor an iterable object)
*/
public function __construct($data = array(), $config = array())
{
if (!empty($data)) {
$this->copyFrom($data);
}
parent::__construct($config);
}
/**
* Returns an iterator for traversing the items in the vector.
* This method is required by the SPL interface `IteratorAggregate`.
* It will be implicitly called when you use `foreach` to traverse the vector.
* @return VectorIterator an iterator for traversing the items in the vector.
*/
public function getIterator()
{
return new VectorIterator($this->_d);
}
/**
* Returns the number of items in the vector.
* This method is required by the SPL `Countable` interface.
* It will be implicitly called when you use `count($vector)`.
* @return integer number of items in the vector.
*/
public function count()
{
return $this->getCount();
}
/**
* Returns the number of items in the vector.
* @return integer the number of items in the vector
*/
public function getCount()
{
return $this->_c;
}
/**
* Returns the item at the specified index.
* @param integer $index the index of the item
* @return mixed the item at the index
* @throws InvalidParamException if the index is out of range
*/
public function itemAt($index)
{
if (isset($this->_d[$index])) {
return $this->_d[$index];
} elseif ($index >= 0 && $index < $this->_c) { // in case the value is null
return $this->_d[$index];
} else {
throw new InvalidParamException('Index out of range: ' . $index);
}
}
/**
* Appends an item at the end of the vector.
* @param mixed $item new item
* @return integer the zero-based index at which the item is added
* @throws Exception if the vector is read-only.
*/
public function add($item)
{
$this->insertAt($this->_c, $item);
return $this->_c - 1;
}
/**
* Inserts an item at the specified position.
* Original item at the position and the following items will be moved
* one step towards the end.
* @param integer $index the specified position.
* @param mixed $item new item to be inserted into the vector
* @throws InvalidParamException if the index specified is out of range, or the vector is read-only.
*/
public function insertAt($index, $item)
{
if ($index === $this->_c) {
$this->_d[$this->_c++] = $item;
} elseif ($index >= 0 && $index < $this->_c) {
array_splice($this->_d, $index, 0, array($item));
$this->_c++;
} else {
throw new InvalidParamException('Index out of range: ' . $index);
}
}
/**
* Removes an item from the vector.
* The vector will search for the item, and the first item found
* will be removed from the vector.
* @param mixed $item the item to be removed.
* @return mixed the index at which the item is being removed, or false
* if the item cannot be found in the vector.
* @throws Exception if the vector is read only.
*/
public function remove($item)
{
if (($index = $this->indexOf($item)) >= 0) {
$this->removeAt($index);
return $index;
} else {
return false;
}
}
/**
* Removes an item at the specified position.
* @param integer $index the index of the item to be removed.
* @return mixed the removed item.
* @throws InvalidParamException if the index is out of range, or the vector is read only.
*/
public function removeAt($index)
{
if ($index >= 0 && $index < $this->_c) {
$this->_c--;
if ($index === $this->_c) {
return array_pop($this->_d);
} else {
$item = $this->_d[$index];
array_splice($this->_d, $index, 1);
return $item;
}
} else {
throw new InvalidParamException('Index out of range: ' . $index);
}
}
/**
* Removes all items from the vector.
* @param boolean $safeClear whether to clear every item by calling [[removeAt]].
* Defaults to false, meaning all items in the vector will be cleared directly
* without calling [[removeAt]].
*/
public function removeAll($safeClear = false)
{
if ($safeClear) {
for ($i = $this->_c - 1; $i >= 0; --$i) {
$this->removeAt($i);
}
} else {
$this->_d = array();
$this->_c = 0;
}
}
/**
* Returns a value indicating whether the vector contains the specified item.
* Note that the search is based on strict PHP comparison.
* @param mixed $item the item
* @return boolean whether the vector contains the item
*/
public function has($item)
{
return $this->indexOf($item) >= 0;
}
/**
* Returns the index of the specified item in the vector.
* The index is zero-based. If the item is not found in the vector, -1 will be returned.
* Note that the search is based on strict PHP comparison.
* @param mixed $item the item
* @return integer the index of the item in the vector (0 based), -1 if not found.
*/
public function indexOf($item)
{
$index = array_search($item, $this->_d, true);
return $index === false ? -1 : $index;
}
/**
* Returns the vector as a PHP array.
* @return array the items in the vector.
*/
public function toArray()
{
return $this->_d;
}
/**
* Copies iterable data into the vector.
* Note, existing data in the vector will be cleared first.
* @param mixed $data the data to be copied from, must be an array or an object implementing `Traversable`
* @throws InvalidParamException if data is neither an array nor an object implementing `Traversable`.
*/
public function copyFrom($data)
{
if (is_array($data) || $data instanceof \Traversable) {
if ($this->_c > 0) {
$this->removeAll();
}
if ($data instanceof self) {
$data = $data->_d;
}
foreach ($data as $item) {
$this->add($item);
}
} else {
throw new InvalidParamException('Data must be either an array or an object implementing Traversable.');
}
}
/**
* Merges iterable data into the vector.
* New items will be appended to the end of the existing items.
* @param array|\Traversable $data the data to be merged with. It must be an array or object implementing Traversable
* @throws InvalidParamException if data is neither an array nor an object implementing `Traversable`.
*/
public function mergeWith($data)
{
if (is_array($data) || ($data instanceof \Traversable)) {
if ($data instanceof Vector) {
$data = $data->_d;
}
foreach ($data as $item) {
$this->add($item);
}
} else {
throw new InvalidParamException('The data to be merged with must be an array or an object implementing Traversable.');
}
}
/**
* Returns a value indicating whether there is an item at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `isset($vector[$offset])`.
* @param integer $offset the offset to be checked
* @return boolean whether there is an item at the specified offset.
*/
public function offsetExists($offset)
{
return $offset >= 0 && $offset < $this->_c;
}
/**
* Returns the item at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `$value = $vector[$offset];`.
* This is equivalent to [[itemAt]].
* @param integer $offset the offset to retrieve item.
* @return mixed the item at the offset
* @throws Exception if the offset is out of range
*/
public function offsetGet($offset)
{
return $this->itemAt($offset);
}
/**
* Sets the item at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `$vector[$offset] = $item;`.
* If the offset is null or equal to the number of the existing items,
* the new item will be appended to the vector.
* Otherwise, the existing item at the offset will be replaced with the new item.
* @param integer $offset the offset to set item
* @param mixed $item the item value
* @throws Exception if the offset is out of range, or the vector is read only.
*/
public function offsetSet($offset, $item)
{
if ($offset === null || $offset === $this->_c) {
$this->insertAt($this->_c, $item);
} else {
$this->removeAt($offset);
$this->insertAt($offset, $item);
}
}
/**
* Unsets the item at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `unset($vector[$offset])`.
* This is equivalent to [[removeAt]].
* @param integer $offset the offset to unset item
* @throws Exception if the offset is out of range, or the vector is read only.
*/
public function offsetUnset($offset)
{
$this->removeAt($offset);
}
}

92
yii/base/VectorIterator.php

@ -1,92 +0,0 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* VectorIterator implements the SPL `Iterator` interface for [[Vector]].
*
* It allows [[Vector]] to return a new iterator for data traversing purpose.
* You normally do not use this class directly.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class VectorIterator implements \Iterator
{
/**
* @var array the data to be iterated through
*/
private $_d;
/**
* @var integer index of the current item
*/
private $_i;
/**
* @var integer count of the data items
*/
private $_c;
/**
* Constructor.
* @param array $data the data to be iterated through
*/
public function __construct(&$data)
{
$this->_d = &$data;
$this->_i = 0;
$this->_c = count($this->_d);
}
/**
* Rewinds the index of the current item.
* This method is required by the SPL interface `Iterator`.
*/
public function rewind()
{
$this->_i = 0;
}
/**
* Returns the key of the current item.
* This method is required by the SPL interface `Iterator`.
* @return integer the key of the current item
*/
public function key()
{
return $this->_i;
}
/**
* Returns the current item.
* This method is required by the SPL interface `Iterator`.
* @return mixed the current item
*/
public function current()
{
return $this->_d[$this->_i];
}
/**
* Moves the internal pointer to the next item.
* This method is required by the SPL interface `Iterator`.
*/
public function next()
{
$this->_i++;
}
/**
* Returns a value indicating whether there is an item at current position.
* This method is required by the SPL interface `Iterator`.
* @return boolean whether there is an item at current position.
*/
public function valid()
{
return $this->_i < $this->_c;
}
}

13
yii/base/View.php

@ -373,9 +373,10 @@ class View extends Component
*/ */
public function beginBlock($id, $renderInPlace = false) public function beginBlock($id, $renderInPlace = false)
{ {
return Block::begin($this, array( return Block::begin(array(
'id' => $id, 'id' => $id,
'renderInPlace' => $renderInPlace, 'renderInPlace' => $renderInPlace,
'view' => $this,
)); ));
} }
@ -390,10 +391,10 @@ class View extends Component
/** /**
* Begins the rendering of content that is to be decorated by the specified view. * Begins the rendering of content that is to be decorated by the specified view.
* This method can be used to implement nested layout. For example, a layout can be embedded * This method can be used to implement nested layout. For example, a layout can be embedded
* in another layout file specified as '@app/view/layouts/base' like the following: * in another layout file specified as '@app/view/layouts/base.php' like the following:
* *
* ~~~ * ~~~
* <?php $this->beginContent('@app/view/layouts/base'); ?> * <?php $this->beginContent('@app/view/layouts/base.php'); ?>
* ...layout content here... * ...layout content here...
* <?php $this->endContent(); ?> * <?php $this->endContent(); ?>
* ~~~ * ~~~
@ -406,9 +407,10 @@ class View extends Component
*/ */
public function beginContent($viewFile, $params = array()) public function beginContent($viewFile, $params = array())
{ {
return ContentDecorator::begin($this, array( return ContentDecorator::begin(array(
'viewFile' => $viewFile, 'viewFile' => $viewFile,
'params' => $params, 'params' => $params,
'view' => $this,
)); ));
} }
@ -442,8 +444,9 @@ class View extends Component
public function beginCache($id, $properties = array()) public function beginCache($id, $properties = array())
{ {
$properties['id'] = $id; $properties['id'] = $id;
$properties['view'] = $this;
/** @var $cache FragmentCache */ /** @var $cache FragmentCache */
$cache = FragmentCache::begin($this, $properties); $cache = FragmentCache::begin($properties);
if ($cache->getCachedContent() !== false) { if ($cache->getCachedContent() !== false) {
$this->endCache(); $this->endCache();
return false; return false;

66
yii/base/Widget.php

@ -18,16 +18,6 @@ use Yii;
class Widget extends Component class Widget extends Component
{ {
/** /**
* @var View the view object that this widget is associated with.
* The widget will use this view object to register any needed assets.
* This property is also required by [[render()]] and [[renderFile()]].
*/
public $view;
/**
* @var string id of the widget.
*/
private $_id;
/**
* @var integer a counter used to generate [[id]] for widgets. * @var integer a counter used to generate [[id]] for widgets.
* @internal * @internal
*/ */
@ -39,32 +29,19 @@ class Widget extends Component
*/ */
public static $_stack = array(); public static $_stack = array();
/**
* Constructor.
* @param View $view the view object that this widget is associated with.
* The widget will use this view object to register any needed assets.
* It is also required by [[render()]] and [[renderFile()]].
* @param array $config name-value pairs that will be used to initialize the object properties
*/
public function __construct($view, $config = array())
{
$this->view = $view;
parent::__construct($config);
}
/** /**
* Begins a widget. * Begins a widget.
* This method creates an instance of the calling class. It will apply the configuration * This method creates an instance of the calling class. It will apply the configuration
* to the created instance. A matching [[end()]] call should be called later. * to the created instance. A matching [[end()]] call should be called later.
* @param View $view the view object that the newly created widget is associated with.
* @param array $config name-value pairs that will be used to initialize the object properties * @param array $config name-value pairs that will be used to initialize the object properties
* @return Widget the newly created widget instance * @return Widget the newly created widget instance
*/ */
public static function begin($view, $config = array()) public static function begin($config = array())
{ {
$config['class'] = get_called_class(); $config['class'] = get_called_class();
/** @var Widget $widget */ /** @var Widget $widget */
$widget = Yii::createObject($config, $view); $widget = Yii::createObject($config);
self::$_stack[] = $widget; self::$_stack[] = $widget;
return $widget; return $widget;
} }
@ -93,21 +70,22 @@ class Widget extends Component
/** /**
* Creates a widget instance and runs it. * Creates a widget instance and runs it.
* The widget rendering result is returned by this method. * The widget rendering result is returned by this method.
* @param View $view the view object that the newly created widget is associated with.
* @param array $config name-value pairs that will be used to initialize the object properties * @param array $config name-value pairs that will be used to initialize the object properties
* @return string the rendering result of the widget. * @return string the rendering result of the widget.
*/ */
public static function widget($view, $config = array()) public static function widget($config = array())
{ {
ob_start(); ob_start();
ob_implicit_flush(false); ob_implicit_flush(false);
/** @var Widget $widget */ /** @var Widget $widget */
$config['class'] = get_called_class(); $config['class'] = get_called_class();
$widget = Yii::createObject($config, $view); $widget = Yii::createObject($config);
$widget->run(); $widget->run();
return ob_get_clean(); return ob_get_clean();
} }
private $_id;
/** /**
* Returns the ID of the widget. * Returns the ID of the widget.
* @param boolean $autoGenerate whether to generate an ID if it is not set previously * @param boolean $autoGenerate whether to generate an ID if it is not set previously
@ -130,6 +108,32 @@ class Widget extends Component
$this->_id = $value; $this->_id = $value;
} }
private $_view;
/**
* Returns the view object that can be used to render views or view files.
* The [[render()]] and [[renderFile()]] methods will use
* this view object to implement the actual view rendering.
* If not set, it will default to the "view" application component.
* @return View the view object that can be used to render views or view files.
*/
public function getView()
{
if ($this->_view === null) {
$this->_view = Yii::$app->getView();
}
return $this->_view;
}
/**
* Sets the view object to be used by this widget.
* @param View $view the view object that can be used to render views or view files.
*/
public function setView($view)
{
$this->_view = $view;
}
/** /**
* Executes the widget. * Executes the widget.
*/ */
@ -159,7 +163,7 @@ class Widget extends Component
public function render($view, $params = array()) public function render($view, $params = array())
{ {
$viewFile = $this->findViewFile($view); $viewFile = $this->findViewFile($view);
return $this->view->renderFile($viewFile, $params, $this); return $this->getView()->renderFile($viewFile, $params, $this);
} }
/** /**
@ -171,7 +175,7 @@ class Widget extends Component
*/ */
public function renderFile($file, $params = array()) public function renderFile($file, $params = array())
{ {
return $this->view->renderFile($file, $params, $this); return $this->getView()->renderFile($file, $params, $this);
} }
/** /**

106
yii/behaviors/AutoTimestamp.php

@ -0,0 +1,106 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\behaviors;
use yii\base\Behavior;
use yii\db\Expression;
use yii\db\ActiveRecord;
/**
* AutoTimestamp will automatically fill the attributes about creation time and updating time.
*
* AutoTimestamp fills the attributes when the associated AR model is being inserted or updated.
* You may specify an AR to use this behavior like the following:
*
* ~~~
* public function behaviors()
* {
* return array(
* 'timestamp' => array(
* 'class' => 'yii\behaviors\AutoTimestamp',
* ),
* );
* }
* ~~~
*
* By default, AutoTimestamp will fill the `create_time` attribute with the current timestamp
* when the associated AR object is being inserted; it will fill the `update_time` attribute
* with the timestamp when the AR object is being updated.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class AutoTimestamp extends Behavior
{
/**
* @var array list of attributes that are to be automatically filled with timestamps.
* The array keys are the ActiveRecord events upon which the attributes are to be filled with timestamps,
* and the array values are the corresponding attribute to be updated. You can use a string to represent
* a single attribute, or an array to represent a list of attributes.
* The default setting is to update the `create_time` attribute upon AR insertion,
* and update the `update_time` attribute upon AR updating.
*/
public $attributes = array(
ActiveRecord::EVENT_BEFORE_INSERT => 'create_time',
ActiveRecord::EVENT_BEFORE_UPDATE => 'update_time',
);
/**
* @var \Closure|Expression The expression that will be used for generating the timestamp.
* This can be either an anonymous function that returns the timestamp value,
* or an [[Expression]] object representing a DB expression (e.g. `new Expression('NOW()')`).
* If not set, it will use the value of `time()` to fill the attributes.
*/
public $timestamp;
/**
* Declares event handlers for the [[owner]]'s events.
* @return array events (array keys) and the corresponding event handler methods (array values).
*/
public function events()
{
$events = array();
$behavior = $this;
foreach ($this->attributes as $event => $attributes) {
if (!is_array($attributes)) {
$attributes = array($attributes);
}
$events[$event] = function () use ($behavior, $attributes) {
$behavior->updateTimestamp($attributes);
};
}
return $events;
}
/**
* Updates the attributes with the current timestamp.
* @param array $attributes list of attributes to be updated.
*/
public function updateTimestamp($attributes)
{
foreach ($attributes as $attribute) {
$this->owner->$attribute = $this->evaluateTimestamp($attribute);
}
}
/**
* Gets the appropriate timestamp for the specified attribute.
* @param string $attribute attribute name
* @return mixed the timestamp value
*/
protected function evaluateTimestamp($attribute)
{
if ($this->timestamp instanceof Expression) {
return $this->timestamp;
} elseif ($this->timestamp !== null) {
return call_user_func($this->timestamp);
} else {
return time();
}
}
}

6
composer.json → yii/composer.json

@ -63,12 +63,6 @@
"irc": "irc://irc.freenode.net/yii", "irc": "irc://irc.freenode.net/yii",
"source": "https://github.com/yiisoft/yii2" "source": "https://github.com/yiisoft/yii2"
}, },
"config": {
"vendor-dir": "yii/vendor"
},
"bin": [
"yii/yiic"
],
"require": { "require": {
"php": ">=5.3.0", "php": ">=5.3.0",
"michelf/php-markdown": "1.3", "michelf/php-markdown": "1.3",

8
yii/console/Application.php

@ -30,7 +30,7 @@ use yii\base\InvalidRouteException;
* To run the console application, enter the following on the command line: * To run the console application, enter the following on the command line:
* *
* ~~~ * ~~~
* yiic <route> [--param1=value1 --param2 ...] * yii <route> [--param1=value1 --param2 ...]
* ~~~ * ~~~
* *
* where `<route>` refers to a controller route in the form of `ModuleID/ControllerID/ActionID` * where `<route>` refers to a controller route in the form of `ModuleID/ControllerID/ActionID`
@ -42,7 +42,7 @@ use yii\base\InvalidRouteException;
* To use this command, simply type: * To use this command, simply type:
* *
* ~~~ * ~~~
* yiic help * yii help
* ~~~ * ~~~
* *
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
@ -94,7 +94,7 @@ class Application extends \yii\base\Application
list ($route, $params) = $request->resolve(); list ($route, $params) = $request->resolve();
return $this->runAction($route, $params); return $this->runAction($route, $params);
} else { } else {
throw new Exception(\Yii::t('yii|This script must be run from the command line.')); throw new Exception(\Yii::t('yii', 'This script must be run from the command line.'));
} }
} }
@ -113,7 +113,7 @@ class Application extends \yii\base\Application
try { try {
return parent::runAction($route, $params); return parent::runAction($route, $params);
} catch (InvalidRouteException $e) { } catch (InvalidRouteException $e) {
throw new Exception(\Yii::t('yii|Unknown command "{command}".', array('{command}' => $route))); throw new Exception(\Yii::t('yii', 'Unknown command "{command}".', array('{command}' => $route)));
} }
} }

8
yii/console/Controller.php

@ -18,10 +18,10 @@ use yii\helpers\Console;
* *
* A controller consists of one or several actions known as sub-commands. * A controller consists of one or several actions known as sub-commands.
* Users call a console command by specifying the corresponding route which identifies a controller action. * Users call a console command by specifying the corresponding route which identifies a controller action.
* The `yiic` program is used when calling a console command, like the following: * The `yii` program is used when calling a console command, like the following:
* *
* ~~~ * ~~~
* yiic <route> [--param1=value1 --param2 ...] * yii <route> [--param1=value1 --param2 ...]
* ~~~ * ~~~
* *
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
@ -91,7 +91,7 @@ class Controller extends \yii\base\Controller
$args = isset($params[Request::ANONYMOUS_PARAMS]) ? $params[Request::ANONYMOUS_PARAMS] : array(); $args = isset($params[Request::ANONYMOUS_PARAMS]) ? $params[Request::ANONYMOUS_PARAMS] : array();
unset($params[Request::ANONYMOUS_PARAMS]); unset($params[Request::ANONYMOUS_PARAMS]);
if (!empty($params)) { if (!empty($params)) {
throw new Exception(Yii::t('yii|Unknown options: {params}', array( throw new Exception(Yii::t('yii', 'Unknown options: {params}', array(
'{params}' => implode(', ', array_keys($params)), '{params}' => implode(', ', array_keys($params)),
))); )));
} }
@ -115,7 +115,7 @@ class Controller extends \yii\base\Controller
} }
if (!empty($missing)) { if (!empty($missing)) {
throw new Exception(Yii::t('yii|Missing required arguments: {params}', array( throw new Exception(Yii::t('yii', 'Missing required arguments: {params}', array(
'{params}' => implode(', ', $missing), '{params}' => implode(', ', $missing),
))); )));
} }

2
yii/console/Exception.php

@ -22,7 +22,7 @@ class Exception extends UserException
*/ */
public function getName() public function getName()
{ {
return \Yii::t('yii|Error'); return \Yii::t('yii', 'Error');
} }
} }

2
yii/console/Request.php

@ -27,7 +27,7 @@ class Request extends \yii\base\Request
public function resolve() public function resolve()
{ {
$rawParams = $this->getRawParams(); $rawParams = $this->getRawParams();
array_shift($rawParams); // the 1st argument is the yiic script name array_shift($rawParams); // the 1st argument is the yii script name
if (isset($rawParams[0])) { if (isset($rawParams[0])) {
$route = $rawParams[0]; $route = $rawParams[0];

203
yii/console/controllers/AssetController.php

@ -12,17 +12,32 @@ use yii\console\Exception;
use yii\console\Controller; use yii\console\Controller;
/** /**
* This command allows you to combine and compress your JavaScript and CSS files.
*
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class AssetController extends Controller class AssetController extends Controller
{ {
/**
* @var string controller default action ID.
*/
public $defaultAction = 'compress'; public $defaultAction = 'compress';
/**
* @var array list of asset bundles to be compressed.
* The keys are the bundle names, and the values are the configuration
* arrays for creating the [[yii\web\AssetBundle]] objects.
*/
public $bundles = array(); public $bundles = array();
/**
* @var array list of paths to the extensions, which assets should be also compressed.
* Each path should contain asset manifest file named "assets.php".
*/
public $extensions = array(); public $extensions = array();
/** /**
* @var array * @var array list of asset bundles, which represents output compressed files.
* You can specify the name of the output compressed file using 'css' and 'js' keys:
* For example:
* ~~~ * ~~~
* 'all' => array( * 'all' => array(
* 'css' => 'all.css', * 'css' => 'all.css',
@ -30,34 +45,74 @@ class AssetController extends Controller
* 'depends' => array( ... ), * 'depends' => array( ... ),
* ) * )
* ~~~ * ~~~
* File names can contain placeholder "{ts}", which will be filled by current timestamp, while
* file creation.
*/ */
public $targets = array(); public $targets = array();
/**
* @var array configuration for [[yii\web\AssetManager]] instance, which will be used
* for assets publishing.
*/
public $assetManager = array(); public $assetManager = array();
/**
* @var string|callback Java Script file compressor.
* If a string, it is treated as shell command template, which should contain
* placeholders {from} - source file name - and {to} - output file name.
* If an array, it is treated as PHP callback, which should perform the compression.
*
* Default value relies on usage of "Closure Compiler"
* @see https://developers.google.com/closure/compiler/
*/
public $jsCompressor = 'java -jar compiler.jar --js {from} --js_output_file {to}'; public $jsCompressor = 'java -jar compiler.jar --js {from} --js_output_file {to}';
/**
* @var string|callback CSS file compressor.
* If a string, it is treated as shell command template, which should contain
* placeholders {from} - source file name - and {to} - output file name.
* If an array, it is treated as PHP callback, which should perform the compression.
*
* Default value relies on usage of "YUI Compressor"
* @see https://github.com/yui/yuicompressor/
*/
public $cssCompressor = 'java -jar yuicompressor.jar {from} -o {to}'; public $cssCompressor = 'java -jar yuicompressor.jar {from} -o {to}';
/**
* Combines and compresses the asset files according to the given configuration.
* During the process new asset bundle configuration file will be created.
* You should replace your original asset bundle configuration with this file in order to use compressed files.
* @param string $configFile configuration file name.
* @param string $bundleFile output asset bundles configuration file name.
*/
public function actionCompress($configFile, $bundleFile) public function actionCompress($configFile, $bundleFile)
{ {
$this->loadConfiguration($configFile); $this->loadConfiguration($configFile);
$bundles = $this->loadBundles($this->bundles, $this->extensions); $bundles = $this->loadBundles($this->bundles, $this->extensions);
$targets = $this->loadTargets($this->targets, $bundles); $targets = $this->loadTargets($this->targets, $bundles);
$this->publishBundles($bundles, $this->publishOptions); $this->publishBundles($bundles, $this->assetManager);
$timestamp = time(); $timestamp = time();
foreach ($targets as $target) { foreach ($targets as $name => $target) {
echo "Creating output bundle '{$name}':\n";
if (!empty($target->js)) { if (!empty($target->js)) {
$this->buildTarget($target, 'js', $bundles, $timestamp); $this->buildTarget($target, 'js', $bundles, $timestamp);
} }
if (!empty($target->css)) { if (!empty($target->css)) {
$this->buildTarget($target, 'css', $bundles, $timestamp); $this->buildTarget($target, 'css', $bundles, $timestamp);
} }
echo "\n";
} }
$targets = $this->adjustDependency($targets, $bundles); $targets = $this->adjustDependency($targets, $bundles);
$this->saveTargets($targets, $bundleFile); $this->saveTargets($targets, $bundleFile);
} }
/**
* Applies configuration from the given file to self instance.
* @param string $configFile configuration file name.
* @throws \yii\console\Exception on failure.
*/
protected function loadConfiguration($configFile) protected function loadConfiguration($configFile)
{ {
echo "Loading configuration from '{$configFile}'...\n";
foreach (require($configFile) as $name => $value) { foreach (require($configFile) as $name => $value) {
if (property_exists($this, $name)) { if (property_exists($this, $name)) {
$this->$name = $value; $this->$name = $value;
@ -74,8 +129,15 @@ class AssetController extends Controller
} }
} }
/**
* Creates full list of source asset bundles.
* @param array[] $bundles list of asset bundle configurations.
* @param array $extensions list of the extension paths.
* @return \yii\web\AssetBundle[] list of source asset bundles.
*/
protected function loadBundles($bundles, $extensions) protected function loadBundles($bundles, $extensions)
{ {
echo "Collecting source bundles information...\n";
$result = array(); $result = array();
foreach ($bundles as $name => $bundle) { foreach ($bundles as $name => $bundle) {
$bundle['class'] = 'yii\\web\\AssetBundle'; $bundle['class'] = 'yii\\web\\AssetBundle';
@ -96,6 +158,13 @@ class AssetController extends Controller
return $result; return $result;
} }
/**
* Creates full list of output asset bundles.
* @param array $targets output asset bundles configuration.
* @param \yii\web\AssetBundle[] $bundles list of source asset bundles.
* @return \yii\web\AssetBundle[] list of output asset bundles.
* @throws \yii\console\Exception on failure.
*/
protected function loadTargets($targets, $bundles) protected function loadTargets($targets, $bundles)
{ {
// build the dependency order of bundles // build the dependency order of bundles
@ -151,26 +220,31 @@ class AssetController extends Controller
} }
/** /**
* @param \yii\web\AssetBundle[] $bundles * Publishes given asset bundles.
* @param array $options * @param \yii\web\AssetBundle[] $bundles asset bundles to be published.
* @param array $options assert manager instance configuration.
*/ */
protected function publishBundles($bundles, $options) protected function publishBundles($bundles, $options)
{ {
echo "\nPublishing bundles:\n";
if (!isset($options['class'])) { if (!isset($options['class'])) {
$options['class'] = 'yii\\web\\AssetManager'; $options['class'] = 'yii\\web\\AssetManager';
} }
$am = Yii::createObject($options); $am = Yii::createObject($options);
foreach ($bundles as $bundle) { foreach ($bundles as $name => $bundle) {
$bundle->publish($am); $bundle->publish($am);
echo " '".$name."' published.\n";
} }
echo "\n";
} }
/** /**
* @param \yii\web\AssetBundle $target * Builds output asset bundle.
* @param string $type either "js" or "css" * @param \yii\web\AssetBundle $target output asset bundle
* @param \yii\web\AssetBundle[] $bundles * @param string $type either "js" or "css".
* @param integer $timestamp * @param \yii\web\AssetBundle[] $bundles source asset bundles.
* @throws Exception * @param integer $timestamp current timestamp.
* @throws Exception on failure.
*/ */
protected function buildTarget($target, $type, $bundles, $timestamp) protected function buildTarget($target, $type, $bundles, $timestamp)
{ {
@ -182,7 +256,7 @@ class AssetController extends Controller
foreach ($target->depends as $name) { foreach ($target->depends as $name) {
if (isset($bundles[$name])) { if (isset($bundles[$name])) {
foreach ($bundles[$name]->$type as $file) { foreach ($bundles[$name]->$type as $file) {
$inputFiles[] = $bundles[$name]->basePath . '/' . $file; $inputFiles[] = $bundles[$name]->basePath . $file;
} }
} else { } else {
throw new Exception("Unknown bundle: $name"); throw new Exception("Unknown bundle: $name");
@ -196,8 +270,16 @@ class AssetController extends Controller
$target->$type = array($outputFile); $target->$type = array($outputFile);
} }
/**
* Adjust dependencies between asset bundles in the way source bundles begin to depend on output ones.
* @param \yii\web\AssetBundle[] $targets output asset bundles.
* @param \yii\web\AssetBundle[] $bundles source asset bundles.
* @return \yii\web\AssetBundle[] output asset bundles.
*/
protected function adjustDependency($targets, $bundles) protected function adjustDependency($targets, $bundles)
{ {
echo "Creating new bundle configuration...\n";
$map = array(); $map = array();
foreach ($targets as $name => $target) { foreach ($targets as $name => $target) {
foreach ($target->depends as $bundle) { foreach ($target->depends as $bundle) {
@ -231,6 +313,13 @@ class AssetController extends Controller
return $targets; return $targets;
} }
/**
* Registers asset bundles including their dependencies.
* @param \yii\web\AssetBundle[] $bundles asset bundles list.
* @param string $name bundle name.
* @param array $registered stores already registered names.
* @throws \yii\console\Exception if circular dependency is detected.
*/
protected function registerBundle($bundles, $name, &$registered) protected function registerBundle($bundles, $name, &$registered)
{ {
if (!isset($registered[$name])) { if (!isset($registered[$name])) {
@ -246,6 +335,11 @@ class AssetController extends Controller
} }
} }
/**
* Saves new asset bundles configuration.
* @param \yii\web\AssetBundle[] $targets list of asset bundles to be saved.
* @param string $bundleFile output file name.
*/
protected function saveTargets($targets, $bundleFile) protected function saveTargets($targets, $bundleFile)
{ {
$array = array(); $array = array();
@ -258,70 +352,117 @@ class AssetController extends Controller
} }
$array = var_export($array, true); $array = var_export($array, true);
$version = date('Y-m-d H:i:s', time()); $version = date('Y-m-d H:i:s', time());
file_put_contents($bundleFile, <<<EOD $bytesWritten = file_put_contents($bundleFile, <<<EOD
<?php <?php
/** /**
* This file is generated by the "yiic script" command. * This file is generated by the "yii script" command.
* DO NOT MODIFY THIS FILE DIRECTLY. * DO NOT MODIFY THIS FILE DIRECTLY.
* @version $version * @version $version
*/ */
return $array; return $array;
EOD EOD
); );
if ($bytesWritten <= 0) {
throw new Exception("Unable to write output bundle configuration at '{$bundleFile}'.");
}
echo "Output bundle configuration created at '{$bundleFile}'.\n";
} }
/**
* Compresses given Java Script files and combines them into the single one.
* @param array $inputFiles list of source file names.
* @param string $outputFile output file name.
* @throws \yii\console\Exception on failure
*/
protected function compressJsFiles($inputFiles, $outputFile) protected function compressJsFiles($inputFiles, $outputFile)
{ {
if (empty($inputFiles)) {
return;
}
echo " Compressing JavaScript files...\n";
if (is_string($this->jsCompressor)) { if (is_string($this->jsCompressor)) {
$tmpFile = $outputFile . '.tmp'; $tmpFile = $outputFile . '.tmp';
$this->combineJsFiles($inputFiles, $tmpFile); $this->combineJsFiles($inputFiles, $tmpFile);
$log = shell_exec(strtr($this->jsCompressor, array( $log = shell_exec(strtr($this->jsCompressor, array(
'{from}' => $tmpFile, '{from}' => escapeshellarg($tmpFile),
'{to}' => $outputFile, '{to}' => escapeshellarg($outputFile),
))); )));
@unlink($tmpFile); @unlink($tmpFile);
} else { } else {
$log = call_user_func($this->jsCompressor, $this, $inputFiles, $outputFile); $log = call_user_func($this->jsCompressor, $this, $inputFiles, $outputFile);
} }
if (!file_exists($outputFile)) {
throw new Exception("Unable to compress JavaScript files into '{$outputFile}'.");
}
echo " JavaScript files compressed into '{$outputFile}'.\n";
} }
/**
* Compresses given CSS files and combines them into the single one.
* @param array $inputFiles list of source file names.
* @param string $outputFile output file name.
* @throws \yii\console\Exception on failure
*/
protected function compressCssFiles($inputFiles, $outputFile) protected function compressCssFiles($inputFiles, $outputFile)
{ {
if (empty($inputFiles)) {
return;
}
echo " Compressing CSS files...\n";
if (is_string($this->cssCompressor)) { if (is_string($this->cssCompressor)) {
$tmpFile = $outputFile . '.tmp'; $tmpFile = $outputFile . '.tmp';
$this->combineCssFiles($inputFiles, $tmpFile); $this->combineCssFiles($inputFiles, $tmpFile);
$log = shell_exec(strtr($this->cssCompressor, array( $log = shell_exec(strtr($this->cssCompressor, array(
'{from}' => $inputFiles, '{from}' => escapeshellarg($tmpFile),
'{to}' => $outputFile, '{to}' => escapeshellarg($outputFile),
))); )));
@unlink($tmpFile);
} else { } else {
$log = call_user_func($this->cssCompressor, $this, $inputFiles, $outputFile); $log = call_user_func($this->cssCompressor, $this, $inputFiles, $outputFile);
} }
if (!file_exists($outputFile)) {
throw new Exception("Unable to compress CSS files into '{$outputFile}'.");
}
echo " CSS files compressed into '{$outputFile}'.\n";
} }
public function combineJsFiles($files, $tmpFile) /**
* Combines Java Script files into a single one.
* @param array $inputFiles source file names.
* @param string $outputFile output file name.
*/
public function combineJsFiles($inputFiles, $outputFile)
{ {
$content = ''; $content = '';
foreach ($files as $file) { foreach ($inputFiles as $file) {
$content .= "/*** BEGIN FILE: $file ***/\n" $content .= "/*** BEGIN FILE: $file ***/\n"
. file_get_contents($file) . file_get_contents($file)
. "/*** END FILE: $file ***/\n"; . "/*** END FILE: $file ***/\n";
} }
file_put_contents($tmpFile, $content); file_put_contents($outputFile, $content);
} }
public function combineCssFiles($files, $tmpFile) /**
* Combines CSS files into a single one.
* @param array $inputFiles source file names.
* @param string $outputFile output file name.
*/
public function combineCssFiles($inputFiles, $outputFile)
{ {
// todo: adjust url() references in CSS files // todo: adjust url() references in CSS files
$content = ''; $content = '';
foreach ($files as $file) { foreach ($inputFiles as $file) {
$content .= "/*** BEGIN FILE: $file ***/\n" $content .= "/*** BEGIN FILE: $file ***/\n"
. file_get_contents($file) . file_get_contents($file)
. "/*** END FILE: $file ***/\n"; . "/*** END FILE: $file ***/\n";
} }
file_put_contents($tmpFile, $content); file_put_contents($outputFile, $content);
} }
/**
* Creates template of configuration file for [[actionCompress]].
* @param string $configFile output file name.
*/
public function actionTemplate($configFile) public function actionTemplate($configFile)
{ {
$template = <<<EOD $template = <<<EOD
@ -348,6 +489,16 @@ return array(
), ),
); );
EOD; EOD;
file_put_contents($configFile, $template); if (file_exists($configFile)) {
if (!$this->confirm("File '{$configFile}' already exists. Do you wish to overwrite it?")) {
return;
}
}
$bytesWritten = file_put_contents($configFile, $template);
if ($bytesWritten<=0) {
echo "Error: unable to write file '{$configFile}'!\n\n";
} else {
echo "Configuration file template created at '{$configFile}'.\n\n";
}
} }
} }

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

Loading…
Cancel
Save