resurtm
12 years ago
538 changed files with 51288 additions and 44868 deletions
@ -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 |
@ -1,9 +0,0 @@
|
||||
<?php |
||||
|
||||
defined('YII_DEBUG') or define('YII_DEBUG', true); |
||||
|
||||
require(__DIR__ . '/../framework/yii.php'); |
||||
|
||||
$config = require(__DIR__ . '/protected/config/main.php'); |
||||
$application = new yii\web\Application($config); |
||||
$application->run(); |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@ -0,0 +1,14 @@
|
||||
<?php |
||||
|
||||
// comment out the following line to disable debug mode |
||||
defined('YII_DEBUG') or define('YII_DEBUG', true); |
||||
|
||||
$frameworkPath = __DIR__ . '/../../yii'; |
||||
|
||||
require($frameworkPath . '/Yii.php'); |
||||
// Register Composer autoloader |
||||
@include($frameworkPath . '/vendor/autoload.php'); |
||||
|
||||
$config = require(__DIR__ . '/protected/config/main.php'); |
||||
$application = new yii\web\Application($config); |
||||
$application->run(); |
@ -1,17 +1,20 @@
|
||||
<?php |
||||
use yii\helpers\Html; |
||||
use yii\widgets\ActiveForm; |
||||
|
||||
/** |
||||
* @var yii\base\View $this |
||||
* @var yii\widgets\ActiveForm $form |
||||
* @var app\models\LoginForm $model |
||||
*/ |
||||
$this->title = 'Login'; |
||||
$this->params['breadcrumbs'][] = $this->title; |
||||
?> |
||||
<h1><?php echo Html::encode($this->title); ?></h1>
|
||||
|
||||
<p>Please fill out the following fields to login:</p> |
||||
|
||||
<?php $form = $this->beginWidget('yii\widgets\ActiveForm', array('options' => array('class' => 'form-horizontal'))); ?> |
||||
<?php $form = $this->beginWidget(ActiveForm::className(), array('options' => array('class' => 'form-horizontal'))); ?> |
||||
<?php echo $form->field($model, 'username')->textInput(); ?> |
||||
<?php echo $form->field($model, 'password')->passwordInput(); ?> |
||||
<?php echo $form->field($model, 'rememberMe')->checkbox(); ?> |
@ -0,0 +1,79 @@
|
||||
{ |
||||
"name": "yiisoft/yii2", |
||||
"description": "Yii2 Web Programming Framework", |
||||
"keywords": ["yii", "framework"], |
||||
"homepage": "http://www.yiiframework.com/", |
||||
"type": "library", |
||||
"license": "BSD-3-Clause", |
||||
"authors": [ |
||||
{ |
||||
"name": "Qiang Xue", |
||||
"email": "qiang.xue@gmail.com", |
||||
"homepage": "http://www.yiiframework.com/", |
||||
"role": "Founder and project lead" |
||||
}, |
||||
{ |
||||
"name": "Alexander Makarov", |
||||
"email": "sam@rmcreative.ru", |
||||
"homepage": "http://rmcreative.ru/", |
||||
"role": "Core framework development" |
||||
}, |
||||
{ |
||||
"name": "Maurizio Domba", |
||||
"homepage": "http://mdomba.info/", |
||||
"role": "Core framework development" |
||||
}, |
||||
{ |
||||
"name": "Carsten Brandt", |
||||
"email": "mail@cebe.cc", |
||||
"homepage": "http://cebe.cc/", |
||||
"role": "Core framework development" |
||||
}, |
||||
{ |
||||
"name": "Timur Ruziev", |
||||
"email": "resurtm@gmail.com", |
||||
"homepage": "http://resurtm.com/", |
||||
"role": "Core framework development" |
||||
}, |
||||
{ |
||||
"name": "Paul Klimov", |
||||
"email": "klimov.paul@gmail.com", |
||||
"role": "Core framework development" |
||||
}, |
||||
{ |
||||
"name": "Wei Zhuo", |
||||
"email": "weizhuo@gmail.com", |
||||
"role": "Project site maintenance and development" |
||||
}, |
||||
{ |
||||
"name": "Sebastián Thierer", |
||||
"email": "sebas@artfos.com", |
||||
"role": "Component development" |
||||
}, |
||||
{ |
||||
"name": "Jeffrey Winesett", |
||||
"email": "jefftulsa@gmail.com", |
||||
"role": "Documentation and marketing" |
||||
} |
||||
], |
||||
"support": { |
||||
"issues": "https://github.com/yiisoft/yii2/issues?state=open", |
||||
"forum": "http://www.yiiframework.com/forum/", |
||||
"wiki": "http://www.yiiframework.com/wiki/", |
||||
"irc": "irc://irc.freenode.net/yii", |
||||
"source": "https://github.com/yiisoft/yii2" |
||||
}, |
||||
"config": { |
||||
"vendor-dir": "yii/vendor" |
||||
}, |
||||
"bin": [ |
||||
"yii/yiic" |
||||
], |
||||
"require": { |
||||
"php": ">=5.3.0", |
||||
"michelf/php-markdown": "1.3", |
||||
"twig/twig": "1.12.*", |
||||
"smarty/smarty": "3.1.*", |
||||
"ezyang/htmlpurifier": "v4.5.0" |
||||
} |
||||
} |
@ -0,0 +1,212 @@
|
||||
{ |
||||
"_readme": [ |
||||
"This file locks the dependencies of your project to a known state", |
||||
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" |
||||
], |
||||
"hash": "7d46ce9c4d8d5f4ecae1611ea8f0b49c", |
||||
"packages": [ |
||||
{ |
||||
"name": "ezyang/htmlpurifier", |
||||
"version": "v4.5.0", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/ezyang/htmlpurifier.git", |
||||
"reference": "v4.5.0" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/v4.5.0", |
||||
"reference": "v4.5.0", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"php": ">=5.2" |
||||
}, |
||||
"type": "library", |
||||
"autoload": { |
||||
"psr-0": { |
||||
"HTMLPurifier": "library/" |
||||
}, |
||||
"files": [ |
||||
"library/HTMLPurifier.composer.php" |
||||
] |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"LGPL" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Edward Z. Yang", |
||||
"email": "admin@htmlpurifier.org", |
||||
"homepage": "http://ezyang.com" |
||||
} |
||||
], |
||||
"description": "Standards compliant HTML filter written in PHP", |
||||
"homepage": "http://htmlpurifier.org/", |
||||
"keywords": [ |
||||
"html" |
||||
], |
||||
"time": "2013-02-18 00:04:08" |
||||
}, |
||||
{ |
||||
"name": "michelf/php-markdown", |
||||
"version": "1.3", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/michelf/php-markdown.git", |
||||
"reference": "1.3" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/michelf/php-markdown/zipball/1.3", |
||||
"reference": "1.3", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"php": ">=5.3.0" |
||||
}, |
||||
"type": "library", |
||||
"extra": { |
||||
"branch-alias": { |
||||
"dev-lib": "1.3.x-dev" |
||||
} |
||||
}, |
||||
"autoload": { |
||||
"psr-0": { |
||||
"Michelf": "" |
||||
} |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"BSD-3-Clause" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Michel Fortin", |
||||
"email": "michel.fortin@michelf.ca", |
||||
"homepage": "http://michelf.ca/", |
||||
"role": "Developer" |
||||
}, |
||||
{ |
||||
"name": "John Gruber", |
||||
"homepage": "http://daringfireball.net/" |
||||
} |
||||
], |
||||
"description": "PHP Markdown", |
||||
"homepage": "http://michelf.ca/projects/php-markdown/", |
||||
"keywords": [ |
||||
"markdown" |
||||
], |
||||
"time": "2013-04-11 18:53:11" |
||||
}, |
||||
{ |
||||
"name": "smarty/smarty", |
||||
"version": "v3.1.13", |
||||
"source": { |
||||
"type": "svn", |
||||
"url": "http://smarty-php.googlecode.com/svn", |
||||
"reference": "/tags/v3.1.13/@4699" |
||||
}, |
||||
"require": { |
||||
"php": ">=5.2" |
||||
}, |
||||
"type": "library", |
||||
"autoload": { |
||||
"classmap": [ |
||||
"distribution/libs/Smarty.class.php", |
||||
"distribution/libs/SmartyBC.class.php" |
||||
] |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"LGPL-3.0" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Monte Ohrt", |
||||
"email": "monte@ohrt.com" |
||||
}, |
||||
{ |
||||
"name": "Uwe Tews", |
||||
"email": "uwe.tews@googlemail.com" |
||||
}, |
||||
{ |
||||
"name": "Rodney Rehm", |
||||
"email": "rodney.rehm@medialize.de" |
||||
} |
||||
], |
||||
"description": "Smarty - the compiling PHP template engine", |
||||
"homepage": "http://www.smarty.net", |
||||
"keywords": [ |
||||
"templating" |
||||
], |
||||
"time": "2013-01-26 12:03:52" |
||||
}, |
||||
{ |
||||
"name": "twig/twig", |
||||
"version": "v1.12.3", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/fabpot/Twig.git", |
||||
"reference": "v1.12.3" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/fabpot/Twig/zipball/v1.12.3", |
||||
"reference": "v1.12.3", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"php": ">=5.2.4" |
||||
}, |
||||
"type": "library", |
||||
"extra": { |
||||
"branch-alias": { |
||||
"dev-master": "1.12-dev" |
||||
} |
||||
}, |
||||
"autoload": { |
||||
"psr-0": { |
||||
"Twig_": "lib/" |
||||
} |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"BSD-3" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Fabien Potencier", |
||||
"email": "fabien@symfony.com" |
||||
}, |
||||
{ |
||||
"name": "Armin Ronacher", |
||||
"email": "armin.ronacher@active-4.com" |
||||
} |
||||
], |
||||
"description": "Twig, the flexible, fast, and secure template language for PHP", |
||||
"homepage": "http://twig.sensiolabs.org", |
||||
"keywords": [ |
||||
"templating" |
||||
], |
||||
"time": "2013-04-08 12:40:11" |
||||
} |
||||
], |
||||
"packages-dev": [ |
||||
|
||||
], |
||||
"aliases": [ |
||||
|
||||
], |
||||
"minimum-stability": "stable", |
||||
"stability-flags": [ |
||||
|
||||
], |
||||
"platform": { |
||||
"php": ">=5.3.0" |
||||
}, |
||||
"platform-dev": [ |
||||
|
||||
] |
||||
} |
@ -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,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,0 +1,459 @@
|
||||
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. |
||||
|
||||
|
||||
Namespace |
||||
--------- |
||||
|
||||
The most obvious change in Yii 2.0 is the use of namespaces. Almost every core class |
||||
is namespaced, e.g., `yii\web\Request`. The "C" prefix is no longer used in class names. |
||||
The naming of the namespaces follows the directory structure. For example, `yii\web\Request` |
||||
indicates the corresponding class file is `web/Request.php` under the Yii framework folder. |
||||
You can use any core class without explicitly include that class file, thanks to the Yii |
||||
class loader. |
||||
|
||||
|
||||
Component and Object |
||||
-------------------- |
||||
|
||||
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 base 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: |
||||
|
||||
```php |
||||
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: |
||||
|
||||
```php |
||||
$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: |
||||
|
||||
```php |
||||
$component->on($eventName, $handler); |
||||
// To detach the handler, use: |
||||
// $component->off($eventName, $handler); |
||||
``` |
||||
|
||||
|
||||
When you attach a handler, you can now associate it with some parameters which can be later |
||||
accessed via the event parameter by the handler: |
||||
|
||||
```php |
||||
$component->on($eventName, $handler, $params); |
||||
``` |
||||
|
||||
|
||||
Because of this change, you can now use "global" events. Simply trigger and attach handlers to |
||||
an event of the application instance: |
||||
|
||||
```php |
||||
Yii::$app->on($eventName, $handler); |
||||
.... |
||||
// this will trigger the event and cause $handler to be invoked. |
||||
Yii::$app->trigger($eventName); |
||||
``` |
||||
|
||||
|
||||
Path Alias |
||||
---------- |
||||
|
||||
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: |
||||
|
||||
```php |
||||
$content = Yii::$app->view->renderFile($viewFile, $params); |
||||
// You can also explicitly create a new View instance to do the rendering |
||||
// $view = new View; |
||||
// $view->renderFile($viewFile, $params); |
||||
``` |
||||
|
||||
|
||||
Also, there is no more `CClientScript` in Yii 2.0. The `View` class has taken over its role |
||||
with significant improvements. For more details, please see the "assets" subsection. |
||||
|
||||
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, |
||||
|
||||
```php |
||||
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, |
||||
|
||||
```php |
||||
$model = new Post; |
||||
if ($this->populate($_POST, $model)) {...} |
||||
// which is equivalent to: |
||||
if (isset($_POST['Post'])) { |
||||
$model->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: |
||||
|
||||
```php |
||||
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 |
||||
<?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, |
||||
|
||||
```php |
||||
$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: |
||||
|
||||
```php |
||||
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: |
||||
|
||||
```php |
||||
// 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, |
||||
|
||||
```php |
||||
$customers = Customer::find()->asArray()->all(); |
||||
``` |
||||
|
||||
By default, ActiveRecord now only saves dirty attributes. In 1.1, all attributes |
||||
would be saved to database when you call `save()`, regardless they are changed or not, |
||||
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, |
||||
|
||||
```php |
||||
$command = $connection->createCommand('SELECT [[id]] FROM {{posts}}'); |
||||
echo $command->sql; // MySQL: SELECT `id` FROM `posts` |
||||
``` |
||||
|
||||
This feature is especially useful if you are developing an application that supports |
||||
different DBMS. |
||||
|
||||
|
||||
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. |
||||
|
||||
```php |
||||
array( |
||||
'pattern' => 'post/<page:\d+>/<tag>', |
||||
'route' => 'post/index', |
||||
'defaults' => array('page' => 1), |
||||
) |
||||
``` |
||||
|
||||
|
||||
|
||||
Response |
||||
-------- |
||||
|
||||
Extensions |
||||
---------- |
||||
|
||||
Integration with Composer |
||||
------------------------- |
||||
|
||||
TBD |
||||
|
After Width: | Height: | Size: 63 KiB |
Binary file not shown.
@ -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, |
||||
), |
||||
), |
||||
); |
@ -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(); |
@ -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', |
||||
), |
||||
), |
||||
); |
@ -1,15 +0,0 @@
|
||||
<?php |
||||
use \yii\web\Controller; |
||||
|
||||
/** |
||||
* SiteController |
||||
*/ |
||||
class SiteController extends Controller |
||||
{ |
||||
public function actionIndex() |
||||
{ |
||||
echo $this->render('index', array( |
||||
'name' => 'Qiang', |
||||
)); |
||||
} |
||||
} |
@ -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 +0,0 @@
|
||||
Hello, <?php echo $name?>!
|
@ -1,470 +0,0 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\helpers\base; |
||||
|
||||
// todo test this on all kinds of terminals, especially windows (check out lib ncurses) |
||||
|
||||
/** |
||||
* Console View is the base class for console view components |
||||
* |
||||
* A console view provides functionality to create rich console application by allowing to format output |
||||
* by adding color and font style to it. |
||||
* |
||||
* @author Carsten Brandt <mail@cebe.cc> |
||||
* @since 2.0 |
||||
*/ |
||||
class ConsoleColor |
||||
{ |
||||
const FG_BLACK = 30; |
||||
const FG_RED = 31; |
||||
const FG_GREEN = 32; |
||||
const FG_YELLOW = 33; |
||||
const FG_BLUE = 34; |
||||
const FG_PURPLE = 35; |
||||
const FG_CYAN = 36; |
||||
const FG_GREY = 37; |
||||
|
||||
const BG_BLACK = 40; |
||||
const BG_RED = 41; |
||||
const BG_GREEN = 42; |
||||
const BG_YELLOW = 43; |
||||
const BG_BLUE = 44; |
||||
const BG_PURPLE = 45; |
||||
const BG_CYAN = 46; |
||||
const BG_GREY = 47; |
||||
|
||||
const BOLD = 1; |
||||
const ITALIC = 3; |
||||
const UNDERLINE = 4; |
||||
const BLINK = 5; |
||||
const NEGATIVE = 7; |
||||
const CONCEALED = 8; |
||||
const CROSSED_OUT = 9; |
||||
const FRAMED = 51; |
||||
const ENCIRCLED = 52; |
||||
const OVERLINED = 53; |
||||
|
||||
/** |
||||
* Moves the terminal cursor up by sending ANSI control code CUU to the terminal. |
||||
* If the cursor is already at the edge of the screen, this has no effect. |
||||
* @param integer $rows number of rows the cursor should be moved up |
||||
*/ |
||||
public static function moveCursorUp($rows=1) |
||||
{ |
||||
echo "\033[" . (int) $rows . 'A'; |
||||
} |
||||
|
||||
/** |
||||
* Moves the terminal cursor down by sending ANSI control code CUD to the terminal. |
||||
* If the cursor is already at the edge of the screen, this has no effect. |
||||
* @param integer $rows number of rows the cursor should be moved down |
||||
*/ |
||||
public static function moveCursorDown($rows=1) |
||||
{ |
||||
echo "\033[" . (int) $rows . 'B'; |
||||
} |
||||
|
||||
/** |
||||
* Moves the terminal cursor forward by sending ANSI control code CUF to the terminal. |
||||
* If the cursor is already at the edge of the screen, this has no effect. |
||||
* @param integer $steps number of steps the cursor should be moved forward |
||||
*/ |
||||
public static function moveCursorForward($steps=1) |
||||
{ |
||||
echo "\033[" . (int) $steps . 'C'; |
||||
} |
||||
|
||||
/** |
||||
* Moves the terminal cursor backward by sending ANSI control code CUB to the terminal. |
||||
* If the cursor is already at the edge of the screen, this has no effect. |
||||
* @param integer $steps number of steps the cursor should be moved backward |
||||
*/ |
||||
public static function moveCursorBackward($steps=1) |
||||
{ |
||||
echo "\033[" . (int) $steps . 'D'; |
||||
} |
||||
|
||||
/** |
||||
* Moves the terminal cursor to the beginning of the next line by sending ANSI control code CNL to the terminal. |
||||
* @param integer $lines number of lines the cursor should be moved down |
||||
*/ |
||||
public static function moveCursorNextLine($lines=1) |
||||
{ |
||||
echo "\033[" . (int) $lines . 'E'; |
||||
} |
||||
|
||||
/** |
||||
* Moves the terminal cursor to the beginning of the previous line by sending ANSI control code CPL to the terminal. |
||||
* @param integer $lines number of lines the cursor should be moved up |
||||
*/ |
||||
public static function moveCursorPrevLine($lines=1) |
||||
{ |
||||
echo "\033[" . (int) $lines . 'F'; |
||||
} |
||||
|
||||
/** |
||||
* Moves the cursor to an absolute position given as column and row by sending ANSI control code CUP or CHA to the terminal. |
||||
* @param integer $column 1-based column number, 1 is the left edge of the screen. |
||||
* @param integer|null $row 1-based row number, 1 is the top edge of the screen. if not set, will move cursor only in current line. |
||||
*/ |
||||
public static function moveCursorTo($column, $row=null) |
||||
{ |
||||
if ($row === null) { |
||||
echo "\033[" . (int) $column . 'G'; |
||||
} else { |
||||
echo "\033[" . (int) $row . ';' . (int) $column . 'H'; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Scrolls whole page up by sending ANSI control code SU to the terminal. |
||||
* New lines are added at the bottom. This is not supported by ANSI.SYS used in windows. |
||||
* @param int $lines number of lines to scroll up |
||||
*/ |
||||
public static function scrollUp($lines=1) |
||||
{ |
||||
echo "\033[".(int)$lines."S"; |
||||
} |
||||
|
||||
/** |
||||
* Scrolls whole page down by sending ANSI control code SD to the terminal. |
||||
* New lines are added at the top. This is not supported by ANSI.SYS used in windows. |
||||
* @param int $lines number of lines to scroll down |
||||
*/ |
||||
public static function scrollDown($lines=1) |
||||
{ |
||||
echo "\033[".(int)$lines."T"; |
||||
} |
||||
|
||||
/** |
||||
* Saves the current cursor position by sending ANSI control code SCP to the terminal. |
||||
* Position can then be restored with {@link restoreCursorPosition}. |
||||
*/ |
||||
public static function saveCursorPosition() |
||||
{ |
||||
echo "\033[s"; |
||||
} |
||||
|
||||
/** |
||||
* Restores the cursor position saved with {@link saveCursorPosition} by sending ANSI control code RCP to the terminal. |
||||
*/ |
||||
public static function restoreCursorPosition() |
||||
{ |
||||
echo "\033[u"; |
||||
} |
||||
|
||||
/** |
||||
* Hides the cursor by sending ANSI DECTCEM code ?25l to the terminal. |
||||
* Use {@link showCursor} to bring it back. |
||||
* Do not forget to show cursor when your application exits. Cursor might stay hidden in terminal after exit. |
||||
*/ |
||||
public static function hideCursor() |
||||
{ |
||||
echo "\033[?25l"; |
||||
} |
||||
|
||||
/** |
||||
* Will show a cursor again when it has been hidden by {@link hideCursor} by sending ANSI DECTCEM code ?25h to the terminal. |
||||
*/ |
||||
public static function showCursor() |
||||
{ |
||||
echo "\033[?25h"; |
||||
} |
||||
|
||||
/** |
||||
* Clears entire screen content by sending ANSI control code ED with argument 2 to the terminal. |
||||
* Cursor position will not be changed. |
||||
* **Note:** ANSI.SYS implementation used in windows will reset cursor position to upper left corner of the screen. |
||||
*/ |
||||
public static function clearScreen() |
||||
{ |
||||
echo "\033[2J"; |
||||
} |
||||
|
||||
/** |
||||
* Clears text from cursor to the beginning of the screen by sending ANSI control code ED with argument 1 to the terminal. |
||||
* Cursor position will not be changed. |
||||
*/ |
||||
public static function clearScreenBeforeCursor() |
||||
{ |
||||
echo "\033[1J"; |
||||
} |
||||
|
||||
/** |
||||
* Clears text from cursor to the end of the screen by sending ANSI control code ED with argument 0 to the terminal. |
||||
* Cursor position will not be changed. |
||||
*/ |
||||
public static function clearScreenAfterCursor() |
||||
{ |
||||
echo "\033[0J"; |
||||
} |
||||
|
||||
/** |
||||
* Clears the line, the cursor is currently on by sending ANSI control code EL with argument 2 to the terminal. |
||||
* Cursor position will not be changed. |
||||
*/ |
||||
public static function clearLine() |
||||
{ |
||||
echo "\033[2K"; |
||||
} |
||||
|
||||
/** |
||||
* Clears text from cursor position to the beginning of the line by sending ANSI control code EL with argument 1 to the terminal. |
||||
* Cursor position will not be changed. |
||||
*/ |
||||
public static function clearLineBeforeCursor() |
||||
{ |
||||
echo "\033[1K"; |
||||
} |
||||
|
||||
/** |
||||
* Clears text from cursor position to the end of the line by sending ANSI control code EL with argument 0 to the terminal. |
||||
* Cursor position will not be changed. |
||||
*/ |
||||
public static function clearLineAfterCursor() |
||||
{ |
||||
echo "\033[0K"; |
||||
} |
||||
|
||||
/** |
||||
* Will send ANSI format for following output |
||||
* |
||||
* You can pass any of the FG_*, BG_* and TEXT_* constants and also xterm256ColorBg |
||||
* TODO: documentation |
||||
*/ |
||||
public static function ansiStyle() |
||||
{ |
||||
echo "\033[" . implode(';', func_get_args()) . 'm'; |
||||
} |
||||
|
||||
/** |
||||
* Will return a string formatted with the given ANSI style |
||||
* |
||||
* See {@link ansiStyle} for possible arguments. |
||||
* @param string $string the string to be formatted |
||||
* @return string |
||||
*/ |
||||
public static function ansiStyleString($string) |
||||
{ |
||||
$args = func_get_args(); |
||||
array_shift($args); |
||||
$code = implode(';', $args); |
||||
return "\033[0m" . ($code !== '' ? "\033[" . $code . "m" : '') . $string."\033[0m"; |
||||
} |
||||
|
||||
//const COLOR_XTERM256 = 38;// http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors |
||||
public static function xterm256ColorFg($i) // TODO naming! |
||||
{ |
||||
return '38;5;'.$i; |
||||
} |
||||
|
||||
public static function xterm256ColorBg($i) // TODO naming! |
||||
{ |
||||
return '48;5;'.$i; |
||||
} |
||||
|
||||
/** |
||||
* Usage: list($w, $h) = ConsoleHelper::getScreenSize(); |
||||
* |
||||
* @return array |
||||
*/ |
||||
public static function getScreenSize() |
||||
{ |
||||
// TODO implement |
||||
return array(150,50); |
||||
} |
||||
|
||||
/** |
||||
* resets any ansi style set by previous method {@link ansiStyle} |
||||
* Any output after this is will have default text style. |
||||
*/ |
||||
public static function reset() |
||||
{ |
||||
echo "\033[0m"; |
||||
} |
||||
|
||||
/** |
||||
* Strips ANSI control codes from a string |
||||
* |
||||
* @param string $string String to strip |
||||
* @return string |
||||
*/ |
||||
public static function strip($string) |
||||
{ |
||||
return preg_replace('/\033\[[\d;]+m/', '', $string); // TODO currently only strips color |
||||
} |
||||
|
||||
// TODO refactor and review |
||||
public static function ansiToHtml($string) |
||||
{ |
||||
$tags = 0; |
||||
return preg_replace_callback('/\033\[[\d;]+m/', function($ansi) use (&$tags) { |
||||
$styleA = array(); |
||||
foreach(explode(';', $ansi) as $controlCode) |
||||
{ |
||||
switch($controlCode) |
||||
{ |
||||
case static::FG_BLACK: $style = array('color' => '#000000'); break; |
||||
case static::FG_BLUE: $style = array('color' => '#000078'); break; |
||||
case static::FG_CYAN: $style = array('color' => '#007878'); break; |
||||
case static::FG_GREEN: $style = array('color' => '#007800'); break; |
||||
case static::FG_GREY: $style = array('color' => '#787878'); break; |
||||
case static::FG_PURPLE: $style = array('color' => '#780078'); break; |
||||
case static::FG_RED: $style = array('color' => '#780000'); break; |
||||
case static::FG_YELLOW: $style = array('color' => '#787800'); break; |
||||
case static::BG_BLACK: $style = array('background-color' => '#000000'); break; |
||||
case static::BG_BLUE: $style = array('background-color' => '#000078'); break; |
||||
case static::BG_CYAN: $style = array('background-color' => '#007878'); break; |
||||
case static::BG_GREEN: $style = array('background-color' => '#007800'); break; |
||||
case static::BG_GREY: $style = array('background-color' => '#787878'); break; |
||||
case static::BG_PURPLE: $style = array('background-color' => '#780078'); break; |
||||
case static::BG_RED: $style = array('background-color' => '#780000'); break; |
||||
case static::BG_YELLOW: $style = array('background-color' => '#787800'); break; |
||||
case static::BOLD: $style = array('font-weight' => 'bold'); break; |
||||
case static::ITALIC: $style = array('font-style' => 'italic'); break; |
||||
case static::UNDERLINE: $style = array('text-decoration' => array('underline')); break; |
||||
case static::OVERLINED: $style = array('text-decoration' => array('overline')); break; |
||||
case static::CROSSED_OUT:$style = array('text-decoration' => array('line-through')); break; |
||||
case static::BLINK: $style = array('text-decoration' => array('blink')); break; |
||||
case static::NEGATIVE: // ??? |
||||
case static::CONCEALED: |
||||
case static::ENCIRCLED: |
||||
case static::FRAMED: |
||||
// TODO allow resetting codes |
||||
break; |
||||
case 0: // ansi reset |
||||
$return = ''; |
||||
for($n=$tags; $tags>0; $tags--) { |
||||
$return .= '</span>'; |
||||
} |
||||
return $return; |
||||
} |
||||
|
||||
$styleA = ArrayHelper::merge($styleA, $style); |
||||
} |
||||
$styleString[] = array(); |
||||
foreach($styleA as $name => $content) { |
||||
if ($name === 'text-decoration') { |
||||
$content = implode(' ', $content); |
||||
} |
||||
$styleString[] = $name.':'.$content; |
||||
} |
||||
$tags++; |
||||
return '<span' . (!empty($styleString) ? 'style="' . implode(';', $styleString) : '') . '>'; |
||||
}, $string); |
||||
} |
||||
|
||||
/** |
||||
* TODO syntax copied from https://github.com/pear/Console_Color2/blob/master/Console/Color2.php |
||||
* |
||||
* Converts colorcodes in the format %y (for yellow) into ansi-control |
||||
* codes. The conversion table is: ('bold' meaning 'light' on some |
||||
* terminals). It's almost the same conversion table irssi uses. |
||||
* <pre> |
||||
* text text background |
||||
* ------------------------------------------------ |
||||
* %k %K %0 black dark grey black |
||||
* %r %R %1 red bold red red |
||||
* %g %G %2 green bold green green |
||||
* %y %Y %3 yellow bold yellow yellow |
||||
* %b %B %4 blue bold blue blue |
||||
* %m %M %5 magenta bold magenta magenta |
||||
* %p %P magenta (think: purple) |
||||
* %c %C %6 cyan bold cyan cyan |
||||
* %w %W %7 white bold white white |
||||
* |
||||
* %F Blinking, Flashing |
||||
* %U Underline |
||||
* %8 Reverse |
||||
* %_,%9 Bold |
||||
* |
||||
* %n Resets the color |
||||
* %% A single % |
||||
* </pre> |
||||
* First param is the string to convert, second is an optional flag if |
||||
* colors should be used. It defaults to true, if set to false, the |
||||
* colorcodes will just be removed (And %% will be transformed into %) |
||||
* |
||||
* @param string $string String to convert |
||||
* @param bool $colored Should the string be colored? |
||||
* |
||||
* @return string |
||||
*/ |
||||
public static function renderColoredString($string) |
||||
{ |
||||
$colored = true; |
||||
|
||||
|
||||
static $conversions = array ( // static so the array doesn't get built |
||||
// everytime |
||||
// %y - yellow, and so on... {{{ |
||||
'%y' => array('color' => 'yellow'), |
||||
'%g' => array('color' => 'green' ), |
||||
'%b' => array('color' => 'blue' ), |
||||
'%r' => array('color' => 'red' ), |
||||
'%p' => array('color' => 'purple'), |
||||
'%m' => array('color' => 'purple'), |
||||
'%c' => array('color' => 'cyan' ), |
||||
'%w' => array('color' => 'grey' ), |
||||
'%k' => array('color' => 'black' ), |
||||
'%n' => array('color' => 'reset' ), |
||||
'%Y' => array('color' => 'yellow', 'style' => 'light'), |
||||
'%G' => array('color' => 'green', 'style' => 'light'), |
||||
'%B' => array('color' => 'blue', 'style' => 'light'), |
||||
'%R' => array('color' => 'red', 'style' => 'light'), |
||||
'%P' => array('color' => 'purple', 'style' => 'light'), |
||||
'%M' => array('color' => 'purple', 'style' => 'light'), |
||||
'%C' => array('color' => 'cyan', 'style' => 'light'), |
||||
'%W' => array('color' => 'grey', 'style' => 'light'), |
||||
'%K' => array('color' => 'black', 'style' => 'light'), |
||||
'%N' => array('color' => 'reset', 'style' => 'light'), |
||||
'%3' => array('background' => 'yellow'), |
||||
'%2' => array('background' => 'green' ), |
||||
'%4' => array('background' => 'blue' ), |
||||
'%1' => array('background' => 'red' ), |
||||
'%5' => array('background' => 'purple'), |
||||
'%6' => array('background' => 'cyan' ), |
||||
'%7' => array('background' => 'grey' ), |
||||
'%0' => array('background' => 'black' ), |
||||
// Don't use this, I can't stand flashing text |
||||
'%F' => array('style' => 'blink'), |
||||
'%U' => array('style' => 'underline'), |
||||
'%8' => array('style' => 'inverse'), |
||||
'%9' => array('style' => 'bold'), |
||||
'%_' => array('style' => 'bold') |
||||
// }}} |
||||
); |
||||
|
||||
if ($colored) { |
||||
$string = str_replace('%%', '% ', $string); |
||||
foreach ($conversions as $key => $value) { |
||||
$string = str_replace($key, Console_Color::color($value), |
||||
$string); |
||||
} |
||||
$string = str_replace('% ', '%', $string); |
||||
|
||||
} else { |
||||
$string = preg_replace('/%((%)|.)/', '$2', $string); |
||||
} |
||||
|
||||
return $string; |
||||
} |
||||
|
||||
/** |
||||
* Escapes % so they don't get interpreted as color codes |
||||
* |
||||
* @param string $string String to escape |
||||
* |
||||
* @access public |
||||
* @return string |
||||
*/ |
||||
public static function escape($string) |
||||
{ |
||||
return str_replace('%', '%%', $string); |
||||
} |
||||
} |
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<phpunit bootstrap="./tests/unit/bootstrap.php" |
||||
colors="true" |
||||
convertErrorsToExceptions="true" |
||||
convertNoticesToExceptions="true" |
||||
convertWarningsToExceptions="true" |
||||
stopOnFailure="false"> |
||||
<testsuites> |
||||
<testsuite name="Yii Test Suite"> |
||||
<directory>./tests/unit</directory> |
||||
</testsuite> |
||||
</testsuites> |
||||
</phpunit> |
@ -0,0 +1,50 @@
|
||||
<?php |
||||
|
||||
namespace yiiunit; |
||||
|
||||
class DatabaseTestCase extends TestCase |
||||
{ |
||||
protected $database; |
||||
protected $driverName = 'mysql'; |
||||
protected $db; |
||||
|
||||
protected function setUp() |
||||
{ |
||||
$databases = $this->getParam('databases'); |
||||
$this->database = $databases[$this->driverName]; |
||||
$pdo_database = 'pdo_'.$this->driverName; |
||||
|
||||
if (!extension_loaded('pdo') || !extension_loaded($pdo_database)) { |
||||
$this->markTestSkipped('pdo and pdo_'.$pdo_database.' extension are required.'); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @param bool $reset whether to clean up the test database |
||||
* @param bool $open whether to open and populate test database |
||||
* @return \yii\db\Connection |
||||
*/ |
||||
public function getConnection($reset = true, $open = true) |
||||
{ |
||||
if (!$reset && $this->db) { |
||||
return $this->db; |
||||
} |
||||
$db = new \yii\db\Connection; |
||||
$db->dsn = $this->database['dsn']; |
||||
if (isset($this->database['username'])) { |
||||
$db->username = $this->database['username']; |
||||
$db->password = $this->database['password']; |
||||
} |
||||
if ($open) { |
||||
$db->open(); |
||||
$lines = explode(';', file_get_contents($this->database['fixture'])); |
||||
foreach ($lines as $line) { |
||||
if (trim($line) !== '') { |
||||
$db->pdo->exec($line); |
||||
} |
||||
} |
||||
} |
||||
$this->db = $db; |
||||
return $db; |
||||
} |
||||
} |
@ -1,10 +1,16 @@
|
||||
<?php |
||||
|
||||
return array( |
||||
'mysql' => array( |
||||
'dsn' => 'mysql:host=127.0.0.1;dbname=yiitest', |
||||
'username' => 'root', |
||||
'password' => '', |
||||
'fixture' => __DIR__ . '/mysql.sql', |
||||
), |
||||
'databases' => array( |
||||
'mysql' => array( |
||||
'dsn' => 'mysql:host=127.0.0.1;dbname=yiitest', |
||||
'username' => 'travis', |
||||
'password' => '', |
||||
'fixture' => __DIR__ . '/mysql.sql', |
||||
), |
||||
'sqlite' => array( |
||||
'dsn' => 'sqlite::memory:', |
||||
'fixture' => __DIR__ . '/sqlite.sql', |
||||
), |
||||
) |
||||
); |
||||
|
Binary file not shown.
@ -0,0 +1,64 @@
|
||||
msgid "" |
||||
msgstr "" |
||||
"Project-Id-Version: \n" |
||||
"POT-Creation-Date: \n" |
||||
"PO-Revision-Date: \n" |
||||
"Last-Translator: resurtm <resurtm@gmail.com>\n" |
||||
"Language-Team: \n" |
||||
"MIME-Version: 1.0\n" |
||||
"Content-Type: text/plain; charset=UTF-8\n" |
||||
"Content-Transfer-Encoding: 8bit\n" |
||||
"X-Generator: Poedit 1.5.5\n" |
||||
|
||||
msgctxt "context1" |
||||
msgid "" |
||||
"Aliquam tempus elit vel purus molestie placerat. In sollicitudin tincidunt\n" |
||||
"aliquet. Integer tincidunt gravida tempor. In convallis blandit dui vel " |
||||
"malesuada.\n" |
||||
"Nunc vel sapien nunc, a pretium nulla." |
||||
msgstr "" |
||||
"Олицетворение однократно. Представленный лексико-семантический анализ " |
||||
"является\n" |
||||
"психолингвистическим в своей основе, но механизм сочленений полидисперсен. " |
||||
"Впечатление\n" |
||||
"однократно. Различное расположение выбирает сюжетный механизм сочленений." |
||||
|
||||
msgctxt "context1" |
||||
msgid "String number two." |
||||
msgstr "Строка номер два." |
||||
|
||||
msgctxt "context2" |
||||
msgid "" |
||||
"The other\n" |
||||
"\n" |
||||
"context.\n" |
||||
msgstr "" |
||||
"Другой\n" |
||||
"\n" |
||||
"контекст.\n" |
||||
|
||||
msgctxt "context1" |
||||
msgid "" |
||||
"Missing\n" |
||||
"\r\t\"translation." |
||||
msgstr "" |
||||
|
||||
msgctxt "context1" |
||||
msgid "" |
||||
"Nunc vel sapien nunc, a pretium nulla.\n" |
||||
"Pellentesque habitant morbi tristique senectus et netus et malesuada fames " |
||||
"ac turpis egestas." |
||||
msgstr "Короткий перевод." |
||||
|
||||
msgid "contextless" |
||||
msgstr "" |
||||
|
||||
msgctxt "context2" |
||||
msgid "" |
||||
"test1\\ntest2\n" |
||||
"\\\n" |
||||
"test3" |
||||
msgstr "" |
||||
"тест1\\nтест2\n" |
||||
"\\\n" |
||||
"тест3" |
@ -1,262 +1,88 @@
|
||||
CREATE TABLE users |
||||
( |
||||
id INTEGER NOT NULL PRIMARY KEY, |
||||
username VARCHAR(128) NOT NULL, |
||||
password VARCHAR(128) NOT NULL, |
||||
email VARCHAR(128) NOT NULL |
||||
); |
||||
|
||||
INSERT INTO users(id,username,password,email) VALUES (1,'user1','pass1','email1'); |
||||
INSERT INTO users(id,username,password,email) VALUES (2,'user2','pass2','email2'); |
||||
INSERT INTO users(id,username,password,email) VALUES (3,'user3','pass3','email3'); |
||||
INSERT INTO users(id,username,password,email) VALUES (4,'user4','pass4','email4'); |
||||
|
||||
CREATE TABLE groups |
||||
( |
||||
id INTEGER NOT NULL PRIMARY KEY, |
||||
name VARCHAR(128) NOT NULL |
||||
); |
||||
|
||||
INSERT INTO groups(id,name) VALUES (1,'group1'); |
||||
INSERT INTO groups(id,name) VALUES (2,'group2'); |
||||
INSERT INTO groups(id,name) VALUES (3,'group3'); |
||||
INSERT INTO groups(id,name) VALUES (4,'group4'); |
||||
INSERT INTO groups(id,name) VALUES (5,'group5'); |
||||
INSERT INTO groups(id,name) VALUES (6,'group6'); |
||||
|
||||
CREATE TABLE groups_descriptions |
||||
( |
||||
group_id INTEGER NOT NULL PRIMARY KEY, |
||||
name VARCHAR(128) NOT NULL |
||||
); |
||||
|
||||
INSERT INTO groups_descriptions(group_id,name) VALUES (1,'room1'); |
||||
INSERT INTO groups_descriptions(group_id,name) VALUES (2,'room2'); |
||||
INSERT INTO groups_descriptions(group_id,name) VALUES (3,'room3'); |
||||
INSERT INTO groups_descriptions(group_id,name) VALUES (4,'room4'); |
||||
|
||||
CREATE TABLE roles |
||||
( |
||||
user_id INTEGER NOT NULL, |
||||
group_id INTEGER NOT NULL, |
||||
name VARCHAR(128) NOT NULL, |
||||
PRIMARY KEY(user_id,group_id) |
||||
); |
||||
|
||||
INSERT INTO roles(user_id,group_id,name) VALUES (1,1,'dev'); |
||||
INSERT INTO roles(user_id,group_id,name) VALUES (1,2,'user'); |
||||
INSERT INTO roles(user_id,group_id,name) VALUES (2,1,'dev'); |
||||
INSERT INTO roles(user_id,group_id,name) VALUES (2,3,'user'); |
||||
|
||||
CREATE TABLE mentorships |
||||
( |
||||
teacher_id INTEGER NOT NULL, |
||||
student_id INTEGER NOT NULL, |
||||
progress VARCHAR(128) NOT NULL, |
||||
PRIMARY KEY(teacher_id,student_id) |
||||
); |
||||
|
||||
INSERT INTO mentorships(teacher_id,student_id,progress) VALUES (1,3,'good'); |
||||
INSERT INTO mentorships(teacher_id,student_id,progress) VALUES (2,4,'average'); |
||||
|
||||
CREATE TABLE profiles |
||||
( |
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, |
||||
first_name VARCHAR(128) NOT NULL, |
||||
last_name VARCHAR(128) NOT NULL, |
||||
user_id INTEGER NOT NULL, |
||||
CONSTRAINT FK_profile_user FOREIGN KEY (user_id) |
||||
REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT |
||||
); |
||||
|
||||
INSERT INTO profiles (first_name, last_name, user_id) VALUES ('first 1','last 1',1); |
||||
INSERT INTO profiles (first_name, last_name, user_id) VALUES ('first 2','last 2',2); |
||||
|
||||
CREATE TABLE posts |
||||
( |
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, |
||||
title VARCHAR(128) NOT NULL, |
||||
create_time TIMESTAMP NOT NULL, |
||||
author_id INTEGER NOT NULL, |
||||
content TEXT, |
||||
CONSTRAINT FK_post_author FOREIGN KEY (author_id) |
||||
REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT |
||||
); |
||||
|
||||
INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 1',100000,1,'content 1'); |
||||
INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 2',100001,2,'content 2'); |
||||
INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 3',100002,2,'content 3'); |
||||
INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 4',100003,2,'content 4'); |
||||
INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 5',100004,3,'content 5'); |
||||
|
||||
|
||||
CREATE TABLE posts_nofk |
||||
( |
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, |
||||
title VARCHAR(128) NOT NULL, |
||||
create_time TIMESTAMP NOT NULL, |
||||
author_id INTEGER NOT NULL, |
||||
content TEXT |
||||
); |
||||
|
||||
INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 1',100000,1,'content 1'); |
||||
INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 2',100001,2,'content 2'); |
||||
INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 3',100002,2,'content 3'); |
||||
INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 4',100003,2,'content 4'); |
||||
INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 5',100004,3,'content 5'); |
||||
|
||||
|
||||
CREATE TABLE comments |
||||
( |
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, |
||||
content TEXT NOT NULL, |
||||
post_id INTEGER NOT NULL, |
||||
author_id INTEGER NOT NULL, |
||||
CONSTRAINT FK_post_comment FOREIGN KEY (post_id) |
||||
REFERENCES posts (id) ON DELETE CASCADE ON UPDATE RESTRICT, |
||||
CONSTRAINT FK_user_comment FOREIGN KEY (author_id) |
||||
REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT |
||||
); |
||||
|
||||
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 1',1, 2); |
||||
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 2',1, 2); |
||||
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 3',1, 2); |
||||
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 4',2, 2); |
||||
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 5',2, 2); |
||||
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 6',3, 2); |
||||
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 7',3, 2); |
||||
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 8',3, 2); |
||||
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 9',3, 2); |
||||
INSERT INTO comments (content, post_id, author_id) VALUES ('comment 10',5, 3); |
||||
|
||||
CREATE TABLE categories |
||||
( |
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, |
||||
name VARCHAR(128) NOT NULL, |
||||
parent_id INTEGER, |
||||
CONSTRAINT FK_category_category FOREIGN KEY (parent_id) |
||||
REFERENCES categories (id) ON DELETE CASCADE ON UPDATE RESTRICT |
||||
); |
||||
|
||||
INSERT INTO categories (name, parent_id) VALUES ('cat 1',NULL); |
||||
INSERT INTO categories (name, parent_id) VALUES ('cat 2',NULL); |
||||
INSERT INTO categories (name, parent_id) VALUES ('cat 3',NULL); |
||||
INSERT INTO categories (name, parent_id) VALUES ('cat 4',1); |
||||
INSERT INTO categories (name, parent_id) VALUES ('cat 5',1); |
||||
INSERT INTO categories (name, parent_id) VALUES ('cat 6',5); |
||||
INSERT INTO categories (name, parent_id) VALUES ('cat 7',5); |
||||
|
||||
CREATE TABLE post_category |
||||
( |
||||
category_id INTEGER NOT NULL, |
||||
post_id INTEGER NOT NULL, |
||||
PRIMARY KEY (category_id, post_id), |
||||
CONSTRAINT FK_post_category_post FOREIGN KEY (post_id) |
||||
REFERENCES posts (id) ON DELETE CASCADE ON UPDATE RESTRICT, |
||||
CONSTRAINT FK_post_category_category FOREIGN KEY (category_id) |
||||
REFERENCES categories (id) ON DELETE CASCADE ON UPDATE RESTRICT |
||||
); |
||||
|
||||
INSERT INTO post_category (category_id, post_id) VALUES (1,1); |
||||
INSERT INTO post_category (category_id, post_id) VALUES (2,1); |
||||
INSERT INTO post_category (category_id, post_id) VALUES (3,1); |
||||
INSERT INTO post_category (category_id, post_id) VALUES (4,2); |
||||
INSERT INTO post_category (category_id, post_id) VALUES (1,2); |
||||
INSERT INTO post_category (category_id, post_id) VALUES (1,3); |
||||
|
||||
CREATE TABLE orders |
||||
( |
||||
key1 INTEGER NOT NULL, |
||||
key2 INTEGER NOT NULL, |
||||
name VARCHAR(128), |
||||
PRIMARY KEY (key1, key2) |
||||
); |
||||
|
||||
INSERT INTO orders (key1,key2,name) VALUES (1,2,'order 12'); |
||||
INSERT INTO orders (key1,key2,name) VALUES (1,3,'order 13'); |
||||
INSERT INTO orders (key1,key2,name) VALUES (2,1,'order 21'); |
||||
INSERT INTO orders (key1,key2,name) VALUES (2,2,'order 22'); |
||||
|
||||
CREATE TABLE items |
||||
( |
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, |
||||
name VARCHAR(128), |
||||
col1 INTEGER NOT NULL, |
||||
col2 INTEGER NOT NULL, |
||||
CONSTRAINT FK_order_item FOREIGN KEY (col1,col2) |
||||
REFERENCES orders (key1,key2) ON DELETE CASCADE ON UPDATE RESTRICT |
||||
); |
||||
|
||||
INSERT INTO items (name,col1,col2) VALUES ('item 1',1,2); |
||||
INSERT INTO items (name,col1,col2) VALUES ('item 2',1,2); |
||||
INSERT INTO items (name,col1,col2) VALUES ('item 3',1,3); |
||||
INSERT INTO items (name,col1,col2) VALUES ('item 4',2,2); |
||||
INSERT INTO items (name,col1,col2) VALUES ('item 5',2,2); |
||||
|
||||
CREATE TABLE types |
||||
( |
||||
int_col INT NOT NULL, |
||||
int_col2 INTEGER DEFAULT 1, |
||||
char_col CHAR(100) NOT NULL, |
||||
char_col2 VARCHAR(100) DEFAULT 'something', |
||||
char_col3 TEXT, |
||||
float_col REAL(4,3) NOT NULL, |
||||
float_col2 DOUBLE DEFAULT 1.23, |
||||
blob_col BLOB, |
||||
numeric_col NUMERIC(5,2) DEFAULT 33.22, |
||||
time TIMESTAMP DEFAULT 123, |
||||
bool_col BOOL NOT NULL, |
||||
bool_col2 BOOLEAN DEFAULT 1, |
||||
null_col INTEGER DEFAULT NULL |
||||
); |
||||
|
||||
CREATE TABLE Content |
||||
( |
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, |
||||
class VARCHAR(128), |
||||
parentID INTEGER NOT NULL, |
||||
ownerID INTEGER NOT NULL, |
||||
title VARCHAR(100), |
||||
CONSTRAINT FK_content_user FOREIGN KEY (ownerID) |
||||
REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT |
||||
CONSTRAINT FK_content_parent FOREIGN KEY (parentID) |
||||
REFERENCES Content (id) ON DELETE CASCADE ON UPDATE RESTRICT |
||||
); |
||||
|
||||
INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Article',-1,1,'article 1'); |
||||
INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Article',-1,2,'article 2'); |
||||
INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Comment',1,1,'comment 1'); |
||||
INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Article',-1,2,'article 3'); |
||||
INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Comment',4,2,'comment 2'); |
||||
INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Comment',4,1,'comment 3'); |
||||
|
||||
CREATE TABLE Article |
||||
( |
||||
id INTEGER NOT NULL PRIMARY KEY, |
||||
authorID INTEGER NOT NULL, |
||||
body TEXT, |
||||
CONSTRAINT FK_article_content FOREIGN KEY (id) |
||||
REFERENCES Content (id) ON DELETE CASCADE ON UPDATE RESTRICT |
||||
CONSTRAINT FK_article_author FOREIGN KEY (authorID) |
||||
REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT |
||||
); |
||||
|
||||
INSERT INTO Article (id,authorID,body) VALUES (1,1,'content for article 1'); |
||||
INSERT INTO Article (id,authorID,body) VALUES (2,2,'content for article 2'); |
||||
INSERT INTO Article (id,authorID,body) VALUES (4,1,'content for article 3'); |
||||
|
||||
CREATE TABLE Comment |
||||
( |
||||
id INTEGER NOT NULL PRIMARY KEY, |
||||
authorID INTEGER NOT NULL, |
||||
body TEXT, |
||||
CONSTRAINT FK_comment_content FOREIGN KEY (id) |
||||
REFERENCES Content (id) ON DELETE CASCADE ON UPDATE RESTRICT |
||||
CONSTRAINT FK_article_author FOREIGN KEY (authorID) |
||||
REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT |
||||
); |
||||
|
||||
INSERT INTO Comment (id,authorID,body) VALUES (3,1,'content for comment 1'); |
||||
INSERT INTO Comment (id,authorID,body) VALUES (5,1,'content for comment 2'); |
||||
INSERT INTO Comment (id,authorID,body) VALUES (6,1,'content for comment 3'); |
||||
|
||||
/** |
||||
* This is the database schema for testing Sqlite support of Yii DAO and Active Record. |
||||
* The database setup in config.php is required to perform then relevant tests: |
||||
*/ |
||||
|
||||
DROP TABLE IF EXISTS tbl_order_item; |
||||
DROP TABLE IF EXISTS tbl_item; |
||||
DROP TABLE IF EXISTS tbl_order; |
||||
DROP TABLE IF EXISTS tbl_category; |
||||
DROP TABLE IF EXISTS tbl_customer; |
||||
DROP TABLE IF EXISTS tbl_type; |
||||
|
||||
CREATE TABLE tbl_customer ( |
||||
id INTEGER NOT NULL, |
||||
email varchar(128) NOT NULL, |
||||
name varchar(128) NOT NULL, |
||||
address text, |
||||
status INTEGER DEFAULT 0, |
||||
PRIMARY KEY (id) |
||||
); |
||||
|
||||
CREATE TABLE tbl_category ( |
||||
id INTEGER NOT NULL, |
||||
name varchar(128) NOT NULL, |
||||
PRIMARY KEY (id) |
||||
); |
||||
|
||||
CREATE TABLE tbl_item ( |
||||
id INTEGER NOT NULL, |
||||
name varchar(128) NOT NULL, |
||||
category_id INTEGER NOT NULL, |
||||
PRIMARY KEY (id) |
||||
); |
||||
|
||||
CREATE TABLE tbl_order ( |
||||
id INTEGER NOT NULL, |
||||
customer_id INTEGER NOT NULL, |
||||
create_time INTEGER NOT NULL, |
||||
total decimal(10,0) NOT NULL, |
||||
PRIMARY KEY (id) |
||||
); |
||||
|
||||
CREATE TABLE tbl_order_item ( |
||||
order_id INTEGER NOT NULL, |
||||
item_id INTEGER NOT NULL, |
||||
quantity INTEGER NOT NULL, |
||||
subtotal decimal(10,0) NOT NULL, |
||||
PRIMARY KEY (order_id, item_id) |
||||
); |
||||
|
||||
CREATE TABLE tbl_type ( |
||||
int_col INTEGER NOT NULL, |
||||
int_col2 INTEGER DEFAULT '1', |
||||
char_col char(100) NOT NULL, |
||||
char_col2 varchar(100) DEFAULT 'something', |
||||
char_col3 text, |
||||
float_col double(4,3) NOT NULL, |
||||
float_col2 double DEFAULT '1.23', |
||||
blob_col blob, |
||||
numeric_col decimal(5,2) DEFAULT '33.22', |
||||
time timestamp NOT NULL DEFAULT '2002-01-01 00:00:00', |
||||
bool_col tinyint(1) NOT NULL, |
||||
bool_col2 tinyint(1) DEFAULT '1' |
||||
); |
||||
|
||||
INSERT INTO tbl_customer (email, name, address, status) VALUES ('user1@example.com', 'user1', 'address1', 1); |
||||
INSERT INTO tbl_customer (email, name, address, status) VALUES ('user2@example.com', 'user2', 'address2', 1); |
||||
INSERT INTO tbl_customer (email, name, address, status) VALUES ('user3@example.com', 'user3', 'address3', 2); |
||||
|
||||
INSERT INTO tbl_category (name) VALUES ('Books'); |
||||
INSERT INTO tbl_category (name) VALUES ('Movies'); |
||||
|
||||
INSERT INTO tbl_item (name, category_id) VALUES ('Agile Web Application Development with Yii1.1 and PHP5', 1); |
||||
INSERT INTO tbl_item (name, category_id) VALUES ('Yii 1.1 Application Development Cookbook', 1); |
||||
INSERT INTO tbl_item (name, category_id) VALUES ('Ice Age', 2); |
||||
INSERT INTO tbl_item (name, category_id) VALUES ('Toy Story', 2); |
||||
INSERT INTO tbl_item (name, category_id) VALUES ('Cars', 2); |
||||
|
||||
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (1, 1325282384, 110.0); |
||||
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (2, 1325334482, 33.0); |
||||
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (2, 1325502201, 40.0); |
||||
|
||||
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 1, 1, 30.0); |
||||
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 2, 2, 40.0); |
||||
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 4, 1, 10.0); |
||||
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 5, 1, 15.0); |
||||
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 3, 1, 8.0); |
||||
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (3, 2, 1, 40.0); |
@ -0,0 +1,12 @@
|
||||
<?php |
||||
|
||||
namespace yiiunit\framework\db\sqlite; |
||||
|
||||
class SqliteActiveRecordTest extends \yiiunit\framework\db\ActiveRecordTest |
||||
{ |
||||
public function setUp() |
||||
{ |
||||
$this->driverName = 'sqlite'; |
||||
parent::setUp(); |
||||
} |
||||
} |
@ -0,0 +1,21 @@
|
||||
<?php |
||||
|
||||
namespace yiiunit\framework\db\sqlite; |
||||
|
||||
class SqliteCommandTest extends \yiiunit\framework\db\CommandTest |
||||
{ |
||||
public function setUp() |
||||
{ |
||||
$this->driverName = 'sqlite'; |
||||
parent::setUp(); |
||||
} |
||||
|
||||
function testAutoQuoting() |
||||
{ |
||||
$db = $this->getConnection(false); |
||||
|
||||
$sql = 'SELECT [[id]], [[t.name]] FROM {{tbl_customer}} t'; |
||||
$command = $db->createCommand($sql); |
||||
$this->assertEquals("SELECT \"id\", 't'.\"name\" FROM 'tbl_customer' t", $command->sql); |
||||
} |
||||
} |
@ -0,0 +1,47 @@
|
||||
<?php |
||||
|
||||
namespace yiiunit\framework\db\sqlite; |
||||
|
||||
class SqliteConnectionTest extends \yiiunit\framework\db\ConnectionTest |
||||
{ |
||||
public function setUp() |
||||
{ |
||||
$this->driverName = 'sqlite'; |
||||
parent::setUp(); |
||||
} |
||||
|
||||
function testConstruct() |
||||
{ |
||||
$connection = $this->getConnection(false); |
||||
$params = $this->database; |
||||
|
||||
$this->assertEquals($params['dsn'], $connection->dsn); |
||||
} |
||||
|
||||
function testQuoteValue() |
||||
{ |
||||
$connection = $this->getConnection(false); |
||||
$this->assertEquals(123, $connection->quoteValue(123)); |
||||
$this->assertEquals("'string'", $connection->quoteValue('string')); |
||||
$this->assertEquals("'It''s interesting'", $connection->quoteValue("It's interesting")); |
||||
} |
||||
|
||||
function testQuoteTableName() |
||||
{ |
||||
$connection = $this->getConnection(false); |
||||
$this->assertEquals("'table'", $connection->quoteTableName('table')); |
||||
$this->assertEquals("'schema'.'table'", $connection->quoteTableName('schema.table')); |
||||
$this->assertEquals('{{table}}', $connection->quoteTableName('{{table}}')); |
||||
$this->assertEquals('(table)', $connection->quoteTableName('(table)')); |
||||
} |
||||
|
||||
function testQuoteColumnName() |
||||
{ |
||||
$connection = $this->getConnection(false); |
||||
$this->assertEquals('"column"', $connection->quoteColumnName('column')); |
||||
$this->assertEquals("'table'.\"column\"", $connection->quoteColumnName('table.column')); |
||||
$this->assertEquals('[[column]]', $connection->quoteColumnName('[[column]]')); |
||||
$this->assertEquals('{{column}}', $connection->quoteColumnName('{{column}}')); |
||||
$this->assertEquals('(column)', $connection->quoteColumnName('(column)')); |
||||
} |
||||
} |
@ -0,0 +1,20 @@
|
||||
<?php |
||||
/** |
||||
* Created by JetBrains PhpStorm. |
||||
* User: RusMaxim |
||||
* Date: 09.05.13 |
||||
* Time: 21:41 |
||||
* To change this template use File | Settings | File Templates. |
||||
*/ |
||||
|
||||
namespace yiiunit\framework\db\sqlite; |
||||
|
||||
|
||||
class SqliteQueryTest extends \yiiunit\framework\db\QueryTest |
||||
{ |
||||
public function setUp() |
||||
{ |
||||
$this->driverName = 'sqlite'; |
||||
parent::setUp(); |
||||
} |
||||
} |
@ -0,0 +1,12 @@
|
||||
<?php |
||||
namespace yiiunit\framework\helpers; |
||||
use \yii\helpers\VarDumper; |
||||
|
||||
class VarDumperTest extends \yii\test\TestCase |
||||
{ |
||||
public function testDumpObject() |
||||
{ |
||||
$obj = new \StdClass(); |
||||
VarDumper::dump($obj); |
||||
} |
||||
} |
@ -0,0 +1,14 @@
|
||||
<?php |
||||
|
||||
namespace yiiunit\framework\i18n; |
||||
|
||||
use yii\i18n\GettextMessageSource; |
||||
use yiiunit\TestCase; |
||||
|
||||
class GettextMessageSourceTest extends TestCase |
||||
{ |
||||
public function testLoadMessages() |
||||
{ |
||||
$this->markTestSkipped(); |
||||
} |
||||
} |
@ -0,0 +1,95 @@
|
||||
<?php |
||||
|
||||
namespace yiiunit\framework\i18n; |
||||
|
||||
use yii\i18n\GettextMoFile; |
||||
use yiiunit\TestCase; |
||||
|
||||
class GettextMoFileTest extends TestCase |
||||
{ |
||||
public function testLoad() |
||||
{ |
||||
$moFile = new GettextMoFile(); |
||||
$moFilePath = __DIR__ . '/../../data/i18n/test.mo'; |
||||
$context1 = $moFile->load($moFilePath, 'context1'); |
||||
$context2 = $moFile->load($moFilePath, 'context2'); |
||||
|
||||
// item count |
||||
$this->assertCount(3, $context1); |
||||
$this->assertCount(2, $context2); |
||||
|
||||
// original messages |
||||
$this->assertArrayNotHasKey("Missing\n\r\t\"translation.", $context1); |
||||
$this->assertArrayHasKey("Aliquam tempus elit vel purus molestie placerat. In sollicitudin tincidunt\naliquet. Integer tincidunt gravida tempor. In convallis blandit dui vel malesuada.\nNunc vel sapien nunc, a pretium nulla.", $context1); |
||||
$this->assertArrayHasKey("String number two.", $context1); |
||||
$this->assertArrayHasKey("Nunc vel sapien nunc, a pretium nulla.\nPellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.", $context1); |
||||
|
||||
$this->assertArrayHasKey("The other\n\ncontext.\n", $context2); |
||||
$this->assertArrayHasKey("test1\\ntest2\n\\\ntest3", $context2); |
||||
|
||||
// translated messages |
||||
$this->assertFalse(in_array("", $context1)); |
||||
$this->assertTrue(in_array("Олицетворение однократно. Представленный лексико-семантический анализ является\nпсихолингвистическим в своей основе, но механизм сочленений полидисперсен. Впечатление\nоднократно. Различное расположение выбирает сюжетный механизм сочленений.", $context1)); |
||||
$this->assertTrue(in_array('Строка номер два.', $context1)); |
||||
$this->assertTrue(in_array('Короткий перевод.', $context1)); |
||||
|
||||
$this->assertTrue(in_array("Другой\n\nконтекст.\n", $context2)); |
||||
$this->assertTrue(in_array("тест1\\nтест2\n\\\nтест3", $context2)); |
||||
} |
||||
|
||||
public function testSave() |
||||
{ |
||||
// initial data |
||||
$s = chr(4); |
||||
$messages = array( |
||||
'Hello!' => 'Привет!', |
||||
"context1{$s}Hello?" => 'Привет?', |
||||
'Hello!?' => '', |
||||
"context1{$s}Hello!?!" => '', |
||||
"context2{$s}\"Quotes\"" => '"Кавычки"', |
||||
"context2{$s}\nNew lines\n" => "\nПереносы строк\n", |
||||
"context2{$s}\tTabs\t" => "\tТабы\t", |
||||
"context2{$s}\rCarriage returns\r" => "\rВозвраты кареток\r", |
||||
); |
||||
|
||||
// create temporary directory and dump messages |
||||
$poFileDirectory = __DIR__ . '/../../runtime/i18n'; |
||||
if (!is_dir($poFileDirectory)) { |
||||
mkdir($poFileDirectory); |
||||
} |
||||
if (is_file($poFileDirectory . '/test.mo')) { |
||||
unlink($poFileDirectory . '/test.mo'); |
||||
} |
||||
|
||||
$moFile = new GettextMoFile(); |
||||
$moFile->save($poFileDirectory . '/test.mo', $messages); |
||||
|
||||
// load messages |
||||
$context1 = $moFile->load($poFileDirectory . '/test.mo', 'context1'); |
||||
$context2 = $moFile->load($poFileDirectory . '/test.mo', 'context2'); |
||||
|
||||
// context1 |
||||
$this->assertCount(2, $context1); |
||||
|
||||
$this->assertArrayHasKey('Hello?', $context1); |
||||
$this->assertTrue(in_array('Привет?', $context1)); |
||||
|
||||
$this->assertArrayHasKey('Hello!?!', $context1); |
||||
$this->assertTrue(in_array('', $context1)); |
||||
|
||||
// context2 |
||||
$this->assertCount(4, $context2); |
||||
|
||||
$this->assertArrayHasKey("\"Quotes\"", $context2); |
||||
$this->assertTrue(in_array('"Кавычки"', $context2)); |
||||
|
||||
$this->assertArrayHasKey("\nNew lines\n", $context2); |
||||
$this->assertTrue(in_array("\nПереносы строк\n", $context2)); |
||||
|
||||
$this->assertArrayHasKey("\tTabs\t", $context2); |
||||
$this->assertTrue(in_array("\tТабы\t", $context2)); |
||||
|
||||
$this->assertArrayHasKey("\rCarriage returns\r", $context2); |
||||
$this->assertTrue(in_array("\rВозвраты кареток\r", $context2)); |
||||
} |
||||
} |
@ -0,0 +1,95 @@
|
||||
<?php |
||||
|
||||
namespace yiiunit\framework\i18n; |
||||
|
||||
use yii\i18n\GettextPoFile; |
||||
use yiiunit\TestCase; |
||||
|
||||
class GettextPoFileTest extends TestCase |
||||
{ |
||||
public function testLoad() |
||||
{ |
||||
$poFile = new GettextPoFile(); |
||||
$poFilePath = __DIR__ . '/../../data/i18n/test.po'; |
||||
$context1 = $poFile->load($poFilePath, 'context1'); |
||||
$context2 = $poFile->load($poFilePath, 'context2'); |
||||
|
||||
// item count |
||||
$this->assertCount(4, $context1); |
||||
$this->assertCount(2, $context2); |
||||
|
||||
// original messages |
||||
$this->assertArrayHasKey("Missing\n\r\t\"translation.", $context1); |
||||
$this->assertArrayHasKey("Aliquam tempus elit vel purus molestie placerat. In sollicitudin tincidunt\naliquet. Integer tincidunt gravida tempor. In convallis blandit dui vel malesuada.\nNunc vel sapien nunc, a pretium nulla.", $context1); |
||||
$this->assertArrayHasKey("String number two.", $context1); |
||||
$this->assertArrayHasKey("Nunc vel sapien nunc, a pretium nulla.\nPellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.", $context1); |
||||
|
||||
$this->assertArrayHasKey("The other\n\ncontext.\n", $context2); |
||||
$this->assertArrayHasKey("test1\\\ntest2\n\\\\\ntest3", $context2); |
||||
|
||||
// translated messages |
||||
$this->assertTrue(in_array("", $context1)); |
||||
$this->assertTrue(in_array("Олицетворение однократно. Представленный лексико-семантический анализ является\nпсихолингвистическим в своей основе, но механизм сочленений полидисперсен. Впечатление\nоднократно. Различное расположение выбирает сюжетный механизм сочленений.", $context1)); |
||||
$this->assertTrue(in_array('Строка номер два.', $context1)); |
||||
$this->assertTrue(in_array('Короткий перевод.', $context1)); |
||||
|
||||
$this->assertTrue(in_array("Другой\n\nконтекст.\n", $context2)); |
||||
$this->assertTrue(in_array("тест1\\\nтест2\n\\\\\nтест3", $context2)); |
||||
} |
||||
|
||||
public function testSave() |
||||
{ |
||||
// initial data |
||||
$s = chr(4); |
||||
$messages = array( |
||||
'Hello!' => 'Привет!', |
||||
"context1{$s}Hello?" => 'Привет?', |
||||
'Hello!?' => '', |
||||
"context1{$s}Hello!?!" => '', |
||||
"context2{$s}\"Quotes\"" => '"Кавычки"', |
||||
"context2{$s}\nNew lines\n" => "\nПереносы строк\n", |
||||
"context2{$s}\tTabs\t" => "\tТабы\t", |
||||
"context2{$s}\rCarriage returns\r" => "\rВозвраты кареток\r", |
||||
); |
||||
|
||||
// create temporary directory and dump messages |
||||
$poFileDirectory = __DIR__ . '/../../runtime/i18n'; |
||||
if (!is_dir($poFileDirectory)) { |
||||
mkdir($poFileDirectory); |
||||
} |
||||
if (is_file($poFileDirectory . '/test.po')) { |
||||
unlink($poFileDirectory . '/test.po'); |
||||
} |
||||
|
||||
$poFile = new GettextPoFile(); |
||||
$poFile->save($poFileDirectory . '/test.po', $messages); |
||||
|
||||
// load messages |
||||
$context1 = $poFile->load($poFileDirectory . '/test.po', 'context1'); |
||||
$context2 = $poFile->load($poFileDirectory . '/test.po', 'context2'); |
||||
|
||||
// context1 |
||||
$this->assertCount(2, $context1); |
||||
|
||||
$this->assertArrayHasKey('Hello?', $context1); |
||||
$this->assertTrue(in_array('Привет?', $context1)); |
||||
|
||||
$this->assertArrayHasKey('Hello!?!', $context1); |
||||
$this->assertTrue(in_array('', $context1)); |
||||
|
||||
// context2 |
||||
$this->assertCount(4, $context2); |
||||
|
||||
$this->assertArrayHasKey("\"Quotes\"", $context2); |
||||
$this->assertTrue(in_array('"Кавычки"', $context2)); |
||||
|
||||
$this->assertArrayHasKey("\nNew lines\n", $context2); |
||||
$this->assertTrue(in_array("\nПереносы строк\n", $context2)); |
||||
|
||||
$this->assertArrayHasKey("\tTabs\t", $context2); |
||||
$this->assertTrue(in_array("\tТабы\t", $context2)); |
||||
|
||||
$this->assertArrayHasKey("\rCarriage returns\r", $context2); |
||||
$this->assertTrue(in_array("\rВозвраты кареток\r", $context2)); |
||||
} |
||||
} |
@ -0,0 +1,248 @@
|
||||
<?php |
||||
|
||||
namespace yiiunit\framework\rbac; |
||||
|
||||
use yii\rbac\Assignment; |
||||
use yii\rbac\Item; |
||||
use yiiunit\TestCase; |
||||
|
||||
abstract class ManagerTestBase extends TestCase |
||||
{ |
||||
/** @var \yii\rbac\PhpManager|\yii\rbac\DbManager */ |
||||
protected $auth; |
||||
|
||||
public function testCreateItem() |
||||
{ |
||||
$type = Item::TYPE_TASK; |
||||
$name = 'editUser'; |
||||
$description = 'edit a user'; |
||||
$bizRule = 'checkUserIdentity()'; |
||||
$data = array(1, 2, 3); |
||||
$item = $this->auth->createItem($name, $type, $description, $bizRule, $data); |
||||
$this->assertTrue($item instanceof Item); |
||||
$this->assertEquals($item->type, $type); |
||||
$this->assertEquals($item->name, $name); |
||||
$this->assertEquals($item->description, $description); |
||||
$this->assertEquals($item->bizRule, $bizRule); |
||||
$this->assertEquals($item->data, $data); |
||||
|
||||
// test shortcut |
||||
$name2 = 'createUser'; |
||||
$item2 = $this->auth->createRole($name2, $description, $bizRule, $data); |
||||
$this->assertEquals($item2->type, Item::TYPE_ROLE); |
||||
|
||||
// test adding an item with the same name |
||||
$this->setExpectedException('Exception'); |
||||
$this->auth->createItem($name, $type, $description, $bizRule, $data); |
||||
} |
||||
|
||||
public function testGetItem() |
||||
{ |
||||
$this->assertTrue($this->auth->getItem('readPost') instanceof Item); |
||||
$this->assertTrue($this->auth->getItem('reader') instanceof Item); |
||||
$this->assertNull($this->auth->getItem('unknown')); |
||||
} |
||||
|
||||
public function testRemoveAuthItem() |
||||
{ |
||||
$this->assertTrue($this->auth->getItem('updatePost') instanceof Item); |
||||
$this->assertTrue($this->auth->removeItem('updatePost')); |
||||
$this->assertNull($this->auth->getItem('updatePost')); |
||||
$this->assertFalse($this->auth->removeItem('updatePost')); |
||||
} |
||||
|
||||
public function testChangeItemName() |
||||
{ |
||||
$item = $this->auth->getItem('readPost'); |
||||
$this->assertTrue($item instanceof Item); |
||||
$this->assertTrue($this->auth->hasItemChild('reader', 'readPost')); |
||||
$item->name = 'readPost2'; |
||||
$this->assertNull($this->auth->getItem('readPost')); |
||||
$this->assertEquals($this->auth->getItem('readPost2'), $item); |
||||
$this->assertFalse($this->auth->hasItemChild('reader', 'readPost')); |
||||
$this->assertTrue($this->auth->hasItemChild('reader', 'readPost2')); |
||||
} |
||||
|
||||
public function testAddItemChild() |
||||
{ |
||||
$this->auth->addItemChild('createPost', 'updatePost'); |
||||
|
||||
// test adding upper level item to lower one |
||||
$this->setExpectedException('Exception'); |
||||
$this->auth->addItemChild('readPost', 'reader'); |
||||
} |
||||
|
||||
public function testAddItemChild2() |
||||
{ |
||||
// test adding inexistent items |
||||
$this->setExpectedException('Exception'); |
||||
$this->assertFalse($this->auth->addItemChild('createPost2', 'updatePost')); |
||||
} |
||||
|
||||
public function testRemoveItemChild() |
||||
{ |
||||
$this->assertTrue($this->auth->hasItemChild('reader', 'readPost')); |
||||
$this->assertTrue($this->auth->removeItemChild('reader', 'readPost')); |
||||
$this->assertFalse($this->auth->hasItemChild('reader', 'readPost')); |
||||
$this->assertFalse($this->auth->removeItemChild('reader', 'readPost')); |
||||
} |
||||
|
||||
public function testGetItemChildren() |
||||
{ |
||||
$this->assertEquals(array(), $this->auth->getItemChildren('readPost')); |
||||
$children = $this->auth->getItemChildren('author'); |
||||
$this->assertEquals(3, count($children)); |
||||
$this->assertTrue(reset($children) instanceof Item); |
||||
} |
||||
|
||||
public function testAssign() |
||||
{ |
||||
$auth = $this->auth->assign('new user', 'createPost', 'rule', 'data'); |
||||
$this->assertTrue($auth instanceof Assignment); |
||||
$this->assertEquals($auth->userId, 'new user'); |
||||
$this->assertEquals($auth->itemName, 'createPost'); |
||||
$this->assertEquals($auth->bizRule, 'rule'); |
||||
$this->assertEquals($auth->data, 'data'); |
||||
|
||||
$this->setExpectedException('Exception'); |
||||
$this->auth->assign('new user', 'createPost2', 'rule', 'data'); |
||||
} |
||||
|
||||
public function testRevoke() |
||||
{ |
||||
$this->assertTrue($this->auth->isAssigned('author B', 'author')); |
||||
$auth = $this->auth->getAssignment('author B', 'author'); |
||||
$this->assertTrue($auth instanceof Assignment); |
||||
$this->assertTrue($this->auth->revoke('author B', 'author')); |
||||
$this->assertFalse($this->auth->isAssigned('author B', 'author')); |
||||
$this->assertFalse($this->auth->revoke('author B', 'author')); |
||||
} |
||||
|
||||
public function testGetAssignments() |
||||
{ |
||||
$this->auth->assign('author B', 'deletePost'); |
||||
$auths = $this->auth->getAssignments('author B'); |
||||
$this->assertEquals(2, count($auths)); |
||||
$this->assertTrue(reset($auths) instanceof Assignment); |
||||
} |
||||
|
||||
public function testGetItems() |
||||
{ |
||||
$this->assertEquals(count($this->auth->getRoles()), 4); |
||||
$this->assertEquals(count($this->auth->getOperations()), 4); |
||||
$this->assertEquals(count($this->auth->getTasks()), 1); |
||||
$this->assertEquals(count($this->auth->getItems()), 9); |
||||
|
||||
$this->assertEquals(count($this->auth->getItems('author B', null)), 1); |
||||
$this->assertEquals(count($this->auth->getItems('author C', null)), 0); |
||||
$this->assertEquals(count($this->auth->getItems('author B', Item::TYPE_ROLE)), 1); |
||||
$this->assertEquals(count($this->auth->getItems('author B', Item::TYPE_OPERATION)), 0); |
||||
} |
||||
|
||||
public function testClearAll() |
||||
{ |
||||
$this->auth->clearAll(); |
||||
$this->assertEquals(count($this->auth->getRoles()), 0); |
||||
$this->assertEquals(count($this->auth->getOperations()), 0); |
||||
$this->assertEquals(count($this->auth->getTasks()), 0); |
||||
$this->assertEquals(count($this->auth->getItems()), 0); |
||||
$this->assertEquals(count($this->auth->getAssignments('author B')), 0); |
||||
} |
||||
|
||||
public function testClearAssignments() |
||||
{ |
||||
$this->auth->clearAssignments(); |
||||
$this->assertEquals(count($this->auth->getAssignments('author B')), 0); |
||||
} |
||||
|
||||
public function testDetectLoop() |
||||
{ |
||||
$this->setExpectedException('Exception'); |
||||
$this->auth->addItemChild('readPost', 'readPost'); |
||||
} |
||||
|
||||
public function testExecuteBizRule() |
||||
{ |
||||
$this->assertTrue($this->auth->executeBizRule(null, array(), null)); |
||||
$this->assertTrue($this->auth->executeBizRule('return 1==true;', array(), null)); |
||||
$this->assertTrue($this->auth->executeBizRule('return $params[0]==$params[1];', array(1, '1'), null)); |
||||
$this->assertFalse($this->auth->executeBizRule('invalid', array(), null)); |
||||
} |
||||
|
||||
public function testCheckAccess() |
||||
{ |
||||
$results = array( |
||||
'reader A' => array( |
||||
'createPost' => false, |
||||
'readPost' => true, |
||||
'updatePost' => false, |
||||
'updateOwnPost' => false, |
||||
'deletePost' => false, |
||||
), |
||||
'author B' => array( |
||||
'createPost' => true, |
||||
'readPost' => true, |
||||
'updatePost' => true, |
||||
'updateOwnPost' => true, |
||||
'deletePost' => false, |
||||
), |
||||
'editor C' => array( |
||||
'createPost' => false, |
||||
'readPost' => true, |
||||
'updatePost' => true, |
||||
'updateOwnPost' => false, |
||||
'deletePost' => false, |
||||
), |
||||
'admin D' => array( |
||||
'createPost' => true, |
||||
'readPost' => true, |
||||
'updatePost' => true, |
||||
'updateOwnPost' => false, |
||||
'deletePost' => true, |
||||
), |
||||
); |
||||
|
||||
$params = array('authorID' => 'author B'); |
||||
|
||||
foreach (array('reader A', 'author B', 'editor C', 'admin D') as $user) { |
||||
$params['userID'] = $user; |
||||
foreach (array('createPost', 'readPost', 'updatePost', 'updateOwnPost', 'deletePost') as $operation) { |
||||
$result = $this->auth->checkAccess($user, $operation, $params); |
||||
$this->assertEquals($results[$user][$operation], $result); |
||||
} |
||||
} |
||||
} |
||||
|
||||
protected function prepareData() |
||||
{ |
||||
$this->auth->createOperation('createPost', 'create a post'); |
||||
$this->auth->createOperation('readPost', 'read a post'); |
||||
$this->auth->createOperation('updatePost', 'update a post'); |
||||
$this->auth->createOperation('deletePost', 'delete a post'); |
||||
|
||||
$task = $this->auth->createTask('updateOwnPost', 'update a post by author himself', 'return $params["authorID"]==$params["userID"];'); |
||||
$task->addChild('updatePost'); |
||||
|
||||
$role = $this->auth->createRole('reader'); |
||||
$role->addChild('readPost'); |
||||
|
||||
$role = $this->auth->createRole('author'); |
||||
$role->addChild('reader'); |
||||
$role->addChild('createPost'); |
||||
$role->addChild('updateOwnPost'); |
||||
|
||||
$role = $this->auth->createRole('editor'); |
||||
$role->addChild('reader'); |
||||
$role->addChild('updatePost'); |
||||
|
||||
$role = $this->auth->createRole('admin'); |
||||
$role->addChild('editor'); |
||||
$role->addChild('author'); |
||||
$role->addChild('deletePost'); |
||||
|
||||
$this->auth->assign('reader A', 'reader'); |
||||
$this->auth->assign('author B', 'author'); |
||||
$this->auth->assign('editor C', 'editor'); |
||||
$this->auth->assign('admin D', 'admin'); |
||||
} |
||||
} |
@ -0,0 +1,34 @@
|
||||
<?php |
||||
|
||||
namespace yiiunit\framework\rbac; |
||||
|
||||
use Yii; |
||||
use yii\rbac\PhpManager; |
||||
|
||||
require_once(__DIR__ . '/ManagerTestBase.php'); |
||||
|
||||
class PhpManagerTest extends ManagerTestBase |
||||
{ |
||||
public function setUp() |
||||
{ |
||||
$authFile = Yii::$app->getRuntimePath() . '/rbac.php'; |
||||
@unlink($authFile); |
||||
$this->auth = new PhpManager; |
||||
$this->auth->authFile = $authFile; |
||||
$this->auth->init(); |
||||
$this->prepareData(); |
||||
} |
||||
|
||||
public function tearDown() |
||||
{ |
||||
@unlink($this->auth->authFile); |
||||
} |
||||
|
||||
public function testSaveLoad() |
||||
{ |
||||
$this->auth->save(); |
||||
$this->auth->clearAll(); |
||||
$this->auth->load(); |
||||
$this->testCheckAccess(); |
||||
} |
||||
} |
@ -1,7 +0,0 @@
|
||||
<phpunit bootstrap="bootstrap.php" |
||||
colors="false" |
||||
convertErrorsToExceptions="true" |
||||
convertNoticesToExceptions="true" |
||||
convertWarningsToExceptions="true" |
||||
stopOnFailure="false"> |
||||
</phpunit> |
@ -1,6 +1,6 @@
|
||||
<?php |
||||
|
||||
require(__DIR__ . '/../../../framework/yii.php'); |
||||
require(__DIR__ . '/../../../yii/Yii.php'); |
||||
|
||||
$application = new yii\web\Application('test', __DIR__ . '/protected'); |
||||
$application->run(); |
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue