Browse Source

Merge branch 'master' of github.com:yiisoft/yii2 into redis

* 'master' of github.com:yiisoft/yii2: (87 commits)
  Added dirty attribute description.
  Fixed doc about renderers.
  Finished the initial draft of upgrading instructions
  Fix attaching behavior via config
  Changed default value of View::renderers.
  Fix attaching behavior via config
  Menu WIP
  upgrading instructions WIP
  Refactored Breadcrumbs.
  Fixes issue #54: Implemented Breadcrumbs.
  Added Breadcrumbs.php
  Fixes issue #134
  Rollback word consistencty over entire codebase (ref. #139).
  Add ensureBehaviors() to detachBehavior*()
  Fixes issue #124.
  code reorganization fix.
  reorganized app code. removed app template from framework folder.
  Fixes issue #128.
  Fixes issue #124.
  Add Newlines
  ...

Conflicts:
	tests/unit/framework/caching/ApcCacheTest.php
tags/2.0.0-alpha
Carsten Brandt 12 years ago
parent
commit
f59f39087f
  1. 14
      .travis.yml
  2. 26
      app/protected/views/layouts/main.php
  3. 17
      app/protected/views/site/index.php
  4. 0
      apps/bootstrap/assets/.gitignore
  5. 0
      apps/bootstrap/css/bootstrap-responsive.css
  6. 0
      apps/bootstrap/css/bootstrap-responsive.min.css
  7. 0
      apps/bootstrap/css/bootstrap.css
  8. 0
      apps/bootstrap/css/bootstrap.min.css
  9. 78
      apps/bootstrap/css/site.css
  10. 0
      apps/bootstrap/img/glyphicons-halflings-white.png
  11. 0
      apps/bootstrap/img/glyphicons-halflings.png
  12. 2
      apps/bootstrap/index.php
  13. 0
      apps/bootstrap/js/bootstrap.js
  14. 0
      apps/bootstrap/js/bootstrap.min.js
  15. 1
      apps/bootstrap/protected/.htaccess
  16. 19
      apps/bootstrap/protected/config/assets.php
  17. 10
      apps/bootstrap/protected/config/main.php
  18. 23
      apps/bootstrap/protected/controllers/SiteController.php
  19. 63
      apps/bootstrap/protected/models/ContactForm.php
  20. 29
      apps/bootstrap/protected/models/LoginForm.php
  21. 2
      apps/bootstrap/protected/models/User.php
  22. 0
      apps/bootstrap/protected/runtime/.gitignore
  23. 60
      apps/bootstrap/protected/views/layouts/main.php
  24. 16
      apps/bootstrap/protected/views/site/about.php
  25. 34
      apps/bootstrap/protected/views/site/contact.php
  26. 47
      apps/bootstrap/protected/views/site/index.php
  27. 12
      apps/bootstrap/protected/views/site/login.php
  28. 2
      build/build
  29. 4
      docs/api/db/ActiveRecord.md
  30. 0
      docs/guide/active-record.md
  31. 0
      docs/guide/application.md
  32. 0
      docs/guide/authentication.md
  33. 0
      docs/guide/authorization.md
  34. 63
      docs/guide/bootstrap.md
  35. 3
      docs/guide/caching.md
  36. 0
      docs/guide/console.md
  37. 0
      docs/guide/controller.md
  38. 0
      docs/guide/dao.md
  39. 3
      docs/guide/error.md
  40. 0
      docs/guide/extension.md
  41. 0
      docs/guide/form.md
  42. 0
      docs/guide/gii.md
  43. 0
      docs/guide/i18n.md
  44. 30
      docs/guide/index.md
  45. 112
      docs/guide/installation.md
  46. 0
      docs/guide/logging.md
  47. 319
      docs/guide/migration.md
  48. 0
      docs/guide/model.md
  49. 52
      docs/guide/mvc.md
  50. 36
      docs/guide/overview.md
  51. 181
      docs/guide/performance.md
  52. 0
      docs/guide/query-builder.md
  53. 0
      docs/guide/security.md
  54. 3
      docs/guide/template.md
  55. 0
      docs/guide/testing.md
  56. 0
      docs/guide/theming.md
  57. 8
      docs/guide/title.md
  58. 437
      docs/guide/upgrade-from-v1.md
  59. 0
      docs/guide/upgrade.md
  60. 3
      docs/guide/url.md
  61. 0
      docs/guide/validation.md
  62. 0
      docs/guide/view.md
  63. 34
      docs/view_renderers.md
  64. 1
      framework/.htaccess
  65. 9
      framework/YiiBase.php
  66. 2
      framework/assets.php
  67. 580
      framework/assets/yii.activeForm.js
  68. 63
      framework/assets/yii.js
  69. 187
      framework/assets/yii.validation.js
  70. 2
      framework/base/ActionFilter.php
  71. 9
      framework/base/Application.php
  72. 3
      framework/base/Component.php
  73. 2
      framework/base/Exception.php
  74. 30
      framework/base/View.php
  75. 2
      framework/base/ViewEvent.php
  76. 5
      framework/base/Widget.php
  77. 2
      framework/caching/Cache.php
  78. 2
      framework/caching/Dependency.php
  79. 16
      framework/caching/ExpressionDependency.php
  80. 2
      framework/caching/MemCacheServer.php
  81. 2
      framework/caching/WinCache.php
  82. 2
      framework/console/Controller.php
  83. 2
      framework/console/controllers/AppController.php
  84. 2
      framework/console/controllers/AssetController.php
  85. 2
      framework/console/controllers/HelpController.php
  86. 17
      framework/console/webapp/config.php
  87. 10
      framework/console/webapp/default/index.php
  88. 20
      framework/console/webapp/default/protected/config/main.php
  89. 15
      framework/console/webapp/default/protected/controllers/SiteController.php
  90. 17
      framework/console/webapp/default/protected/views/layouts/main.php
  91. 1
      framework/console/webapp/default/protected/views/site/index.php
  92. 3
      framework/db/ActiveQuery.php
  93. 2
      framework/db/ActiveRelation.php
  94. 4
      framework/db/Connection.php
  95. 2
      framework/db/Exception.php
  96. 2
      framework/db/Expression.php
  97. 10
      framework/db/Migration.php
  98. 4
      framework/db/Query.php
  99. 2
      framework/db/StaleObjectException.php
  100. 6
      framework/db/Transaction.php
  101. Some files were not shown because too many files have changed in this diff Show More

14
.travis.yml

@ -0,0 +1,14 @@
language: php
php:
- 5.3
- 5.4
- 5.5
env:
- DB=mysql
before_script:
- sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'create database IF NOT EXISTS yiitest;'; fi"
script: phpunit

26
app/protected/views/layouts/main.php

@ -1,26 +0,0 @@
<?php
/**
* @var $this \yii\base\View
* @var $content string
*/
use yii\helpers\Html;
?>
<?php $this->beginPage(); ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title><?php echo Html::encode($this->title); ?></title>
<?php echo Html::cssFile("css/bootstrap.min.css", array('media' => 'screen')); ?>
<?php $this->head(); ?>
</head>
<body>
<div class="container">
<h1>Welcome</h1>
<?php $this->beginBody(); ?>
<?php echo $content; ?>
<?php $this->endBody(); ?>
</div>
</body>
</html>
<?php $this->endPage(); ?>

17
app/protected/views/site/index.php

@ -1,17 +0,0 @@
<?php
/** @var $this \yii\base\View */
use yii\helpers\Html;
$this->title = 'Hello World';
$user = Yii::$app->getUser();
if ($user->isGuest) {
echo Html::a('login', array('login'));
} else {
echo "You are logged in as " . $user->identity->username . "<br/>";
echo Html::a('logout', array('logout'));
}
?>

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

0
app/css/bootstrap-responsive.css → apps/bootstrap/css/bootstrap-responsive.css vendored

0
app/css/bootstrap-responsive.min.css → apps/bootstrap/css/bootstrap-responsive.min.css vendored

0
app/css/bootstrap.css → apps/bootstrap/css/bootstrap.css vendored

0
app/css/bootstrap.min.css → apps/bootstrap/css/bootstrap.min.css vendored

78
apps/bootstrap/css/site.css vendored

@ -0,0 +1,78 @@
body {
padding-top: 20px;
padding-bottom: 60px;
}
/* Custom container */
.container {
margin: 0 auto;
max-width: 1000px;
}
.container > hr {
margin: 60px 0;
}
/* Main marketing message and sign up button */
.jumbotron {
margin: 80px 0;
text-align: center;
}
.jumbotron h1 {
font-size: 100px;
line-height: 1;
}
.jumbotron .lead {
font-size: 24px;
line-height: 1.25;
}
.jumbotron .btn {
font-size: 21px;
padding: 14px 24px;
}
/* Supporting marketing content */
.marketing {
margin: 60px 0;
}
.marketing p + h4 {
margin-top: 28px;
}
/* Customize the navbar links to be fill the entire space of the .navbar */
.navbar .navbar-inner {
padding: 0;
}
.navbar .nav {
margin: 0;
display: table;
width: 100%;
}
.navbar .nav li {
display: table-cell;
width: 1%;
float: none;
}
.navbar .nav li a {
font-weight: bold;
text-align: center;
border-left: 1px solid rgba(255, 255, 255, .75);
border-right: 1px solid rgba(0, 0, 0, .1);
}
.navbar .nav li:first-child a {
border-left: 0;
border-radius: 3px 0 0 3px;
}
.navbar .nav li:last-child a {
border-right: 0;
border-radius: 0 3px 3px 0;
}

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

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

0
app/img/glyphicons-halflings.png → apps/bootstrap/img/glyphicons-halflings.png

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

2
app/index.php → apps/bootstrap/index.php

@ -2,7 +2,7 @@
defined('YII_DEBUG') or define('YII_DEBUG', true); defined('YII_DEBUG') or define('YII_DEBUG', true);
require(__DIR__ . '/../framework/yii.php'); require(__DIR__ . '/../../framework/yii.php');
$config = require(__DIR__ . '/protected/config/main.php'); $config = require(__DIR__ . '/protected/config/main.php');
$application = new yii\web\Application($config); $application = new yii\web\Application($config);

0
app/js/bootstrap.js → apps/bootstrap/js/bootstrap.js vendored

0
app/js/bootstrap.min.js → apps/bootstrap/js/bootstrap.min.js vendored

1
apps/bootstrap/protected/.htaccess

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

19
apps/bootstrap/protected/config/assets.php

@ -0,0 +1,19 @@
<?php
return array(
'app' => array(
'basePath' => '@wwwroot',
'baseUrl' => '@www',
'css' => array(
'css/bootstrap.min.css',
'css/bootstrap-responsive.min.css',
'css/site.css',
),
'js' => array(
),
'depends' => array(
'yii',
),
),
);

10
app/protected/config/main.php → apps/bootstrap/protected/config/main.php

@ -10,6 +10,12 @@ return array(
'user' => array( 'user' => array(
'class' => 'yii\web\User', 'class' => 'yii\web\User',
'identityClass' => 'app\models\User', 'identityClass' => 'app\models\User',
) ),
'assetManager' => array(
'bundles' => require(__DIR__ . '/assets.php'),
),
),
'params' => array(
'adminEmail' => 'admin@example.com',
), ),
); );

23
app/protected/controllers/SiteController.php → apps/bootstrap/protected/controllers/SiteController.php

@ -2,6 +2,7 @@
use yii\web\Controller; use yii\web\Controller;
use app\models\LoginForm; use app\models\LoginForm;
use app\models\ContactForm;
class SiteController extends Controller class SiteController extends Controller
{ {
@ -14,7 +15,7 @@ class SiteController extends Controller
{ {
$model = new LoginForm(); $model = new LoginForm();
if ($this->populate($_POST, $model) && $model->login()) { if ($this->populate($_POST, $model) && $model->login()) {
Yii::$app->getResponse()->redirect(array('site/index')); Yii::$app->response->redirect(array('site/index'));
} else { } else {
echo $this->render('login', array( echo $this->render('login', array(
'model' => $model, 'model' => $model,
@ -27,4 +28,22 @@ class SiteController extends Controller
Yii::$app->getUser()->logout(); Yii::$app->getUser()->logout();
Yii::$app->getResponse()->redirect(array('site/index')); Yii::$app->getResponse()->redirect(array('site/index'));
} }
}
public function actionContact()
{
$model = new ContactForm;
if ($this->populate($_POST, $model) && $model->contact(Yii::$app->params['adminEmail'])) {
Yii::$app->session->setFlash('contactFormSubmitted');
Yii::$app->response->refresh();
} else {
echo $this->render('contact', array(
'model' => $model,
));
}
}
public function actionAbout()
{
echo $this->render('about');
}
}

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

@ -0,0 +1,63 @@
<?php
namespace app\models;
use yii\base\Model;
/**
* ContactForm is the model behind the contact form.
*/
class ContactForm extends Model
{
public $name;
public $email;
public $subject;
public $body;
public $verifyCode;
/**
* @return array the validation rules.
*/
public function rules()
{
return array(
// name, email, subject and body are required
array('name, email, subject, body', 'required'),
// email has to be a valid email address
array('email', 'email'),
// verifyCode needs to be entered correctly
//array('verifyCode', 'captcha', 'allowEmpty' => !Captcha::checkRequirements()),
);
}
/**
* @return array customized attribute labels
*/
public function attributeLabels()
{
return array(
'verifyCode' => 'Verification Code',
);
}
/**
* Sends an email to the specified email address using the information collected by this model.
* @param string $email the target email address
* @return boolean whether the model passes validation
*/
public function contact($email)
{
if ($this->validate()) {
$name = '=?UTF-8?B?' . base64_encode($this->name) . '?=';
$subject = '=?UTF-8?B?' . base64_encode($this->subject) . '?=';
$headers = "From: $name <{$this->email}>\r\n" .
"Reply-To: {$this->email}\r\n" .
"MIME-Version: 1.0\r\n" .
"Content-type: text/plain; charset=UTF-8";
mail($email, $subject, $this->body, $headers);
return true;
} else {
return false;
}
}
}

29
app/protected/models/LoginForm.php → apps/bootstrap/protected/models/LoginForm.php

@ -1,9 +1,4 @@
<?php <?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace app\models; namespace app\models;
@ -11,8 +6,7 @@ use Yii;
use yii\base\Model; use yii\base\Model;
/** /**
* @author Qiang Xue <qiang.xue@gmail.com> * LoginForm is the model behind the login form.
* @since 2.0
*/ */
class LoginForm extends Model class LoginForm extends Model
{ {
@ -20,16 +14,25 @@ class LoginForm extends Model
public $password; public $password;
public $rememberMe = true; public $rememberMe = true;
/**
* @return array the validation rules.
*/
public function rules() public function rules()
{ {
return array( return array(
array('username', 'required'), // username and password are both required
array('password', 'required'), array('username, password', 'required'),
// password is validated by validatePassword()
array('password', 'validatePassword'), array('password', 'validatePassword'),
// rememberMe must be a boolean value
array('rememberMe', 'boolean'), array('rememberMe', 'boolean'),
); );
} }
/**
* Validates the password.
* This method serves as the inline validation for password.
*/
public function validatePassword() public function validatePassword()
{ {
$user = User::findByUsername($this->username); $user = User::findByUsername($this->username);
@ -38,14 +41,18 @@ class LoginForm extends Model
} }
} }
/**
* Logs in a user using the provided username and password.
* @return boolean whether the user is logged in successfully
*/
public function login() public function login()
{ {
if ($this->validate()) { if ($this->validate()) {
$user = User::findByUsername($this->username); $user = User::findByUsername($this->username);
Yii::$app->getUser()->login($user, $this->rememberMe ? 3600*24*30 : 0); Yii::$app->user->login($user, $this->rememberMe ? 3600*24*30 : 0);
return true; return true;
} else { } else {
return false; return false;
} }
} }
} }

2
app/protected/models/User.php → apps/bootstrap/protected/models/User.php

@ -58,4 +58,4 @@ class User extends \yii\base\Object implements \yii\web\Identity
{ {
return $this->password === $password; return $this->password === $password;
} }
} }

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

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

@ -0,0 +1,60 @@
<?php
/**
* @var $this \yii\base\View
* @var $content string
*/
use yii\helpers\Html;
$this->registerAssetBundle('app');
?>
<?php $this->beginPage(); ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title><?php echo Html::encode($this->title); ?></title>
<?php $this->head(); ?>
</head>
<body>
<div class="container">
<?php $this->beginBody(); ?>
<div class="masthead">
<h3 class="muted">My Company</h3>
<div class="navbar">
<div class="navbar-inner">
<div class="container">
<ul class="nav">
<li><?php echo Html::a('Home', Yii::$app->homeUrl); ?></li>
<li><?php echo Html::a('About', array('/site/about')); ?></li>
<li><?php echo Html::a('Contact', array('/site/contact')); ?></li>
<?php if (Yii::$app->user->isGuest): ?>
<li><?php echo Html::a('Login', array('/site/login')); ?></li>
<?php else: ?>
<li><?php echo Html::a('Logout (' . Html::encode(Yii::$app->user->identity->username) . ')', array('/site/logout')); ?></li>
<?php endif; ?>
</ul>
</div>
</div>
</div>
<!-- /.navbar -->
</div>
<?php $this->widget('yii\widgets\Breadcrumbs', array(
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : array(),
)); ?>
<?php echo $content; ?>
<hr>
<div class="footer">
<p>&copy; My Company <?php echo date('Y'); ?></p>
<p>
<?php echo Yii::powered(); ?>
Template by <a href="http://twitter.github.io/bootstrap/">Twitter Bootstrap</a>
</p>
</div>
<?php $this->endBody(); ?>
</div>
</body>
</html>
<?php $this->endPage(); ?>

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

@ -0,0 +1,16 @@
<?php
use yii\helpers\Html;
/**
* @var yii\base\View $this
*/
$this->title = 'About';
$this->params['breadcrumbs'][] = $this->title;
?>
<h1><?php echo Html::encode($this->title); ?></h1>
<p>
This is the About page. You may modify the following file to customize its content:
</p>
<code><?php echo __FILE__; ?></code>

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

@ -0,0 +1,34 @@
<?php
use yii\helpers\Html;
/**
* @var yii\base\View $this
* @var yii\widgets\ActiveForm $form
* @var app\models\ContactForm $model
*/
$this->title = 'Contact';
$this->params['breadcrumbs'][] = $this->title;
?>
<h1><?php echo Html::encode($this->title); ?></h1>
<?php if(Yii::$app->session->hasFlash('contactFormSubmitted')): ?>
<div class="alert alert-success">
Thank you for contacting us. We will respond to you as soon as possible.
</div>
<?php return; endif; ?>
<p>
If you have business inquiries or other questions, please fill out the following form to contact us. Thank you.
</p>
<?php $form = $this->beginWidget('yii\widgets\ActiveForm', array(
'options' => array('class' => 'form-horizontal'),
'fieldConfig' => array('inputOptions' => array('class' => 'input-xlarge')),
)); ?>
<?php echo $form->field($model, 'name')->textInput(); ?>
<?php echo $form->field($model, 'email')->textInput(); ?>
<?php echo $form->field($model, 'subject')->textInput(); ?>
<?php echo $form->field($model, 'body')->textArea(array('rows' => 6)); ?>
<div class="form-actions">
<?php echo Html::submitButton('Submit', null, null, array('class' => 'btn btn-primary')); ?>
</div>
<?php $this->endWidget(); ?>

47
apps/bootstrap/protected/views/site/index.php

@ -0,0 +1,47 @@
<?php
/**
* @var yii\base\View $this
*/
$this->title = 'Welcome';
?>
<div class="jumbotron">
<h1>Welcome!</h1>
<p class="lead">Cras justo odio, dapibus ac facilisis in, egestas eget quam. Fusce dapibus, tellus ac cursus
commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
<a class="btn btn-large btn-success" href="http://www.yiiframework.com">Get started with Yii</a>
</div>
<hr>
<!-- Example row of columns -->
<div class="row-fluid">
<div class="span4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn" href="#">View details &raquo;</a></p>
</div>
<div class="span4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn" href="#">View details &raquo;</a></p>
</div>
<div class="span4">
<h2>Heading</h2>
<p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta
felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum
massa.</p>
<p><a class="btn" href="#">View details &raquo;</a></p>
</div>
</div>

12
app/protected/views/site/login.php → apps/bootstrap/protected/views/site/login.php

@ -5,8 +5,10 @@ use yii\helpers\Html;
* @var yii\widgets\ActiveForm $form * @var yii\widgets\ActiveForm $form
* @var app\models\LoginForm $model * @var app\models\LoginForm $model
*/ */
$this->title = 'Login';
$this->params['breadcrumbs'][] = $this->title;
?> ?>
<h1>Login</h1> <h1><?php echo Html::encode($this->title); ?></h1>
<p>Please fill out the following fields to login:</p> <p>Please fill out the following fields to login:</p>
@ -14,9 +16,7 @@ use yii\helpers\Html;
<?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(); ?>
<div class="control-group"> <div class="form-actions">
<div class="controls"> <?php echo Html::submitButton('Login', null, null, array('class' => 'btn btn-primary')); ?>
<?php echo Html::submitButton('Login', null, null, array('class' => 'btn btn-primary')); ?>
</div>
</div> </div>
<?php $this->endWidget(); ?> <?php $this->endWidget(); ?>

2
build/build

@ -16,5 +16,5 @@ require(__DIR__ . '/../framework/yii.php');
$id = 'yiic-build'; $id = 'yiic-build';
$basePath = __DIR__; $basePath = __DIR__;
$application = new yii\console\Application($id, $basePath); $application = new yii\console\Application(array('id' => $id, 'basePath' => $basePath));
$application->run(); $application->run();

4
docs/api/db/ActiveRecord.md

@ -412,7 +412,7 @@ class Customer extends \yii\db\ActiveRecord
/** /**
* @param ActiveQuery $query * @param ActiveQuery $query
*/ */
public function active($query) public static function active($query)
{ {
$query->andWhere('status = 1'); $query->andWhere('status = 1');
} }
@ -435,7 +435,7 @@ class Customer extends \yii\db\ActiveRecord
* @param ActiveQuery $query * @param ActiveQuery $query
* @param integer $age * @param integer $age
*/ */
public function olderThan($query, $age = 30) public static function olderThan($query, $age = 30)
{ {
$query->andWhere('age > :age', array(':age' => $age)); $query->andWhere('age > :age', array(':age' => $age));
} }

0
docs/guide/active-record.md

0
docs/guide/application.md

0
docs/guide/authentication.md

0
docs/guide/authorization.md

63
docs/guide/bootstrap.md

@ -0,0 +1,63 @@
Bootstrap with Yii
==================
A ready-to-use Web application is distributed together with Yii. You may find
its source code under the `app` folder after you expand the Yii release file.
If you have installed Yii under a Web-accessible folder, you should be able to
access this application through the following URL:
~~~
http://localhost/yii/apps/bootstrap/index.php
~~~
As you can see, the application has four pages: the homepage, the about page,
the contact page and the login page. The contact page displays a contact
form that users can fill in to submit their inquiries to the webmaster,
and the login page allows users to be authenticated before accessing privileged contents.
The following diagram shows the directory structure of this application.
~~~
app/
index.php Web application entry script file
index-test.php entry script file for the functional tests
assets/ containing published resource files
css/ containing CSS files
img/ containing image files
themes/ containing application themes
protected/ containing protected application files
yiic yiic command line script for Unix/Linux
yiic.bat yiic command line script for Windows
yiic.php yiic command line PHP script
commands/ containing customized 'yiic' commands
components/ containing reusable user components
config/ containing configuration files
console.php the console application configuration
main.php the Web application configuration
controllers/ containing controller class files
SiteController.php the default controller class
data/ containing the sample database
schema.mysql.sql the DB schema for the sample MySQL database
schema.sqlite.sql the DB schema for the sample SQLite database
bootstrap.db the sample SQLite database file
vendor/ containing third-party extensions and libraries
messages/ containing translated messages
models/ containing model class files
User.php the User model
LoginForm.php the form model for 'login' action
ContactForm.php the form model for 'contact' action
runtime/ containing temporarily generated files
views/ containing controller view and layout files
layouts/ containing layout view files
main.php the base layout shared by all pages
site/ containing view files for the 'site' controller
about.php the view for the 'about' action
contact.php the view for the 'contact' action
index.php the view for the 'index' action
login.php the view for the 'login' action
~~~
TBD

3
docs/guide/caching.md

@ -0,0 +1,3 @@
Caching
=======

0
docs/guide/console.md

0
docs/guide/controller.md

0
docs/guide/dao.md

3
docs/guide/error.md

@ -0,0 +1,3 @@
Error Handling
==============

0
docs/guide/extension.md

0
docs/guide/form.md

0
docs/guide/gii.md

0
docs/guide/i18n.md

30
docs/guide/index.md

@ -0,0 +1,30 @@
* [Overview](overview.md)
* [Installation](installation.md)
* [Bootstrap with Yii](bootstrap.md)
* [MVC Overview](mvc.md)
* [Controller](controller.md)
* [Model](model.md)
* [View](view.md)
* [Application](application.md)
* [Form](form.md)
* [Data Validation](validation.md)
* [Database Access Objects](dao.md)
* [Query Builder](query-builder.md)
* [ActiveRecord](active-record.md)
* [Database Migration](migration.md)
* [Caching](caching.md)
* [Internationalization](i18n.md)
* [Extending Yii](extension.md)
* [Authentication](authentication.md)
* [Authorization](authorization.md)
* [Logging](logging.md)
* [URL Management](url.md)
* [Theming](theming.md)
* [Error Handling](error.md)
* [Template](template.md)
* [Console Application](console.md)
* [Security](security.md)
* [Performance Tuning](performance.md)
* [Testing](testing.md)
* [Automatic Code Generation](gii.md)
* [Upgrading from 1.1 to 2.0](upgrade-from-v1.md)

112
docs/guide/installation.md

@ -0,0 +1,112 @@
Installation
============
Installation of Yii mainly involves the following two steps:
1. Download Yii Framework from [yiiframework.com](http://www.yiiframework.com/).
2. Unpack the Yii release file to a Web-accessible directory.
> Tip: Yii does not need to be installed under a Web-accessible directory.
A Yii application has one entry script which is usually the only file that
needs to be exposed to Web users. Other PHP scripts, including those from
Yii, should be protected from Web access; otherwise they might be exploited
by hackers.
Requirements
------------
After installing Yii, you may want to verify that your server satisfies
Yii's requirements. You can do so by accessing the requirement checker
script via the following URL in a Web browser:
~~~
http://hostname/path/to/yii/requirements/index.php
~~~
Yii requires PHP 5.3, so the server must have PHP 5.3 or above installed and
available to the web server. Yii has been tested with [Apache HTTP server](http://httpd.apache.org/)
on Windows and Linux. It may also run on other Web servers and platforms,
provided PHP 5.3 is supported.
Recommended Apache Configuration
--------------------------------
Yii is ready to work with a default Apache web server configuration.
The `.htaccess` files in Yii framework and application folders deny
access to the restricted resources. To hide the bootstrap file (usually `index.php`)
in your URLs you can add `mod_rewrite` instructions to the `.htaccess` file
in your document root or to the virtual host configuration:
~~~
RewriteEngine on
# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# otherwise forward it to index.php
RewriteRule . index.php
~~~
Recommended Nginx Configuration
-------------------------------
You can use Yii with [Nginx](http://wiki.nginx.org/) and PHP with [FPM SAPI](http://php.net/install.fpm).
Here is a sample host configuration. It defines the bootstrap file and makes
Yii to catch all requests to nonexistent files, which allows us to have nice-looking URLs.
~~~
server {
set $host_path "/www/mysite";
access_log /www/mysite/log/access.log main;
server_name mysite;
root $host_path/htdocs;
set $yii_bootstrap "index.php";
charset utf-8;
location / {
index index.html $yii_bootstrap;
try_files $uri $uri/ /$yii_bootstrap?$args;
}
location ~ ^/(protected|framework|themes/\w+/views) {
deny all;
}
#avoid processing of calls to unexisting static files by yii
location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {
try_files $uri =404;
}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php {
fastcgi_split_path_info ^(.+\.php)(.*)$;
#let yii catch the calls to unexising PHP files
set $fsn /$yii_bootstrap;
if (-f $document_root$fastcgi_script_name){
set $fsn $fastcgi_script_name;
}
fastcgi_pass 127.0.0.1:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fsn;
#PATH_INFO and PATH_TRANSLATED can be omitted, but RFC 3875 specifies them for CGI
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fsn;
}
location ~ /\.ht {
deny all;
}
}
~~~
Using this configuration you can set `cgi.fix_pathinfo=0` in php.ini to avoid
many unnecessary system `stat()` calls.

0
docs/guide/logging.md

319
docs/guide/migration.md

@ -0,0 +1,319 @@
Database Migration
==================
Like source code, the structure of a database is evolving as we develop and maintain
a database-driven application. For example, during development, we may want to
add a new table; or after the application is put into production, we may realize
the need of adding an index on a column. It is important to keep track of these
structural database changes (called **migration**) like we do with our source
code. If the source code and the database are out of sync, it is very likely
the whole system may break. For this reason, Yii provides a database migration
tool that can keep track of database migration history, apply new migrations,
or revert existing ones.
The following steps show how we can use database migration during development:
1. Tim creates a new migration (e.g. create a new table)
2. Tim commits the new migration into source control system (e.g. GIT, Mercurial)
3. Doug updates from source control system and receives the new migration
4. Doug applies the migration to his local development database
Yii supports database migration via the `yiic migrate` command line tool. This
tool supports creating new migrations, applying/reverting/redoing migrations, and
showing migration history and new migrations.
Creating Migrations
-------------------
To create a new migration (e.g. create a news table), we run the following command:
~~~
yiic migrate/create <name>
~~~
The required `name` parameter specifies a very brief description of the migration
(e.g. `create_news_table`). As we will show in the following, the `name` parameter
is used as part of a PHP class name. Therefore, it should only contain letters,
digits and/or underscore characters.
~~~
yiic migrate/create create_news_table
~~~
The above command will create under the `protected/migrations` directory a new
file named `m101129_185401_create_news_table.php` which contains the following
initial code:
~~~
[php]
class m101129_185401_create_news_table extends \yii\db\Migration
{
public function up()
{
}
public function down()
{
echo "m101129_185401_create_news_table cannot be reverted.\n";
return false;
}
}
~~~
Notice that the class name is the same as the file name which is of the pattern
`m<timestamp>_<name>`, where `<timestamp>` refers to the UTC timestamp (in the
format of `yymmdd_hhmmss`) when the migration is created, and `<name>` is taken
from the command's `name` parameter.
The `up()` method should contain the code implementing the actual database
migration, while the `down()` method may contain the code reverting what is
done in `up()`.
Sometimes, it is impossible to implement `down()`. For example, if we delete
table rows in `up()`, we will not be able to recover them in `down()`. In this
case, the migration is called irreversible, meaning we cannot roll back to
a previous state of the database. In the above generated code, the `down()`
method returns `false` to indicate that the migration cannot be reverted.
As an example, let's show the migration about creating a news table.
~~~
[php]
class m101129_185401_create_news_table extends \yii\db\Migration
{
public function up()
{
$this->db->createCommand()->createTable('tbl_news, array(
'id' => 'pk',
'title' => 'string NOT NULL',
'content' => 'text',
))->execute();
}
public function down()
{
$this->db->createCommand()->dropTable('tbl_news')->execute();
}
}
~~~
The base class [\yii\db\Migration] exposes a database connection via `db`
property. You can use it for manipulating data and schema of a database.
Transactional Migrations
------------------------
While performing complex DB migrations, we usually want to make sure that each
migration succeed or fail as a whole so that the database maintains the
consistency and integrity. In order to achieve this goal, we can exploit
DB transactions.
We could explicitly start a DB transaction and enclose the rest of the DB-related
code within the transaction, like the following:
~~~
[php]
class m101129_185401_create_news_table extends \yii\db\Migration
{
public function up()
{
$transaction=$this->getDbConnection()->beginTransaction();
try
{
$this->db->createCommand()->createTable('tbl_news, array(
'id' => 'pk',
'title' => 'string NOT NULL',
'content' => 'text',
))->execute();
$transaction->commit();
}
catch(Exception $e)
{
echo "Exception: ".$e->getMessage()."\n";
$transaction->rollback();
return false;
}
}
// ...similar code for down()
}
~~~
> Note: Not all DBMS support transactions. And some DB queries cannot be put
> into a transaction. In this case, you will have to implement `up()` and
> `down()`, instead. And for MySQL, some SQL statements may cause
> [implicit commit](http://dev.mysql.com/doc/refman/5.1/en/implicit-commit.html).
Applying Migrations
-------------------
To apply all available new migrations (i.e., make the local database up-to-date),
run the following command:
~~~
yiic migrate
~~~
The command will show the list of all new migrations. If you confirm to apply
the migrations, it will run the `up()` method in every new migration class, one
after another, in the order of the timestamp value in the class name.
After applying a migration, the migration tool will keep a record in a database
table named `tbl_migration`. This allows the tool to identify which migrations
have been applied and which are not. If the `tbl_migration` table does not exist,
the tool will automatically create it in the database specified by the `db`
application component.
Sometimes, we may only want to apply one or a few new migrations. We can use the
following command:
~~~
yiic migrate/up 3
~~~
This command will apply the 3 new migrations. Changing the value 3 will allow
us to change the number of migrations to be applied.
We can also migrate the database to a specific version with the following command:
~~~
yiic migrate/to 101129_185401
~~~
That is, we use the timestamp part of a migration name to specify the version
that we want to migrate the database to. If there are multiple migrations between
the last applied migration and the specified migration, all these migrations
will be applied. If the specified migration has been applied before, then all
migrations applied after it will be reverted (to be described in the next section).
Reverting Migrations
--------------------
To revert the last one or several applied migrations, we can use the following
command:
~~~
yiic migrate/down [step]
~~~
where the optional `step` parameter specifies how many migrations to be reverted
back. It defaults to 1, meaning reverting back the last applied migration.
As we described before, not all migrations can be reverted. Trying to revert
such migrations will throw an exception and stop the whole reverting process.
Redoing Migrations
------------------
Redoing migrations means first reverting and then applying the specified migrations.
This can be done with the following command:
~~~
yiic migrate/redo [step]
~~~
where the optional `step` parameter specifies how many migrations to be redone.
It defaults to 1, meaning redoing the last migration.
Showing Migration Information
-----------------------------
Besides applying and reverting migrations, the migration tool can also display
the migration history and the new migrations to be applied.
~~~
yiic migrate/history [limit]
yiic migrate/new [limit]
~~~
where the optional parameter `limit` specifies the number of migrations to be
displayed. If `limit` is not specified, all available migrations will be displayed.
The first command shows the migrations that have been applied, while the second
command shows the migrations that have not been applied.
Modifying Migration History
---------------------------
Sometimes, we may want to modify the migration history to a specific migration
version without actually applying or reverting the relevant migrations. This
often happens when developing a new migration. We can use the following command
to achieve this goal.
~~~
yiic migrate/mark 101129_185401
~~~
This command is very similar to `yiic migrate/to` command, except that it only
modifies the migration history table to the specified version without applying
or reverting the migrations.
Customizing Migration Command
-----------------------------
There are several ways to customize the migration command.
### Use Command Line Options
The migration command comes with four options that can be specified in command
line:
* `interactive`: boolean, specifies whether to perform migrations in an
interactive mode. Defaults to true, meaning the user will be prompted when
performing a specific migration. You may set this to false should the
migrations be done in a background process.
* `migrationPath`: string, specifies the directory storing all migration class
files. This must be specified in terms of a path alias, and the corresponding
directory must exist. If not specified, it will use the `migrations`
sub-directory under the application base path.
* `migrationTable`: string, specifies the name of the database table for storing
migration history information. It defaults to `tbl_migration`. The table
structure is `version varchar(255) primary key, apply_time integer`.
* `connectionID`: string, specifies the ID of the database application component.
Defaults to 'db'.
* `templateFile`: string, specifies the path of the file to be served as the code
template for generating the migration classes. This must be specified in terms
of a path alias (e.g. `application.migrations.template`). If not set, an
internal template will be used. Inside the template, the token `{ClassName}`
will be replaced with the actual migration class name.
To specify these options, execute the migrate command using the following format
~~~
yiic migrate/up --option1=value1 --option2=value2 ...
~~~
For example, if we want to migrate for a `forum` module whose migration files
are located within the module's `migrations` directory, we can use the following
command:
~~~
yiic migrate/up --migrationPath=ext.forum.migrations
~~~
### Configure Command Globally
While command line options allow us to configure the migration command
on-the-fly, sometimes we may want to configure the command once for all.
For example, we may want to use a different table to store the migration history,
or we may want to use a customized migration template. We can do so by modifying
the console application's configuration file like the following,
```php
TBD
```
Now if we run the `migrate` command, the above configurations will take effect
without requiring us to enter the command line options every time.

0
docs/guide/model.md

52
docs/guide/mvc.md

@ -0,0 +1,52 @@
MVC Overview
============
Yii implements the model-view-controller (MVC) design pattern, which is
widely adopted in Web programming. MVC aims to separate business logic from
user interface considerations, so that developers can more easily change
each part without affecting the other. In MVC, the model represents the
information (the data) and the business rules; the view contains elements
of the user interface such as text, form inputs; and the controller manages
the communication between the model and the view.
Besides implementing MVC, Yii also introduces a front-controller, called
`Application`, which encapsulates the execution context for the processing
of a request. Application collects information about a user request and
then dispatches it to an appropriate controller for further handling.
The following diagram shows the static structure of a Yii application:
![Static structure of Yii application](structure.png)
A Typical Workflow
------------------
The following diagram shows a typical workflow of a Yii application when
it is handling a user request:
![Typical workflow of a Yii application](flow.png)
1. A user makes a request with the URL `http://www.example.com/index.php?r=post/show&id=1`
and the Web server handles the request by executing the bootstrap script `index.php`.
2. The bootstrap script creates an [Application](/doc/guide/basics.application)
instance and runs it.
3. The Application obtains detailed user request information from
an [application component](/doc/guide/basics.application#application-component)
named `request`.
4. The application determines the requested [controller](/doc/guide/basics.controller)
and [action](/doc/guide/basics.controller#action) with the help
of an application component named `urlManager`. For this example, the controller
is `post`, which refers to the `PostController` class; and the action is `show`,
whose actual meaning is determined by the controller.
5. The application creates an instance of the requested controller
to further handle the user request. The controller determines that the action
`show` refers to a method named `actionShow` in the controller class. It then
creates and executes filters (e.g. access control, benchmarking) associated
with this action. The action is executed if it is allowed by the filters.
6. The action reads a `Post` [model](/doc/guide/basics.model) whose ID is `1` from the database.
7. The action renders a [view](/doc/guide/basics.view) named `show` with the `Post` model.
8. The view reads and displays the attributes of the `Post` model.
9. The view executes some [widgets](/doc/guide/basics.view#widget).
10. The view rendering result is embedded in a [layout](/doc/guide/basics.view#layout).
11. The action completes the view rendering and displays the result to the user.

36
docs/guide/overview.md

@ -0,0 +1,36 @@
What is Yii
===========
Yii is a high-performance, component-based PHP framework for developing
large-scale Web applications rapidly. It enables maximum reusability in Web
programming and can significantly accelerate your Web application development
process. The name Yii (pronounced `Yee` or `[ji:]`) is an acronym for
"**Yes It Is!**".
Requirements
------------
To run a Yii-powered Web application, you need a Web server that supports
PHP 5.3.?.
For developers who want to use Yii, understanding object-oriented
programming (OOP) is very helpful, because Yii is a pure OOP framework.
What is Yii Best for?
---------------------
Yii is a generic Web programming framework that can be used for developing
virtually any type of Web application. Because it is light-weight and
equipped with sophisticated caching mechanisms, it is especially suited
to high-traffic applications, such as portals, forums, content
management systems (CMS), e-commerce systems, etc.
How does Yii Compare with Other Frameworks?
-------------------------------------------
Like most PHP frameworks, Yii is an MVC (Model-View-Controller) framework.
TBD

181
docs/guide/performance.md

@ -0,0 +1,181 @@
Performance Tuning
==================
Application performance consists of two parts. First is the framework performance
and the second is the application itself. Yii has a pretty low performance impact
on your application out of the box and can be fine-tuned further for production
environment. As for the application, we'll provide some of the best practices
along with examples on how to apply them to Yii.
Preparing framework for production
----------------------------------
### Disabling Debug Mode
First thing you should do before deploying your application to production environment
is to disable debug mode. A Yii application runs in debug mode if the constant
`YII_DEBUG` is defined as `true` in `index.php` so to disable debug the following
should be in your `index.php`:
```php
defined('YII_DEBUG') or define('YII_DEBUG', false);
```
Debug mode is very useful during development stage, but it would impact performance
because some components cause extra burden in debug mode. For example, the message
logger may record additional debug information for every message being logged.
### Enabling PHP opcode cache
Enabling the PHP opcode cache improves any PHP application performance and lowers
memory usage significantly. Yii is no exception. It was tested with
[APC PHP extension](http://php.net/manual/en/book.apc.php) that caches
and optimizes PHP intermediate code and avoids the time spent in parsing PHP
scripts for every incoming request.
### Turning on ActiveRecord database schema caching
If the application is using Active Record, we should turn on the schema caching
to save the time of parsing database schema. This can be done by setting the
`Connection::enableSchemaCache` property to be `true` via application configuration
`protected/config/main.php`:
```php
return array(
// ...
'components' => array(
// ...
'db' => array(
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=mydatabase',
'username' => 'root',
'password' => '',
'enableSchemaCache' => true,
// Duration of schema cache.
// 'schemaCacheDuration' => 3600,
// Name of the cache component used. Default is 'cache'.
//'schemaCache' => 'cache',
),
'cache' => array(
'class' => 'yii\caching\FileCache',
),
),
);
```
Note that `cache` application component should be configured.
### Combining and Minimizing Assets
TBD
### Using better storage for sessions
By default PHP uses files to handle sessions. It is OK for development and
small projects but when it comes to handling concurrent requests it's better to
switch to another storage such as database. You can do so by configuring your
application via `protected/config/main.php`:
```php
return array(
// ...
'components' => array(
'session' => array(
'class' => 'yii\web\DbSession',
// Set the following if want to use DB component other than
// default 'db'.
// 'db' => 'mydb',
// To override default session table set the following
// 'sessionTable' => 'my_session',
),
),
);
```
You can use `CacheSession` to store sessions using cache. Note that some
cache storages such as memcached has no guaranteee that session data will not
be lost leading to unexpected logouts.
Improving application
---------------------
### Using Serverside Caching Techniques
As described in the Caching section, Yii provides several caching solutions that
may improve the performance of a Web application significantly. If the generation
of some data takes long time, we can use the data caching approach to reduce the
data generation frequency; If a portion of page remains relatively static, we
can use the fragment caching approach to reduce its rendering frequency;
If a whole page remains relative static, we can use the page caching approach to
save the rendering cost for the whole page.
### Leveraging HTTP to save procesing time and bandwidth
TBD
### Database Optimization
Fetching data from database is often the main performance bottleneck in
a Web application. Although using caching may alleviate the performance hit,
it does not fully solve the problem. When the database contains enormous data
and the cached data is invalid, fetching the latest data could be prohibitively
expensive without proper database and query design.
Design index wisely in a database. Indexing can make SELECT queries much faster,
but it may slow down INSERT, UPDATE or DELETE queries.
For complex queries, it is recommended to create a database view for it instead
of issuing the queries inside the PHP code and asking DBMS to parse them repetitively.
Do not overuse Active Record. Although Active Record is good at modelling data
in an OOP fashion, it actually degrades performance due to the fact that it needs
to create one or several objects to represent each row of query result. For data
intensive applications, using DAO or database APIs at lower level could be
a better choice.
Last but not least, use LIMIT in your SELECT queries. This avoids fetching
overwhelming data from database and exhausting the memory allocated to PHP.
### Using asArray
A good way to save memory and processing time on read-only pages is to use
ActiveRecord's `asArray` method.
```php
class PostController extends Controller
{
public function actionIndex()
{
$posts = Post::find()->orderBy('id DESC')->limit(100)->asArray()->all();
echo $this->render('index', array(
'posts' => $posts,
));
}
}
```
In the view you should access fields of each invidual record from `$posts` as array:
```php
foreach($posts as $post) {
echo $post['title']."<br>";
}
```
Note that you can use array notation even if `asArray` wasn't specified and you're
working with AR objects.
### Processing data in background
In order to respond to user requests faster you can process heavy parts of the
request later if there's no need for immediate response.
- Cron jobs + console.
- queues + handlers.
TBD

0
docs/guide/query-builder.md

0
docs/guide/security.md

3
docs/guide/template.md

@ -0,0 +1,3 @@
Template
========

0
docs/guide/testing.md

0
docs/guide/theming.md

8
docs/guide/title.md

@ -0,0 +1,8 @@
The Definitive Guide to Yii 2.0
===============================
This tutorial is released under [the Terms of Yii Documentation](http://www.yiiframework.com/doc/terms/).
All Rights Reserved.
2008 (c) Yii Software LLC.

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

@ -0,0 +1,437 @@
Upgrading from Yii 1.1
======================
In this chapter, we list the major changes introduced in Yii 2.0 since version 1.1.
We hope this list will make it easier for you to upgrade from Yii 1.1 and quickly
master Yii 2.0 based on your existing Yii knowledge.
Component and Object
--------------------
Yii 2.0 breaks the `CComponent` class in 1.1 into two classes: `Object` and `Component`.
The `Object` class is a lightweight base class that allows defining class properties
via getters and setters. The `Component` class extends from `Object` and supports
the event feature and the behavior feature.
If your class does not need the event or behavior feature, you should consider using
`Object` as the based class. This is usually the case for classes that represent basic
data structures.
Object Configuration
--------------------
The `Object` class introduces a uniform way of configuring objects. Any descendant class
of `Object` should declare its constructor (if needed) in the following way so that
it can be properly configured:
~~~
class MyClass extends \yii\Object
{
public function __construct($param1, $param2, $config = array())
{
// ... initialization before configuration is applied
parent::__construct($config);
}
public function init()
{
parent::init();
// ... initialization after configuration is applied
}
}
~~~
In the above, the last parameter of the constructor must take a configuration array
which contains name-value pairs for initializing the properties at the end of the constructor.
You can override the `init()` method to do initialization work that should be done after
the configuration is applied.
By following this convention, you will be able to create and configure a new object
using a configuration array like the following:
~~~
$object = Yii::createObject(array(
'class' => 'MyClass',
'property1' => 'abc',
'property2' => 'cde',
), $param1, $param2);
~~~
Events
------
There is no longer the need to define an `on`-method in order to define an event in Yii 2.0.
Instead, you can use whatever event names. To attach a handler to an event, you should
use the `on` method now:
~~~
$component->on($eventName, $handler);
// To detach the handler, use:
// $component->off($eventName, $handler);
~~~
When you attach a handler, you can now associate it with some parameters which can be later
accessed via the event parameter by the handler:
~~~
$component->on($eventName, $handler, $params);
~~~
Because of this change, you can now use "global" events. Simply trigger and attach handlers to
an event of the application instance:
~~~
Yii::$app->on($eventName, $handler);
....
// this will trigger the event and cause $handler to be invoked.
Yii::$app->trigger($eventName);
~~~
Path Alias
----------
Yii 2.0 expands the usage of path aliases to both file/directory paths and URLs. An alias
must start with a `@` character so that it can be differentiated from file/directory paths and URLs.
For example, the alias `@yii` refers to the Yii installation directory. Path aliases are
supported in most places in the Yii core code. For example, `FileCache::cachePath` can take
both a path alias and a normal directory path.
Path alias is also closely related with class namespaces. It is recommended that a path
alias defined for each root namespace so that you can use Yii class autoloader without
any further configuration. For example, because `@yii` refers to the Yii installation directory,
a class like `yii\web\Request` can be autoloaded by Yii. If you use a third party library
such as Zend Framework, you may define a path alias `@Zend` which refers to its installation directory.
And Yii will be able to autoload any class in this library.
View
----
Yii 2.0 introduces a `View` class to represent the view part in the MVC pattern.
It can be configured globally through the "view" application component. It is also
accessible in any view file via `$this`. This is one of the biggest changes compared to 1.1:
**`$this` in a view file no longer refers to the controller or widget object.**
It refers to the view object that is used to render the view file. To access the controller
or the widget object, you have to use `$this->context` now.
Because you can access the view object through the "view" application component,
you can now render a view file like the following anywhere in your code, not necessarily
in controllers or widgets:
~~~
$content = Yii::$app->view->renderFile($viewFile, $params);
// You can also explicitly create a new View instance to do the rendering
// $view = new View;
// $view->renderFile($viewFile, $params);
~~~
Also, there is no more `CClientScript` in Yii 2.0. The `View` class has taken over its role
with significant improvements. For more details, please see the "assets" subsection.
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
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
`View::renderers` property to use other template engines.
Models
------
A model is now associated with a form name returned its `formName()` method. This is
mainly used when using HTML forms to collect user inputs for a model. Previously in 1.1,
this is usually hardcoded as the class name of the model.
Yii 2.0 introduces a new method called `scenarios()` to declare which attributes require
validation under which scenario. Child classes should overwrite `scenarios()` to return
a list of scenarios and the corresponding attributes that need to be validated when
`validate()` is called. For example,
~~~
public function scenarios()
{
return array(
'backend' => array('email', 'role'),
'frontend' => array('email', '!name'),
);
}
~~~
This method also determines which attributes are safe and which are not. In particular,
given a scenario, if an attribute appears in the corresponding attribute list in `scenarios()`
and the name is not prefixed with `!`, it is considered *safe*.
Because of the above change, Yii 2.0 no longer has "safe" and "unsafe" validators.
If your model only has one scenario (very common), you do not have to overwrite `scenarios()`,
and everything will still work like the 1.1 way.
Controllers
-----------
The `render()` and `renderPartial()` methods now return the rendering results instead of directly
sending them out. You have to `echo` them explicitly, e.g., `echo $this->render(...);`.
A new method called `populate()` is introduced to simplify the data population from user inputs
to a model. For example,
~~~
$post = new Post;
if ($this->populate($_POST, $model)) {...}
// which is equivalent to:
if (isset($_POST['Post'])) {
$post->attributes = $_POST['Post'];
}
~~~
Themes
------
Theme works completely different in 2.0. It is now based on a path map to "translate" a source
view into a themed view. For example, if the path map for a theme is
`array('/www/views' => '/www/themes/basic')`, then the themed version for a view file
`/www/views/site/index.php` will be `/www/themes/basic/site/index.php`.
For this reason, theme can now be applied to any view file, even if a view rendered outside
of the context of a controller or a widget.
There is no more `CThemeManager`. Instead, `theme` is a configurable property of the "view"
application component.
Console Applications
--------------------
Console applications are now composed by controllers, too, like Web applications. In fact,
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
actions. You use the `yiic <route>` command to execute a console command, where `<route>`
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 treated as global options declared in `globalOptions()`.
Yii 2.0 supports automatic generation of command help information from comment blocks.
I18N
----
Yii 2.0 removes date formatter and number formatter in favor of the PECL intl PHP module.
Message translation is still supported, but managed via the "i18n" application component.
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`.
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 are implemented via behaviors now. You should extend from `ActionFilter` to
define a new filter. To use a filter, you should attach the filter class to the controller
as a behavior. For example, to use the `AccessControl` filter, you should have the following
code in a controller:
~~~
public function behaviors()
{
return array(
'access' => array(
'class' => 'yii\web\AccessControl',
'rules' => array(
array('allow' => true, 'actions' => array('admin'), 'roles' => array('@')),
array('allow' => false),
),
),
);
}
~~~
Assets
------
Yii 2.0 introduces a new concept called *asset bundle*. It is a bit similar to script
packages (managed by `CClientScript`) in 1.1, but with better support.
An asset bundle is a collection of asset files (e.g. JavaScript files, CSS files, image files, etc.)
under a directory. By registering an asset bundle via `View::registerAssetBundle()`, you
will be able to make the assets in that bundle accessible via Web, and the current page
will automatically contain references to the JavaScript and CSS files in that bundle.
Static Helpers
--------------
Yii 2.0 introduces many commonly used static helper classes, such as `Html`, `ArrayHelper`,
`StringHelper`. These classes are designed to be easily extended. Note that static classes
are usually hard to be extended because of the fixed class name references. But Yii 2.0
introduces the class map (via `Yii::$classMap`) to overcome this difficulty.
`ActiveForm`
------------
Yii 2.0 introduces the *field* concept for building a form using `ActiveForm`. A field
is a container consisting of a label, an input, and an error message. It is represented
as an `ActiveField` object. Using fields, you can build a form more cleanly than before:
~~~
<?php $form = $this->beginWidget('yii\widgets\ActiveForm'); ?>
<?php echo $form->field($model, 'username')->textInput(); ?>
<?php echo $form->field($model, 'password')->passwordInput(); ?>
<div class="form-actions">
<?php echo Html::submitButton('Login'); ?>
</div>
<?php $this->endWidget(); ?>
~~~
Query Builder
-------------
In 1.1, query building is scattered among several classes, including `CDbCommand`,
`CDbCriteria`, and `CDbCommandBuilder`. Yii 2.0 uses `Query` to represent a DB query
and `QueryBuilder` to generate SQL statements from query objects. For example,
~~~
$query = new \yii\db\Query;
$query->select('id, name')
->from('tbl_user')
->limit(10);
$command = $query->createCommand();
$sql = $command->sql;
$rows = $command->queryAll();
~~~
Best of all, such query building methods can be used together with `ActiveRecord`,
as explained in the next sub-section.
ActiveRecord
------------
ActiveRecord has undergone significant changes in Yii 2.0. The most important one
is about relational ActiveRecord query. In 1.1, you have to declare the relations
in the `relations()` method. In 2.0, this is done via getter methods that return
an `ActiveQuery` object. For example, the following method declares an "orders" relation:
~~~
class Customer extends \yii\db\ActiveRecord
{
public function getOrders()
{
return $this->hasMany('Order', array('customer_id' => 'id'));
}
}
~~~
You can use `$customer->orders` to access the customer's orders. You can also
use `$customer->getOrders()->andWhere('status=1')->all()` to perform on-the-fly
relational query with customized query conditions.
When loading relational records in an eager way, Yii 2.0 does it differently from 1.1.
In particular, in 1.1 a JOIN query would be used to bring both the primary and the relational
records; while in 2.0, two SQL statements are executed without using JOIN: the first
statement brings back the primary records and the second brings back the relational records
by filtering with the primary keys of the primary records.
Yii 2.0 no longer uses the `model()` method when performing queries. Instead, you
use the `find()` method like the following:
~~~
// to retrieve all *active* customers and order them by their ID:
$customers = Customer::find()
->where(array('status' => $active))
->orderBy('id')
->all();
// return the customer whose PK is 1
$customer = Customer::find(1);
~~~
The `find()` method returns an instance of `ActiveQuery` which is a subclass of `Query`.
Therefore, you can use all query methods of `Query`.
Instead of returning ActiveRecord objects, you may call `ActiveQuery::asArray()` to
return results in terms of arrays. This is more efficient and is especially useful
when you need to return large number of records. For example,
~~~
$customers = Customer::find()->asArray()->all();
~~~
By default, ActiveRecord now only saves dirty attributes. In 1.1, all attributes
would be saved to database when you call `save()`, regardless they are changed or not,
unless you explicitly list the attributes to save.
Auto-quoting Table and Column Names
------------------------------------
Yii 2.0 supports automatic quoting of database table and column names. A name enclosed
within double curly brackets is treated as a table name, and a name enclosed within
double square brackets is treated as a column name. They will be quoted according to
the database driver being used. For example,
~~~
$command = $connection->createCommand('SELECT [[id]] FROM {{posts}}');
echo $command->sql; // MySQL: SELECT `id` FROM `posts`
~~~
This feature is especially useful if you are developing an application that supports
different DBMS.
User and Identity
-----------------
The `CWebUser` class in 1.1 is now replaced by `\yii\Web\User`, and there is no more
`CUserIdentity` class. Instead, you should implement the `Identity` interface which
is much more straightforward to implement. The bootstrap application provides such an example.
URL Management
--------------
URL management is similar to 1.1. A major enhancement is that it now supports optional
parameters. For example, if you have rule declared as follows, then it will match
both `post/popular` and `post/1/popular`. In 1.1, you would have to use two rules to achieve
the same goal.
~~~
array(
'pattern' => 'post/<page:\d+>/<tag>',
'route' => 'post/index',
'defaults' => array('page' => 1),
)
~~~
Response
--------
Extensions
----------
Integration with Composer
-------------------------
TBD

0
docs/guide/upgrade.md

3
docs/guide/url.md

@ -0,0 +1,3 @@
URL Management
==============

0
docs/guide/validation.md

0
docs/guide/view.md

34
docs/view_renderers.md

@ -18,6 +18,7 @@ array(
), ),
'twig' => array( 'twig' => array(
'class' => 'yii\renderers\TwigViewRenderer', 'class' => 'yii\renderers\TwigViewRenderer',
'twigPath' => '@app/vendors/Twig',
), ),
// ... // ...
), ),
@ -26,6 +27,9 @@ array(
) )
``` ```
Note that Smarty and Twig are not bundled with Yii and you have to download and
unpack these yourself and then specify `twigPath` and `smartyPath` respectively.
Twig Twig
---- ----
@ -38,6 +42,21 @@ or `$this->renderPartial()` from your controller:
echo $this->render('renderer.twig', array('username' => 'Alex')); echo $this->render('renderer.twig', array('username' => 'Alex'));
``` ```
### Additional functions
Additionally to regular Twig syntax the following is available in Yii:
```php
<a href="{{ path('blog/view', {'alias' : post.alias}) }}">{{ post.title }}</a>
```
path function calls `Html::url()` internally.
### Additional variables
- `app` = `\Yii::$app`
- `this` = current `View` object
Smarty Smarty
------ ------
@ -49,3 +68,18 @@ or `$this->renderPartial()` from your controller:
```php ```php
echo $this->render('renderer.tpl', array('username' => 'Alex')); echo $this->render('renderer.tpl', array('username' => 'Alex'));
``` ```
### Additional functions
Additionally to regular Smarty syntax the following is available in Yii:
```php
<a href="{path route='blog/view' alias=$post.alias}">{$post.title}</a>
```
path function calls `Html::url()` internally.
### Additional variables
- `$app` = `\Yii::$app`
- `$this` = current `View` object

1
framework/.htaccess

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

9
framework/YiiBase.php

@ -600,6 +600,13 @@ class YiiBase
*/ */
public static function t($message, $params = array(), $language = null) public static function t($message, $params = array(), $language = null)
{ {
return self::$app->getI18N()->translate($message, $params, $language); if (self::$app !== null) {
return self::$app->getI18N()->translate($message, $params, $language);
} else {
if (strpos($message, '|') !== false && preg_match('/^([\w\-\\/\.\\\\]+)\|(.*)/', $message, $matches)) {
$message = $matches[2];
}
return is_array($params) ? strtr($message, $params) : $message;
}
} }
} }

2
framework/assets.php

@ -28,4 +28,4 @@ return array(
), ),
'depends' => array('yii', 'yii/validation'), 'depends' => array('yii', 'yii/validation'),
), ),
); );

580
framework/assets/yii.activeForm.js

@ -23,38 +23,54 @@
}; };
var defaults = { var defaults = {
// whether it is waiting for ajax submission result // the jQuery selector for the error summary
submitting: false errorSummary: undefined,
// whether to perform validation before submitting the form.
validateOnSubmit: true,
// the container CSS class representing the corresponding attribute has validation error
errorCssClass: 'error',
// the container CSS class representing the corresponding attribute passes validation
successCssClass: 'success',
// the container CSS class representing the corresponding attribute is being validated
validatingCssClass: 'validating',
// the URL for performing AJAX-based validation. If not set, it will use the the form's action
validationUrl: undefined,
// a callback that is called before submitting the form. The signature of the callback should be:
// function ($form) { ...return false to cancel submission...}
beforeSubmit: undefined,
// a callback that is called before validating each attribute. The signature of the callback should be:
// function ($form, attribute, messages) { ...return false to cancel the validation...}
beforeValidate: undefined,
// the GET parameter name indicating an AJAX-based validation
ajaxVar: 'ajax'
};
var attributeDefaults = {
// attribute name or expression (e.g. "[0]content" for tabular input)
name: undefined,
// the jQuery selector of the container of the input field
container: undefined,
// the jQuery selector of the input field
input: undefined,
// the jQuery selector of the error tag
error: undefined,
// whether to perform validation when a change is detected on the input
validateOnChange: false,
// whether to perform validation when the user is typing.
validateOnType: false,
// number of milliseconds that the validation should be delayed when a user is typing in the input field.
validationDelay: 200,
// whether to enable AJAX-based validation.
enableAjaxValidation: false,
// function (attribute, value, messages), the client-side validation function.
validate: undefined,
// status of the input field, 0: empty, not entered before, 1: validated, 2: pending validation, 3: validating
status: 0,
// the value of the input
value: undefined
}; };
var methods = { var methods = {
/**
* Initializes the plugin.
* @param attributes array attribute configurations. Each attribute may contain the following options:
*
* - id: 'ModelClass_attribute', // the unique attribute ID
* - model: 'ModelClass', // the model class name
* - name: 'name', // attribute name
* - inputID: 'input-tag-id',
* - errorID: 'error-tag-id',
* - value: undefined,
* - status: 0, // 0: empty, not entered before, 1: validated, 2: pending validation, 3: validating
* - validationDelay: 200,
* - validateOnChange: true,
* - validateOnType: false,
* - hideErrorMessage: false,
* - inputContainer: undefined,
* - errorCssClass: 'error',
* - successCssClass: 'success',
* - validatingCssClass: 'validating',
* - enableAjaxValidation: true,
* - enableClientValidation: true,
* - clientValidation: undefined, // function (value, messages, attribute) | client-side validation
* - beforeValidateAttribute: undefined, // function (form, attribute) | boolean
* - afterValidateAttribute: undefined, // function (form, attribute, data, hasError)
*
* @param options object the configuration for the plugin. The following options can be set:
*/
init: function (attributes, options) { init: function (attributes, options) {
return this.each(function () { return this.each(function () {
var $form = $(this); var $form = $(this);
@ -62,64 +78,33 @@
return; return;
} }
var settings = $.extend(defaults, options || {}); var settings = $.extend({}, defaults, options || {});
if (settings.validationUrl === undefined) { if (settings.validationUrl === undefined) {
settings.validationUrl = $form.attr('action'); settings.validationUrl = $form.attr('action');
} }
$.each(attributes, function (i) { $.each(attributes, function (i) {
this.value = getInputValue($form.find('#' + this.inputID)); attributes[i] = $.extend({value: getValue($form, this)}, attributeDefaults, this);
attributes[i] = $.extend(settings, this);
}); });
$form.data('yiiActiveForm', { $form.data('yiiActiveForm', {
settings: settings, settings: settings,
attributes: attributes attributes: attributes,
submitting: false,
validated: false
}); });
bindAttributes(attributes); watchAttributes($form, attributes);
$form.bind('reset', resetForm); /**
* Clean up error status when the form is reset.
* Note that $form.on('reset', ...) does work because the "reset" event does not bubble on IE.
*/
$form.bind('reset.yiiActiveForm', methods.resetForm);
if (settings.validateOnSubmit) { if (settings.validateOnSubmit) {
$form.on('mouseup keyup', ':submit', function () { $form.on('mouseup.yiiActiveForm keyup.yiiActiveForm', ':submit', function () {
$form.data('submitObject', $(this)); $form.data('yiiActiveForm').submitObject = $(this);
});
var validated = false;
$form.submit(function () {
if (validated) {
validated = false;
return true;
}
if (settings.timer !== undefined) {
clearTimeout(settings.timer);
}
settings.submitting = true;
if (settings.beforeValidate === undefined || settings.beforeValidate($form)) {
$.fn.yiiactiveform.validate($form, function (data) {
var hasError = false;
$.each(settings.attributes, function () {
hasError = $.fn.yiiactiveform.updateInput(this, data, $form) || hasError;
});
$.fn.yiiactiveform.updateSummary($form, data);
if (settings.afterValidate === undefined || settings.afterValidate($form, data, hasError)) {
if (!hasError) {
validated = true;
var $button = $form.data('submitObject') || $form.find(':submit:first');
// TODO: if the submission is caused by "change" event, it will not work
if ($button.length) {
$button.click();
} else { // no submit button in the form
$form.submit();
}
return;
}
}
settings.submitting = false;
});
} else {
settings.submitting = false;
}
return false;
}); });
$form.on('submit', methods.submitForm);
} }
}); });
}, },
@ -128,134 +113,109 @@
return this.each(function () { return this.each(function () {
$(window).unbind('.yiiActiveForm'); $(window).unbind('.yiiActiveForm');
$(this).removeData('yiiActiveForm'); $(this).removeData('yiiActiveForm');
})
}
};
/**
* Returns the value of the specified input element.
* This method will perform additional checks to get proper values
* for checkbox, radio, checkbox list and radio list.
* @param $e jQuery the jQuery object of the input element
* @return string the input value
*/
var getInputValue = function ($e) {
var type,
c = [];
if (!$e.length) {
return undefined;
}
if ($e[0].tagName.toLowerCase() === 'div') {
$e.find(':checked').each(function () {
c.push(this.value);
}); });
return c.join(','); },
}
type = $e.attr('type');
if (type === 'checkbox' || type === 'radio') {
return $e.filter(':checked').val();
} else {
return $e.val();
}
};
var bindAttributes = function (attributes) { options: function() {
$.each(attributes, function (i, attribute) { return this.data('yiiActiveForm').settings;
if (this.validateOnChange) { },
$form.find('#' + this.inputID).change(function () {
validateAttribute(attribute, false);
}).blur(function () {
if (attribute.status !== 2 && attribute.status !== 3) {
validateAttribute(attribute, !attribute.status);
}
});
}
if (this.validateOnType) {
$form.find('#' + this.inputID).keyup(function () {
if (attribute.value !== getAFValue($(this))) {
validateAttribute(attribute, false);
}
});
}
});
};
/** submitForm: function () {
* Performs the ajax validation request. var $form = $(this),
* This method is invoked internally to trigger the ajax validation. data = $form.data('yiiActiveForm');
* @param form jquery the jquery representation of the form if (data.validated) {
* @param successCallback function the function to be invoked if the ajax request succeeds // continue submitting the form since validation passes
* @param errorCallback function the function to be invoked if the ajax request fails data.validated = false;
*/ return true;
var validateForm = function (form, successCallback, errorCallback) {
var $form = $(form),
settings = $form.data('settings'),
needAjaxValidation = false,
messages = {};
$.each(settings.attributes, function () {
var value,
msg = [];
if (this.clientValidation !== undefined && (settings.submitting || this.status === 2 || this.status === 3)) {
value = getInputValue($form.find('#' + this.inputID));
this.clientValidation(value, msg, this);
if (msg.length) {
messages[this.id] = msg;
}
} }
if (this.enableAjaxValidation && !msg.length && (settings.submitting || this.status === 2 || this.status === 3)) {
needAjaxValidation = true;
}
});
if (!needAjaxValidation || settings.submitting && !$.isEmptyObject(messages)) { if (data.settings.timer !== undefined) {
if (settings.submitting) { clearTimeout(data.settings.timer);
// delay callback so that the form can be submitted without problem }
setTimeout(function () { data.submitting = true;
successCallback(messages); if (!data.settings.beforeSubmit || data.settings.beforeSubmit($form)) {
}, 200); validate($form, function (messages) {
var hasError = false;
$.each(data.attributes, function () {
hasError = updateInput($form, this, messages) || hasError;
});
updateSummary($form, messages);
if (!hasError) {
data.validated = true;
var $button = data.submitObject || $form.find(':submit:first');
// TODO: if the submission is caused by "change" event, it will not work
if ($button.length) {
$button.click();
} else {
// no submit button in the form
$form.submit();
}
return;
}
data.submitting = false;
}, function () {
data.submitting = false;
});
} else { } else {
successCallback(messages); data.submitting = false;
} }
return; return false;
} },
var $button = $form.data('submitObject'), resetForm: function () {
extData = '&' + settings.ajaxVar + '=' + $form.attr('id'); var $form = $(this);
if ($button && $button.length) { var data = $form.data('yiiActiveForm');
extData += '&' + $button.attr('name') + '=' + $button.attr('value'); // Because we bind directly to a form reset event instead of a reset button (that may not exist),
// when this function is executed form input values have not been reset yet.
// Therefore we do the actual reset work through setTimeout.
setTimeout(function () {
$.each(data.attributes, function () {
// Without setTimeout() we would get the input values that are not reset yet.
this.value = getValue($form, this);
this.status = 0;
var $container = $form.find(this.container);
$container.removeClass(
data.settings.validatingCssClass + ' ' +
data.settings.errorCssClass + ' ' +
data.settings.successCssClass
);
$container.find(this.error).html('');
});
$form.find(data.settings.summary).hide().find('ul').html('');
}, 1);
} }
};
$.ajax({ var watchAttributes = function ($form, attributes) {
url: settings.validationUrl, $.each(attributes, function (i, attribute) {
type: $form.attr('method'), var $input = findInput($form, attribute);
data: $form.serialize() + extData, if (attribute.validateOnChange) {
dataType: 'json', $input.on('change.yiiActiveForm', function () {
success: function (data) { validateAttribute($form, attribute, false);
if (data !== null && typeof data === 'object') { }).on('blur.yiiActiveForm', function () {
$.each(settings.attributes, function () { if (attribute.status == 0 || attribute.status == 1) {
if (!this.enableAjaxValidation) { validateAttribute($form, attribute, !attribute.status);
delete data[this.id]; }
} });
}); }
successCallback($.extend({}, messages, data)); if (attribute.validateOnType) {
} else { $input.on('keyup.yiiActiveForm', function () {
successCallback(messages); if (attribute.value !== getValue($form, attribute)) {
} validateAttribute($form, attribute, false);
}, }
error: function () { });
if (errorCallback !== undefined) {
errorCallback();
}
} }
}); });
}; };
var validateAttribute = function (attribute, forceValidate) { var validateAttribute = function ($form, attribute, forceValidate) {
var data = $form.data('yiiActiveForm');
if (forceValidate) { if (forceValidate) {
attribute.status = 2; attribute.status = 2;
} }
$.each(attributes, function () { $.each(data.attributes, function () {
if (this.value !== getInputValue($form.find('#' + this.inputID))) { if (this.value !== getValue($form, this)) {
this.status = 2; this.status = 2;
forceValidate = true; forceValidate = true;
} }
@ -264,158 +224,164 @@
return; return;
} }
if (settings.timer !== undefined) { if (data.settings.timer !== undefined) {
clearTimeout(settings.timer); clearTimeout(data.settings.timer);
} }
settings.timer = setTimeout(function () { data.settings.timer = setTimeout(function () {
if (settings.submitting || $form.is(':hidden')) { if (data.submitting || $form.is(':hidden')) {
return; return;
} }
if (attribute.beforeValidateAttribute === undefined || attribute.beforeValidateAttribute($form, attribute)) { $.each(data.attributes, function () {
$.each(settings.attributes, function () { if (this.status === 2) {
if (this.status === 2) { this.status = 3;
this.status = 3; $form.find(this.container).addClass(data.settings.validatingCssClass);
$.fn.yiiactiveform.getInputContainer(this, $form).addClass(this.validatingCssClass); }
} });
}); validate($form, function (messages) {
$.fn.yiiactiveform.validate($form, function (data) { var hasError = false;
var hasError = false; $.each(data.attributes, function () {
$.each(settings.attributes, function () { if (this.status === 2 || this.status === 3) {
if (this.status === 2 || this.status === 3) { hasError = updateInput($form, this, messages) || hasError;
hasError = $.fn.yiiactiveform.updateInput(this, data, $form) || hasError;
}
});
if (attribute.afterValidateAttribute !== undefined) {
attribute.afterValidateAttribute($form, attribute, data, hasError);
} }
}); });
}
}, attribute.validationDelay);
};
var resetForm = function () {
/*
* In case of resetting the form we need to reset error messages
* NOTE1: $form.reset - does not exist
* NOTE2: $form.on('reset', ...) does not work
*/
/*
* because we bind directly to a form reset event, not to a reset button (that could or could not exist),
* when this function is executed form elements values have not been reset yet,
* because of that we use the setTimeout
*/
setTimeout(function () {
$.each(settings.attributes, function () {
this.status = 0;
var $error = $form.find('#' + this.errorID),
$container = $.fn.yiiactiveform.getInputContainer(this, $form);
$container.removeClass(
this.validatingCssClass + ' ' +
this.errorCssClass + ' ' +
this.successCssClass
);
$error.html('').hide();
/*
* without the setTimeout() we would get here the current entered value before the reset instead of the reseted value
*/
this.value = getAFValue($form.find('#' + this.inputID));
});
/*
* If the form is submited (non ajax) with errors, labels and input gets the class 'error'
*/
$form.find('label, input').each(function () {
$(this).removeClass(settings.errorCss);
}); });
$('#' + settings.summaryID).hide().find('ul').html(''); }, data.settings.validationDelay);
//.. set to initial focus on reset
if (settings.focus !== undefined && !window.location.hash) {
$form.find(settings.focus).focus();
}
}, 1);
}; };
/** /**
* Returns the container element of the specified attribute. * Performs validation.
* @param attribute object the configuration for a particular attribute. * @param $form jQuery the jquery representation of the form
* @param form the form jQuery object * @param successCallback function the function to be invoked if the validation completes
* @return jQuery the jQuery representation of the container * @param errorCallback function the function to be invoked if the ajax validation request fails
*/ */
var getInputContainer = function (attribute, form) { var validate = function ($form, successCallback, errorCallback) {
if (attribute.inputContainer === undefined) { var data = $form.data('yiiActiveForm'),
return form.find('#' + attribute.inputID).closest('div'); needAjaxValidation = false,
messages = {};
$.each(data.attributes, function () {
if (data.submitting || this.status === 2 || this.status === 3) {
var msg = [];
if (!data.settings.beforeValidate || data.settings.beforeValidate($form, this, msg)) {
if (this.validate) {
this.validate(this, getValue($form, this), msg);
}
if (msg.length) {
messages[this.name] = msg;
} else if (this.enableAjaxValidation) {
needAjaxValidation = true;
}
}
}
});
if (needAjaxValidation && (!data.submitting || $.isEmptyObject(messages))) {
// Perform ajax validation when at least one input needs it.
// If the validation is triggered by form submission, ajax validation
// should be done only when all inputs pass client validation
var $button = data.submitObject,
extData = '&' + data.settings.ajaxVar + '=' + $form.attr('id');
if ($button && $button.length && $button.attr('name')) {
extData += '&' + $button.attr('name') + '=' + $button.attr('value');
}
$.ajax({
url: data.settings.validationUrl,
type: $form.attr('method'),
data: $form.serialize() + extData,
dataType: 'json',
success: function (msgs) {
if (msgs !== null && typeof msgs === 'object') {
$.each(data.attributes, function () {
if (!this.enableAjaxValidation) {
delete msgs[this.name];
}
});
successCallback($.extend({}, messages, msgs));
} else {
successCallback(messages);
}
},
error: errorCallback
});
} else if (data.submitting) {
// delay callback so that the form can be submitted without problem
setTimeout(function () {
successCallback(messages);
}, 200);
} else { } else {
return form.find(attribute.inputContainer).filter(':has("#' + attribute.inputID + '")'); successCallback(messages);
} }
}; };
/** /**
* updates the error message and the input container for a particular attribute. * Updates the error message and the input container for a particular attribute.
* @param $form the form jQuery object
* @param attribute object the configuration for a particular attribute. * @param attribute object the configuration for a particular attribute.
* @param messages array the json data obtained from the ajax validation request * @param messages array the validation error messages
* @param form the form jQuery object
* @return boolean whether there is a validation error for the specified attribute * @return boolean whether there is a validation error for the specified attribute
*/ */
var updateInput = function (attribute, messages, form) { var updateInput = function ($form, attribute, messages) {
attribute.status = 1; var data = $form.data('yiiActiveForm'),
var $error, $container, $input = findInput($form, attribute),
hasError = false, hasError = false;
$el = form.find('#' + attribute.inputID),
errorCss = form.data('settings').errorCss;
if ($el.length) {
hasError = messages !== null && $.isArray(messages[attribute.id]) && messages[attribute.id].length > 0;
$error = form.find('#' + attribute.errorID);
$container = $.fn.yiiactiveform.getInputContainer(attribute, form);
$container.removeClass(
attribute.validatingCssClass + ' ' +
attribute.errorCssClass + ' ' +
attribute.successCssClass
);
$container.find('label, input').each(function () {
$(this).removeClass(errorCss);
});
attribute.status = 1;
if ($input.length) {
hasError = messages && $.isArray(messages[attribute.name]) && messages[attribute.name].length;
var $container = $form.find(attribute.container);
var $error = $container.find(attribute.error);
if (hasError) { if (hasError) {
$error.html(messages[attribute.id][0]); $error.html(messages[attribute.name][0]);
$container.addClass(attribute.errorCssClass); $container.removeClass(data.settings.validatingCssClass + ' ' + data.settings.successCssClass)
} else if (attribute.enableAjaxValidation || attribute.clientValidation) { .addClass(data.settings.errorCssClass);
$container.addClass(attribute.successCssClass); } else {
} $error.html('');
if (!attribute.hideErrorMessage) { $container.removeClass(data.settings.validatingCssClass + ' ' + data.settings.errorCssClass + ' ')
$error.toggle(hasError); .addClass(data.settings.successCssClass);
} }
attribute.value = getValue($form, attribute);
attribute.value = getAFValue($el);
} }
return hasError; return hasError;
}; };
/** /**
* updates the error summary, if any. * Updates the error summary.
* @param form jquery the jquery representation of the form * @param $form the form jQuery object
* @param messages array the json data obtained from the ajax validation request * @param messages array the validation error messages
*/ */
var updateSummary = function (form, messages) { var updateSummary = function ($form, messages) {
var settings = $(form).data('settings'), var data = $form.data('yiiActiveForm'),
$summary = $form.find(data.settings.errorSummary),
content = ''; content = '';
if (settings.summaryID === undefined) {
return; if ($summary.length && messages) {
} $.each(data.attributes, function () {
if (messages) { if ($.isArray(messages[this.name]) && messages[this.name].length) {
$.each(settings.attributes, function () { content += '<li>' + messages[this.name][0] + '</li>';
if ($.isArray(messages[this.id])) {
$.each(messages[this.id], function (j, message) {
content = content + '<li>' + message + '</li>';
});
} }
}); });
$summary.toggle(content !== '').find('ul').html(content);
}
};
var getValue = function ($form, attribute) {
var $input = findInput($form, attribute);
var type = $input.attr('type');
if (type === 'checkbox' || type === 'radio') {
return $input.filter(':checked').val();
} else {
return $input.val();
}
};
var findInput = function ($form, attribute) {
var $input = $form.find(attribute.input);
if ($input.length && $input[0].tagName.toLowerCase() === 'div') {
// checkbox list or radio list
return $input.find('input');
} else {
return $input;
} }
$('#' + settings.summaryID).toggle(content !== '').find('ul').html(content);
}; };
})(window.jQuery); })(window.jQuery);

63
framework/assets/yii.js

@ -7,24 +7,59 @@
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
/**
* yii is the root module for all Yii JavaScript modules.
* It implements a mechanism of organizing JavaScript code in modules through the function "yii.initModule()".
*
* Each module should be named as "x.y.z", where "x" stands for the root module (for the Yii core code, this is "yii").
*
* A module may be structured as follows:
*
* ~~~
* yii.sample = (function($) {
* var pub = {
* // whether this module is currently active. If false, init() will not be called for this module
* // it will also not be called for all its child modules. If this property is undefined, it means true.
* isActive: true,
* init: function() {
* // ... module initialization code go here ...
* },
*
* // ... other public functions and properties go here ...
* };
*
* // ... private functions and properties go here ...
*
* return pub;
* });
* ~~~
*
* Using this structure, you can define public and private functions/properties for a module.
* Private functions/properties are only visible within the module, while public functions/properties
* may be accessed outside of the module. For example, you can access "yii.sample.init()".
*
* You must call "yii.initModule()" once for the root module of all your modules.
*/
yii = (function ($) { yii = (function ($) {
var pub = { var pub = {
version: '2.0' version: '2.0',
initModule: function (module) {
if (module.isActive === undefined || module.isActive) {
if ($.isFunction(module.init)) {
module.init();
}
$.each(module, function () {
if ($.isPlainObject(this)) {
pub.initModule(this);
}
});
}
}
}; };
return pub; return pub;
})(jQuery); })(jQuery);
jQuery(document).ready(function ($) { jQuery(document).ready(function () {
// call the init() method of every module yii.initModule(yii);
var init = function (module) {
if ($.isFunction(module.init) && (module.trigger == undefined || $(module.trigger).length)) {
module.init();
}
$.each(module, function () {
if ($.isPlainObject(this)) {
init(this);
}
});
};
init(yii);
}); });

187
framework/assets/yii.validation.js

@ -1,7 +1,7 @@
/** /**
* Yii validation module. * Yii validation module.
* *
* This is the JavaScript widget used by the yii\widgets\ActiveForm widget. * This JavaScript module provides the validation methods for the built-in validaotrs.
* *
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
@ -11,7 +11,188 @@
*/ */
yii.validation = (function ($) { yii.validation = (function ($) {
var pub = { var isEmpty = function (value, trim) {
return value === null || value === undefined || value == []
|| value === '' || trim && $.trim(value) === '';
};
return {
required: function (value, messages, options) {
var valid = false;
if (options.requiredValue === undefined) {
if (options.strict && value !== undefined || !options.strict && !isEmpty(value, true)) {
valid = true;
}
} else if (!options.strict && value == options.requiredValue || options.strict && value === options.requiredValue) {
valid = true;
}
if (!valid) {
messages.push(options.message);
}
},
boolean: function (value, messages, options) {
if (options.skipOnEmpty && isEmpty(value)) {
return;
}
var valid = !options.strict && (value == options.trueValue || value == options.falseValue)
|| options.strict && (value === options.trueValue || value === options.falseValue);
if (!valid) {
messages.push(options.message);
}
},
string: function (value, messages, options) {
if (options.skipOnEmpty && isEmpty(value)) {
return;
}
if (typeof value !== 'string') {
messages.push(options.message);
return;
}
if (options.min !== undefined && value.length < options.min) {
messages.push(options.tooShort);
}
if (options.max !== undefined && value.length > options.max) {
messages.push(options.tooLong);
}
if (options.is !== undefined && value.length != options.is) {
messages.push(options.is);
}
},
number: function (value, messages, options) {
if (options.skipOnEmpty && isEmpty(value)) {
return;
}
if (typeof value === 'string' && !value.match(options.pattern)) {
messages.push(options.message);
return;
}
if (options.min !== undefined && value < options.min) {
messages.push(options.tooSmall);
}
if (options.max !== undefined && value > options.max) {
messages.push(options.tooBig);
}
},
range: function (value, messages, options) {
if (options.skipOnEmpty && isEmpty(value)) {
return;
}
var valid = !options.not && $.inArray(value, options.range)
|| options.not && !$.inArray(value, options.range);
if (!valid) {
messages.push(options.message);
}
},
regularExpression: function (value, messages, options) {
if (options.skipOnEmpty && isEmpty(value)) {
return;
}
if (!options.not && !value.match(options.pattern) || options.not && value.match(options.pattern)) {
messages.push(options.message)
}
},
email: function (value, messages, options) {
if (options.skipOnEmpty && isEmpty(value)) {
return;
}
var valid = value.match(options.pattern) && (!options.allowName || value.match(options.fullPattern));
if (!valid) {
messages.push(options.message);
}
},
url: function (value, messages, options) {
if (options.skipOnEmpty && isEmpty(value)) {
return;
}
if (options.defaultScheme && !value.match(/:\/\//)) {
value = options.defaultScheme + '://' + value;
}
if (!value.match(options.pattern)) {
messages.push(options.message);
}
},
captcha: function (value, messages, options) {
if (options.skipOnEmpty && isEmpty(value)) {
return;
}
// CAPTCHA may be updated via AJAX and the updated hash is stored in body data
var hash = $('body').data(options.hashKey);
if (hash == null) {
hash = options.hash;
} else {
hash = hash[options.caseSensitive ? 0 : 1];
}
var v = options.caseSensitive ? value : value.toLowerCase();
for (var i = v.length - 1, h = 0; i >= 0; --i) {
h += v.charCodeAt(i);
}
if (h != hash) {
messages.push(options.message);
}
},
compare: function (value, messages, options) {
if (options.skipOnEmpty && isEmpty(value)) {
return;
}
var compareValue, valid = true;
if (options.compareAttribute === undefined) {
compareValue = options.compareValue;
} else {
compareValue = $('#' + options.compareAttribute).val();
}
switch (options.operator) {
case '==':
valid = value == compareValue;
break;
case '===':
valid = value === compareValue;
break;
case '!=':
valid = value != compareValue;
break;
case '!==':
valid = value !== compareValue;
break;
case '>':
valid = value > compareValue;
break;
case '>=':
valid = value >= compareValue;
break;
case '<':
valid = value < compareValue;
break;
case '<=':
valid = value <= compareValue;
break;
}
if (!valid) {
messages.push(options.message);
}
}
}; };
return pub;
})(jQuery); })(jQuery);

2
framework/base/ActionFilter.php

@ -87,4 +87,4 @@ class ActionFilter extends Behavior
{ {
return !in_array($action->id, $this->except, true) && (empty($this->only) || in_array($action->id, $this->only, true)); return !in_array($action->id, $this->except, true) && (empty($this->only) || in_array($action->id, $this->only, true));
} }
} }

9
framework/base/Application.php

@ -306,6 +306,15 @@ class Application extends Module
} }
/** /**
* @return null|Component
* @todo
*/
public function getAuthManager()
{
return $this->getComponent('auth');
}
/**
* Registers the core application components. * Registers the core application components.
* @see setComponents * @see setComponents
*/ */

3
framework/base/Component.php

@ -90,6 +90,7 @@ class Component extends Object
// as behavior: attach behavior // as behavior: attach behavior
$name = trim(substr($name, 3)); $name = trim(substr($name, 3));
$this->attachBehavior($name, $value instanceof Behavior ? $value : Yii::createObject($value)); $this->attachBehavior($name, $value instanceof Behavior ? $value : Yii::createObject($value));
return;
} else { } else {
// behavior property // behavior property
$this->ensureBehaviors(); $this->ensureBehaviors();
@ -496,6 +497,7 @@ class Component extends Object
*/ */
public function detachBehavior($name) public function detachBehavior($name)
{ {
$this->ensureBehaviors();
if (isset($this->_behaviors[$name])) { if (isset($this->_behaviors[$name])) {
$behavior = $this->_behaviors[$name]; $behavior = $this->_behaviors[$name];
unset($this->_behaviors[$name]); unset($this->_behaviors[$name]);
@ -511,6 +513,7 @@ class Component extends Object
*/ */
public function detachBehaviors() public function detachBehaviors()
{ {
$this->ensureBehaviors();
if ($this->_behaviors !== null) { if ($this->_behaviors !== null) {
foreach ($this->_behaviors as $name => $behavior) { foreach ($this->_behaviors as $name => $behavior) {
$this->detachBehavior($name); $this->detachBehavior($name);

2
framework/base/Exception.php

@ -22,4 +22,4 @@ class Exception extends \Exception
{ {
return \Yii::t('yii|Exception'); return \Yii::t('yii|Exception');
} }
} }

30
framework/base/View.php

@ -72,23 +72,21 @@ class View extends Component
/** /**
* @var array a list of available renderers indexed by their corresponding supported file extensions. * @var array a list of available renderers indexed by their corresponding supported file extensions.
* Each renderer may be a view renderer object or the configuration for creating the renderer object. * Each renderer may be a view renderer object or the configuration for creating the renderer object.
* For example, * The default setting supports both Smarty and Twig (their corresponding file extension is "tpl"
* * and "twig" respectively. Please refer to [[SmartyRenderer]] and [[TwigRenderer]] on how to install
* ~~~ * the needed libraries for these template engines.
* array(
* 'tpl' => array(
* 'class' => 'yii\renderers\SmartyRenderer',
* ),
* 'twig' => array(
* 'class' => 'yii\renderers\TwigRenderer',
* ),
* )
* ~~~
* *
* If no renderer is available for the given view file, the view file will be treated as a normal PHP * If no renderer is available for the given view file, the view file will be treated as a normal PHP
* and rendered via [[renderPhpFile()]]. * and rendered via [[renderPhpFile()]].
*/ */
public $renderers = array(); public $renderers = array(
'tpl' => array(
'class' => 'yii\renderers\SmartyRenderer',
),
'twig' => array(
'class' => 'yii\renderers\TwigRenderer',
),
);
/** /**
* @var Theme|array the theme object or the configuration array for creating the theme object. * @var Theme|array the theme object or the configuration array for creating the theme object.
* If not set, it means theming is not enabled. * If not set, it means theming is not enabled.
@ -744,10 +742,10 @@ class View extends Component
{ {
$lines = array(); $lines = array();
if (!empty($this->metaTags)) { if (!empty($this->metaTags)) {
$lines[] = implode("\n", $this->cssFiles); $lines[] = implode("\n", $this->metaTags);
} }
if (!empty($this->linkTags)) { if (!empty($this->linkTags)) {
$lines[] = implode("\n", $this->cssFiles); $lines[] = implode("\n", $this->linkTags);
} }
if (!empty($this->cssFiles)) { if (!empty($this->cssFiles)) {
$lines[] = implode("\n", $this->cssFiles); $lines[] = implode("\n", $this->cssFiles);
@ -797,4 +795,4 @@ class View extends Component
} }
return implode("\n", $lines); return implode("\n", $lines);
} }
} }

2
framework/base/ViewEvent.php

@ -41,4 +41,4 @@ class ViewEvent extends Event
$this->viewFile = $viewFile; $this->viewFile = $viewFile;
parent::__construct($config); parent::__construct($config);
} }
} }

5
framework/base/Widget.php

@ -83,7 +83,8 @@ class Widget extends Component
*/ */
public function render($view, $params = array()) public function render($view, $params = array())
{ {
return $this->view->render($view, $params, $this); $viewFile = $this->findViewFile($view);
return $this->view->renderFile($viewFile, $params, $this);
} }
/** /**
@ -133,4 +134,4 @@ class Widget extends Component
return pathinfo($file, PATHINFO_EXTENSION) === '' ? $file . '.php' : $file; return pathinfo($file, PATHINFO_EXTENSION) === '' ? $file . '.php' : $file;
} }
} }

2
framework/caching/Cache.php

@ -349,4 +349,4 @@ abstract class Cache extends Component implements \ArrayAccess
{ {
$this->delete($key); $this->delete($key);
} }
} }

2
framework/caching/Dependency.php

@ -49,4 +49,4 @@ abstract class Dependency extends \yii\base\Object
* @return mixed the data needed to determine if dependency has been changed. * @return mixed the data needed to determine if dependency has been changed.
*/ */
abstract protected function generateDependencyData(); abstract protected function generateDependencyData();
} }

16
framework/caching/ExpressionDependency.php

@ -14,24 +14,36 @@ namespace yii\caching;
* The dependency is reported as unchanged if and only if the result of the expression is * The dependency is reported as unchanged if and only if the result of the expression is
* the same as the one evaluated when storing the data to cache. * the same as the one evaluated when storing the data to cache.
* *
* A PHP expression can be any PHP code that has a value. To learn more about what an expression is,
* please refer to the [php manual](http://www.php.net/manual/en/language.expressions.php).
*
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class ExpressionDependency extends Dependency class ExpressionDependency extends Dependency
{ {
/** /**
* @var string the PHP expression whose result is used to determine the dependency. * @var string the string representation of a PHP expression whose result is used to determine the dependency.
* A PHP expression can be any PHP code that has a value. To learn more about what an expression is,
* please refer to the [php manual](http://www.php.net/manual/en/language.expressions.php).
*/ */
public $expression; public $expression;
/**
* @var mixed custom data associated with this dependency. In [[expression]], you may compare the value of
* this property with the latest data to determine if the dependency has changed or not.
*/
public $data;
/** /**
* Constructor. * Constructor.
* @param string $expression the PHP expression whose result is used to determine the dependency. * @param string $expression the PHP expression whose result is used to determine the dependency.
* @param mixed $data the custom data associated with this dependency
* @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
*/ */
public function __construct($expression = 'true', $config = array()) public function __construct($expression = 'true', $data = null, $config = array())
{ {
$this->expression = $expression; $this->expression = $expression;
$this->data = $data;
parent::__construct($config); parent::__construct($config);
} }

2
framework/caching/MemCacheServer.php

@ -46,4 +46,4 @@ class MemCacheServer extends \yii\base\Object
* @var boolean if the server should be flagged as online upon a failure. This is used by memcache only. * @var boolean if the server should be flagged as online upon a failure. This is used by memcache only.
*/ */
public $status = true; public $status = true;
} }

2
framework/caching/WinCache.php

@ -89,4 +89,4 @@ class WinCache extends Cache
{ {
return wincache_ucache_clear(); return wincache_ucache_clear();
} }
} }

2
framework/console/Controller.php

@ -147,4 +147,4 @@ class Controller extends \yii\base\Controller
{ {
return array(); return array();
} }
} }

2
framework/console/controllers/AppController.php

@ -321,4 +321,4 @@ class AppController extends Controller
closedir($handle); closedir($handle);
return $list; return $list;
} }
} }

2
framework/console/controllers/AssetController.php

@ -350,4 +350,4 @@ return array(
EOD; EOD;
file_put_contents($configFile, $template); file_put_contents($configFile, $template);
} }
} }

2
framework/console/controllers/HelpController.php

@ -418,4 +418,4 @@ class HelpController extends Controller
$name = $required ? "$name (required)" : $name; $name = $required ? "$name (required)" : $name;
return $doc === '' ? $name : "$name: $doc"; return $doc === '' ? $name : "$name: $doc";
} }
} }

17
framework/console/webapp/config.php

@ -1,17 +0,0 @@
<?php
/** @var $controller \yii\console\controllers\AppController */
$controller = $this;
return array(
'default' => array(
'index.php' => array(
'handler' => function($source) use ($controller) {
return $controller->replaceRelativePath($source, realpath(YII_PATH.'/yii.php'), 'yii');
},
'permissions' => 0777,
),
'protected/runtime' => array(
'permissions' => 0755,
),
),
);

10
framework/console/webapp/default/index.php

@ -1,10 +0,0 @@
<?php
define('YII_DEBUG', true);
require __DIR__.'/../framework/yii.php';
$config = require dirname(__DIR__).'/protected/config/main.php';
$config['basePath'] = dirname(__DIR__).'/protected';
$app = new \yii\web\Application($config);
$app->run();

20
framework/console/webapp/default/protected/config/main.php

@ -1,20 +0,0 @@
<?php
return array(
'id' => 'webapp',
'name' => 'My Web Application',
'components' => array(
// uncomment the following to use a MySQL database
/*
'db' => array(
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=testdrive',
'username' => 'root',
'password' => '',
),
*/
'cache' => array(
'class' => 'yii\caching\DummyCache',
),
),
);

15
framework/console/webapp/default/protected/controllers/SiteController.php

@ -1,15 +0,0 @@
<?php
use \yii\web\Controller;
/**
* SiteController
*/
class SiteController extends Controller
{
public function actionIndex()
{
echo $this->render('index', array(
'name' => 'Qiang',
));
}
}

17
framework/console/webapp/default/protected/views/layouts/main.php

@ -1,17 +0,0 @@
<?php use yii\helpers\Html as Html; ?>
<!doctype html>
<html lang="<?php \Yii::$app->language?>">
<head>
<meta charset="utf-8" />
<title><?php echo Html::encode($this->title)?></title>
</head>
<body>
<h1><?php echo Html::encode($this->title)?></h1>
<div class="content">
<?php echo $content?>
</div>
<div class="footer">
<?php echo \Yii::powered()?>
</div>
</body>
</html>

1
framework/console/webapp/default/protected/views/site/index.php

@ -1 +0,0 @@
Hello, <?php echo $name?>!

3
framework/db/ActiveQuery.php

@ -88,7 +88,8 @@ class ActiveQuery extends Query
{ {
if (method_exists($this->modelClass, $name)) { if (method_exists($this->modelClass, $name)) {
array_unshift($params, $this); array_unshift($params, $this);
return call_user_func_array(array($this->modelClass, $name), $params); call_user_func_array(array($this->modelClass, $name), $params);
return $this;
} else { } else {
return parent::__call($name, $params); return parent::__call($name, $params);
} }

2
framework/db/ActiveRelation.php

@ -266,7 +266,7 @@ class ActiveRelation extends ActiveQuery
{ {
$attributes = array_keys($this->link); $attributes = array_keys($this->link);
$values = array(); $values = array();
if (count($attributes) ===1) { if (count($attributes) === 1) {
// single key // single key
$attribute = reset($this->link); $attribute = reset($this->link);
foreach ($models as $model) { foreach ($models as $model) {

4
framework/db/Connection.php

@ -66,7 +66,7 @@ use yii\caching\Cache;
* // ... executing other SQL statements ... * // ... executing other SQL statements ...
* $transaction->commit(); * $transaction->commit();
* } catch(Exception $e) { * } catch(Exception $e) {
* $transaction->rollBack(); * $transaction->rollback();
* } * }
* ~~~ * ~~~
* *
@ -517,7 +517,7 @@ class Connection extends Component
public function quoteSql($sql) public function quoteSql($sql)
{ {
$db = $this; $db = $this;
return preg_replace_callback('/(\\{\\{([\w\-\. ]+)\\}\\}|\\[\\[([\w\-\. ]+)\\]\\])/', return preg_replace_callback('/(\\{\\{([%\w\-\. ]+)\\}\\}|\\[\\[([\w\-\. ]+)\\]\\])/',
function($matches) use($db) { function($matches) use($db) {
if (isset($matches[3])) { if (isset($matches[3])) {
return $db->quoteColumnName($matches[3]); return $db->quoteColumnName($matches[3]);

2
framework/db/Exception.php

@ -41,4 +41,4 @@ class Exception extends \yii\base\Exception
{ {
return \Yii::t('yii|Database Exception'); return \Yii::t('yii|Database Exception');
} }
} }

2
framework/db/Expression.php

@ -57,4 +57,4 @@ class Expression extends \yii\base\Object
{ {
return $this->expression; return $this->expression;
} }
} }

10
framework/db/Migration.php

@ -64,14 +64,14 @@ class Migration extends \yii\base\Component
$transaction = $this->db->beginTransaction(); $transaction = $this->db->beginTransaction();
try { try {
if ($this->safeUp() === false) { if ($this->safeUp() === false) {
$transaction->rollBack(); $transaction->rollback();
return false; return false;
} }
$transaction->commit(); $transaction->commit();
} catch (\Exception $e) { } catch (\Exception $e) {
echo "Exception: " . $e->getMessage() . ' (' . $e->getFile() . ':' . $e->getLine() . ")\n"; echo "Exception: " . $e->getMessage() . ' (' . $e->getFile() . ':' . $e->getLine() . ")\n";
echo $e->getTraceAsString() . "\n"; echo $e->getTraceAsString() . "\n";
$transaction->rollBack(); $transaction->rollback();
return false; return false;
} }
return null; return null;
@ -89,14 +89,14 @@ class Migration extends \yii\base\Component
$transaction = $this->db->beginTransaction(); $transaction = $this->db->beginTransaction();
try { try {
if ($this->safeDown() === false) { if ($this->safeDown() === false) {
$transaction->rollBack(); $transaction->rollback();
return false; return false;
} }
$transaction->commit(); $transaction->commit();
} catch (\Exception $e) { } catch (\Exception $e) {
echo "Exception: " . $e->getMessage() . ' (' . $e->getFile() . ':' . $e->getLine() . ")\n"; echo "Exception: " . $e->getMessage() . ' (' . $e->getFile() . ':' . $e->getLine() . ")\n";
echo $e->getTraceAsString() . "\n"; echo $e->getTraceAsString() . "\n";
$transaction->rollBack(); $transaction->rollback();
return false; return false;
} }
return null; return null;
@ -368,4 +368,4 @@ class Migration extends \yii\base\Component
$this->db->createCommand()->dropIndex($name, $table)->execute(); $this->db->createCommand()->dropIndex($name, $table)->execute();
echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n"; echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
} }
} }

4
framework/db/Query.php

@ -483,7 +483,7 @@ class Query extends \yii\base\Component
* Sets the ORDER BY part of the query. * Sets the ORDER BY part of the query.
* @param string|array $columns the columns (and the directions) to be ordered by. * @param string|array $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* (e.g. `array('id' => Query::SORT_ASC ASC, 'name' => Query::SORT_DESC)`). * (e.g. `array('id' => Query::SORT_ASC, 'name' => Query::SORT_DESC)`).
* The method will automatically quote the column names unless a column contains some parenthesis * The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression). * (which means the column contains a DB expression).
* @return Query the query object itself * @return Query the query object itself
@ -499,7 +499,7 @@ class Query extends \yii\base\Component
* Adds additional ORDER BY columns to the query. * Adds additional ORDER BY columns to the query.
* @param string|array $columns the columns (and the directions) to be ordered by. * @param string|array $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* (e.g. `array('id' => Query::SORT_ASC ASC, 'name' => Query::SORT_DESC)`). * (e.g. `array('id' => Query::SORT_ASC, 'name' => Query::SORT_DESC)`).
* The method will automatically quote the column names unless a column contains some parenthesis * The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression). * (which means the column contains a DB expression).
* @return Query the query object itself * @return Query the query object itself

2
framework/db/StaleObjectException.php

@ -20,4 +20,4 @@ class StaleObjectException extends Exception
{ {
return \Yii::t('yii|Stale Object Exception'); return \Yii::t('yii|Stale Object Exception');
} }
} }

6
framework/db/Transaction.php

@ -25,7 +25,7 @@ use yii\base\InvalidConfigException;
* //.... other SQL executions * //.... other SQL executions
* $transaction->commit(); * $transaction->commit();
* } catch(Exception $e) { * } catch(Exception $e) {
* $transaction->rollBack(); * $transaction->rollback();
* } * }
* ~~~ * ~~~
* *
@ -42,14 +42,14 @@ class Transaction extends \yii\base\Object
public $db; public $db;
/** /**
* @var boolean whether this transaction is active. Only an active transaction * @var boolean whether this transaction is active. Only an active transaction
* can [[commit()]] or [[rollBack()]]. This property is set true when the transaction is started. * can [[commit()]] or [[rollback()]]. This property is set true when the transaction is started.
*/ */
private $_active = false; private $_active = false;
/** /**
* Returns a value indicating whether this transaction is active. * Returns a value indicating whether this transaction is active.
* @return boolean whether this transaction is active. Only an active transaction * @return boolean whether this transaction is active. Only an active transaction
* can [[commit()]] or [[rollBack()]]. * can [[commit()]] or [[rollback()]].
*/ */
public function getIsActive() public function getIsActive()
{ {

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

Loading…
Cancel
Save