Natchanon Tatsaneepong 11 years ago
parent
commit
d7aeffadce
100 changed files with 16676 additions and 0 deletions
  1. 53 0
      php-yii2/README.md
  2. 97 0
      php-yii2/app/controllers/SiteController.php
  3. 28 0
      php-yii2/app/index.php
  4. 1 0
      php-yii2/app/runtime/keys.json
  5. 1 0
      php-yii2/app/vendor/yiisoft/yii2/.htaccess
  6. 533 0
      php-yii2/app/vendor/yiisoft/yii2/BaseYii.php
  7. 138 0
      php-yii2/app/vendor/yiisoft/yii2/CHANGELOG.md
  8. 32 0
      php-yii2/app/vendor/yiisoft/yii2/LICENSE.md
  9. 24 0
      php-yii2/app/vendor/yiisoft/yii2/README.md
  10. 9 0
      php-yii2/app/vendor/yiisoft/yii2/UPGRADE.md
  11. 26 0
      php-yii2/app/vendor/yiisoft/yii2/Yii.php
  12. 338 0
      php-yii2/app/vendor/yiisoft/yii2/assets/jquery.maskedinput.js
  13. 278 0
      php-yii2/app/vendor/yiisoft/yii2/assets/punycode/LICENSE-GPL.txt
  14. 20 0
      php-yii2/app/vendor/yiisoft/yii2/assets/punycode/LICENSE-MIT.txt
  15. 502 0
      php-yii2/app/vendor/yiisoft/yii2/assets/punycode/punycode.js
  16. 1 0
      php-yii2/app/vendor/yiisoft/yii2/assets/punycode/punycode.min.js
  17. 404 0
      php-yii2/app/vendor/yiisoft/yii2/assets/yii.activeForm.js
  18. 72 0
      php-yii2/app/vendor/yiisoft/yii2/assets/yii.captcha.js
  19. 139 0
      php-yii2/app/vendor/yiisoft/yii2/assets/yii.gridView.js
  20. 245 0
      php-yii2/app/vendor/yiisoft/yii2/assets/yii.js
  21. 227 0
      php-yii2/app/vendor/yiisoft/yii2/assets/yii.validation.js
  22. 89 0
      php-yii2/app/vendor/yiisoft/yii2/base/Action.php
  23. 45 0
      php-yii2/app/vendor/yiisoft/yii2/base/ActionEvent.php
  24. 102 0
      php-yii2/app/vendor/yiisoft/yii2/base/ActionFilter.php
  25. 646 0
      php-yii2/app/vendor/yiisoft/yii2/base/Application.php
  26. 80 0
      php-yii2/app/vendor/yiisoft/yii2/base/ArrayAccessTrait.php
  27. 23 0
      php-yii2/app/vendor/yiisoft/yii2/base/Arrayable.php
  28. 91 0
      php-yii2/app/vendor/yiisoft/yii2/base/Behavior.php
  29. 581 0
      php-yii2/app/vendor/yiisoft/yii2/base/Component.php
  30. 417 0
      php-yii2/app/vendor/yiisoft/yii2/base/Controller.php
  31. 108 0
      php-yii2/app/vendor/yiisoft/yii2/base/ErrorException.php
  32. 331 0
      php-yii2/app/vendor/yiisoft/yii2/base/ErrorHandler.php
  33. 185 0
      php-yii2/app/vendor/yiisoft/yii2/base/Event.php
  34. 53 0
      php-yii2/app/vendor/yiisoft/yii2/base/Exception.php
  35. 29 0
      php-yii2/app/vendor/yiisoft/yii2/base/Extension.php
  36. 384 0
      php-yii2/app/vendor/yiisoft/yii2/base/Formatter.php
  37. 55 0
      php-yii2/app/vendor/yiisoft/yii2/base/InlineAction.php
  38. 25 0
      php-yii2/app/vendor/yiisoft/yii2/base/InvalidCallException.php
  39. 25 0
      php-yii2/app/vendor/yiisoft/yii2/base/InvalidConfigException.php
  40. 25 0
      php-yii2/app/vendor/yiisoft/yii2/base/InvalidParamException.php
  41. 25 0
      php-yii2/app/vendor/yiisoft/yii2/base/InvalidRouteException.php
  42. 35 0
      php-yii2/app/vendor/yiisoft/yii2/base/MailEvent.php
  43. 858 0
      php-yii2/app/vendor/yiisoft/yii2/base/Model.php
  44. 25 0
      php-yii2/app/vendor/yiisoft/yii2/base/ModelEvent.php
  45. 667 0
      php-yii2/app/vendor/yiisoft/yii2/base/Module.php
  46. 25 0
      php-yii2/app/vendor/yiisoft/yii2/base/NotSupportedException.php
  47. 240 0
      php-yii2/app/vendor/yiisoft/yii2/base/Object.php
  48. 82 0
      php-yii2/app/vendor/yiisoft/yii2/base/Request.php
  49. 30 0
      php-yii2/app/vendor/yiisoft/yii2/base/Response.php
  50. 152 0
      php-yii2/app/vendor/yiisoft/yii2/base/Theme.php
  51. 25 0
      php-yii2/app/vendor/yiisoft/yii2/base/UnknownClassException.php
  52. 25 0
      php-yii2/app/vendor/yiisoft/yii2/base/UnknownMethodException.php
  53. 25 0
      php-yii2/app/vendor/yiisoft/yii2/base/UnknownPropertyException.php
  54. 19 0
      php-yii2/app/vendor/yiisoft/yii2/base/UserException.php
  55. 461 0
      php-yii2/app/vendor/yiisoft/yii2/base/View.php
  56. 26 0
      php-yii2/app/vendor/yiisoft/yii2/base/ViewContextInterface.php
  57. 46 0
      php-yii2/app/vendor/yiisoft/yii2/base/ViewEvent.php
  58. 30 0
      php-yii2/app/vendor/yiisoft/yii2/base/ViewRenderer.php
  59. 214 0
      php-yii2/app/vendor/yiisoft/yii2/base/Widget.php
  60. 116 0
      php-yii2/app/vendor/yiisoft/yii2/behaviors/AutoTimestamp.php
  61. 133 0
      php-yii2/app/vendor/yiisoft/yii2/caching/ApcCache.php
  62. 473 0
      php-yii2/app/vendor/yiisoft/yii2/caching/Cache.php
  63. 75 0
      php-yii2/app/vendor/yiisoft/yii2/caching/ChainedDependency.php
  64. 274 0
      php-yii2/app/vendor/yiisoft/yii2/caching/DbCache.php
  65. 66 0
      php-yii2/app/vendor/yiisoft/yii2/caching/DbDependency.php
  66. 99 0
      php-yii2/app/vendor/yiisoft/yii2/caching/Dependency.php
  67. 81 0
      php-yii2/app/vendor/yiisoft/yii2/caching/DummyCache.php
  68. 47 0
      php-yii2/app/vendor/yiisoft/yii2/caching/ExpressionDependency.php
  69. 240 0
      php-yii2/app/vendor/yiisoft/yii2/caching/FileCache.php
  70. 42 0
      php-yii2/app/vendor/yiisoft/yii2/caching/FileDependency.php
  71. 73 0
      php-yii2/app/vendor/yiisoft/yii2/caching/GroupDependency.php
  72. 265 0
      php-yii2/app/vendor/yiisoft/yii2/caching/MemCache.php
  73. 58 0
      php-yii2/app/vendor/yiisoft/yii2/caching/MemCacheServer.php
  74. 132 0
      php-yii2/app/vendor/yiisoft/yii2/caching/WinCache.php
  75. 104 0
      php-yii2/app/vendor/yiisoft/yii2/caching/XCache.php
  76. 83 0
      php-yii2/app/vendor/yiisoft/yii2/caching/ZendDataCache.php
  77. 138 0
      php-yii2/app/vendor/yiisoft/yii2/captcha/Captcha.php
  78. 341 0
      php-yii2/app/vendor/yiisoft/yii2/captcha/CaptchaAction.php
  79. 27 0
      php-yii2/app/vendor/yiisoft/yii2/captcha/CaptchaAsset.php
  80. 106 0
      php-yii2/app/vendor/yiisoft/yii2/captcha/CaptchaValidator.php
  81. 11 0
      php-yii2/app/vendor/yiisoft/yii2/captcha/SpicyRice.md
  82. BIN
      php-yii2/app/vendor/yiisoft/yii2/captcha/SpicyRice.ttf
  83. 253 0
      php-yii2/app/vendor/yiisoft/yii2/classes.php
  84. 64 0
      php-yii2/app/vendor/yiisoft/yii2/composer.json
  85. 165 0
      php-yii2/app/vendor/yiisoft/yii2/console/Application.php
  86. 276 0
      php-yii2/app/vendor/yiisoft/yii2/console/Controller.php
  87. 27 0
      php-yii2/app/vendor/yiisoft/yii2/console/Exception.php
  88. 77 0
      php-yii2/app/vendor/yiisoft/yii2/console/Request.php
  89. 18 0
      php-yii2/app/vendor/yiisoft/yii2/console/Response.php
  90. 613 0
      php-yii2/app/vendor/yiisoft/yii2/console/controllers/AssetController.php
  91. 67 0
      php-yii2/app/vendor/yiisoft/yii2/console/controllers/CacheController.php
  92. 335 0
      php-yii2/app/vendor/yiisoft/yii2/console/controllers/FixtureController.php
  93. 437 0
      php-yii2/app/vendor/yiisoft/yii2/console/controllers/HelpController.php
  94. 288 0
      php-yii2/app/vendor/yiisoft/yii2/console/controllers/MessageController.php
  95. 633 0
      php-yii2/app/vendor/yiisoft/yii2/console/controllers/MigrateController.php
  96. 182 0
      php-yii2/app/vendor/yiisoft/yii2/data/ActiveDataProvider.php
  97. 131 0
      php-yii2/app/vendor/yiisoft/yii2/data/ArrayDataProvider.php
  98. 249 0
      php-yii2/app/vendor/yiisoft/yii2/data/BaseDataProvider.php
  99. 70 0
      php-yii2/app/vendor/yiisoft/yii2/data/DataProviderInterface.php
  100. 335 0
      php-yii2/app/vendor/yiisoft/yii2/data/ModelSerializer.php

+ 53 - 0
php-yii2/README.md

@@ -0,0 +1,53 @@
+# Yii2 Benchmarking Test
+
+This is the Yii2 portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
+
+### JSON Encoding Test
+Uses the PHP standard [JSON encoder](http://www.php.net/manual/en/function.json-encode.php).
+
+* [JSON test controller](app/controllers/SiteController.php)
+
+
+### Data-Store/Database Mapping Test
+Uses the Yii2 Fluent Query Builder.
+
+* [DB test controller](app/controllers/SiteController.php)
+
+### Template Test
+Uses Yii2s template engine 'blade'
+
+* [Template test controller](application/controllers/Bench.php)
+
+
+## Infrastructure Software Versions
+The tests were run with:
+
+* [Yii2 Version 2](http://yiiframework.com/)
+* [PHP Version 5.4.*](http://www.php.net/) with FPM and APC
+* [nginx 1.4.0](http://nginx.org/)
+* [MySQL 5.5.29](https://dev.mysql.com/)
+
+## Test URLs
+### JSON Encoding Test
+
+http://localhost/site/json
+
+### Data-Store/Database Mapping Test
+
+http://localhost/site/db
+
+### Variable Query Test
+    
+http://localhost/site/db?queries=2
+
+### Templating Test
+
+http://localhost/site/fortunes
+
+### Update Test
+
+http://localhost/site/updates?queries=2
+
+### Plain Text Test
+
+http://localhost/site/plaintext

+ 97 - 0
php-yii2/app/controllers/SiteController.php

@@ -0,0 +1,97 @@
+<?php
+
+namespace app\controllers;
+
+use Yii;
+use yii\web\Controller;
+
+class SiteController extends Controller
+{
+    private function resJson($data) {
+        header('Content-type: application/json');
+        echo  json_encode($data);
+    }
+
+    public function actionJson() {
+        return $this->resJson(array('message'=>'Hello, World!'));
+    }
+
+    public function actionDb($queries = 1) {
+        // Set up for Test
+//        $cmd = Yii::$app->db->createCommand('insert into World (randomNumber) values (:v)');
+//        for($i = 1; $i <=10000 ; $i ++ ) {
+//            $cmd->bindValue(':v',mt_rand(1, 10000))->execute();
+//        }
+
+        $statement =  Yii::$app->db->createCommand('select id,randomNumber from World where id = :id');
+
+        if ($queries == 1) {
+            $arr = $statement->bindValue(':id',mt_rand(1, 10000))->queryOne();
+        } else {
+            // Create an array with the response string.
+            $arr = array();
+            // For each query, store the result set values in the response array
+            while (0 < $queries--) {
+                // Store result in array.
+                $arr[] = $statement->bindValue(':id',mt_rand(1, 10000))->queryOne();
+            }
+        }
+
+        return $this->resJson($arr);
+    }
+
+    public function actionFortunes() {
+        $arr = Yii::$app->db->createCommand('select id, message from Fortune')->queryAll();
+        $arr[0] = 'Additional fortune added at request time.';
+        asort($arr);
+        header("Content-Type: text/html; charset=utf-8");
+        echo <<<EOM
+            <!DOCTYPE html>
+            <html>
+            <head>
+            <title>Fortunes</title>
+            </head>
+            <body>
+            <table>
+            <tr>
+            <th>id</th>
+            <th>message</th>
+            </tr>
+EOM;
+        foreach ( $arr as $id => $fortune ) {
+            echo '<tr>';
+            echo '<td>'.$id.'</td>';
+            echo '<td>'.htmlspecialchars($fortune, ENT_QUOTES, 'utf-8').'</td>';
+            echo '</tr>';
+        }
+        echo <<<EOM
+        </table>
+        </body>
+        </html>
+EOM;
+
+    }
+
+    public function actionUpdates($queries = 1) {
+        if ($queries > 500) $queries = 500;
+        elseif ($queries < 0 ) $queries = 1;
+        $selectCommand = Yii::$app->db->createCommand('select randomNumber from World where id = :id');
+        $updateCommand = Yii::$app->db->createCommand('update World set randomNumber = :num where id = :id');
+        $arr = [];
+        while (0 < $queries--) {
+            // Store result in array.
+            $id = mt_rand(1,10000);
+            $randomNumber = mt_rand(1, 1000);
+            $selectCommand->bindParam(':id',$id)->queryScalar();
+            $updateCommand->bindValues([':id'=>$id,':num'=>$randomNumber])->execute();
+            $arr[] = array('id' => $id, 'randomNumber' => $randomNumber);
+        }
+
+        return $this->resJson($arr);
+    }
+
+    public function actionPlaintext() {
+        header("Content-Type: text/plain;");
+        echo 'Hello, World!';
+    }
+}

+ 28 - 0
php-yii2/app/index.php

@@ -0,0 +1,28 @@
+<?php
+
+// comment out the following two lines when deployed to production
+defined('YII_DEBUG') or define('YII_DEBUG', false);
+defined('YII_ENV') or define('YII_ENV', 'prod');
+error_reporting(E_ALL);
+ini_set('display_errors','on');
+
+require(__DIR__ . '/vendor/yiisoft/yii2/Yii.php');
+
+$config = [
+    'id' => 'basic',
+    'basePath' => __DIR__,
+    'components' => [
+        'db' => [
+            'class' => 'yii\db\Connection',
+            'dsn' => 'mysql:host=localhost;dbname=hello_world',
+            'username' => 'benchmarkdbuser',
+            'password' => 'benchmarkdbpass',
+            'charset' => 'utf8',
+        ],
+        'urlManager' => [
+            'enablePrettyUrl' => true,
+        ],
+    ],
+];
+
+(new yii\web\Application($config))->run();

+ 1 - 0
php-yii2/app/runtime/keys.json

@@ -0,0 +1 @@
+{"yii\\web\\Request\/basic":"7Wi_m-xi23s8cnTFrgs-heOZgl9X3xkm"}

+ 1 - 0
php-yii2/app/vendor/yiisoft/yii2/.htaccess

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

+ 533 - 0
php-yii2/app/vendor/yiisoft/yii2/BaseYii.php

@@ -0,0 +1,533 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+namespace yii;
+
+use yii\base\InvalidConfigException;
+use yii\base\InvalidParamException;
+use yii\base\UnknownClassException;
+use yii\log\Logger;
+
+/**
+ * Gets the application start timestamp.
+ */
+defined('YII_BEGIN_TIME') or define('YII_BEGIN_TIME', microtime(true));
+/**
+ * This constant defines the framework installation directory.
+ */
+defined('YII_PATH') or define('YII_PATH', __DIR__);
+/**
+ * This constant defines whether the application should be in debug mode or not. Defaults to false.
+ */
+defined('YII_DEBUG') or define('YII_DEBUG', false);
+/**
+ * This constant defines in which environment the application is running. Defaults to 'prod', meaning production environment.
+ * You may define this constant in the bootstrap script. The value could be 'prod' (production), 'dev' (development), 'test', 'staging', etc.
+ */
+defined('YII_ENV') or define('YII_ENV', 'prod');
+/**
+ * Whether the the application is running in production environment
+ */
+defined('YII_ENV_PROD') or define('YII_ENV_PROD', YII_ENV === 'prod');
+/**
+ * Whether the the application is running in development environment
+ */
+defined('YII_ENV_DEV') or define('YII_ENV_DEV', YII_ENV === 'dev');
+/**
+ * Whether the the application is running in testing environment
+ */
+defined('YII_ENV_TEST') or define('YII_ENV_TEST', YII_ENV === 'test');
+
+/**
+ * This constant defines whether error handling should be enabled. Defaults to true.
+ */
+defined('YII_ENABLE_ERROR_HANDLER') or define('YII_ENABLE_ERROR_HANDLER', true);
+
+
+/**
+ * BaseYii is the core helper class for the Yii framework.
+ *
+ * Do not use BaseYii directly. Instead, use its child class [[\Yii]] where
+ * you can customize methods of BaseYii.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class BaseYii
+{
+	/**
+	 * @var array class map used by the Yii autoloading mechanism.
+	 * The array keys are the class names (without leading backslashes), and the array values
+	 * are the corresponding class file paths (or path aliases). This property mainly affects
+	 * how [[autoload()]] works.
+	 * @see autoload()
+	 */
+	public static $classMap = [];
+	/**
+	 * @var \yii\console\Application|\yii\web\Application the application instance
+	 */
+	public static $app;
+	/**
+	 * @var array registered path aliases
+	 * @see getAlias()
+	 * @see setAlias()
+	 */
+	public static $aliases = ['@yii' => __DIR__];
+	/**
+	 * @var array initial property values that will be applied to objects newly created via [[createObject]].
+	 * The array keys are class names without leading backslashes "\", and the array values are the corresponding
+	 * name-value pairs for initializing the created class instances. For example,
+	 *
+	 * ~~~
+	 * [
+	 *     'Bar' => [
+	 *         'prop1' => 'value1',
+	 *         'prop2' => 'value2',
+	 *     ],
+	 *     'mycompany\foo\Car' => [
+	 *         'prop1' => 'value1',
+	 *         'prop2' => 'value2',
+	 *     ],
+	 * ]
+	 * ~~~
+	 *
+	 * @see createObject()
+	 */
+	public static $objectConfig = [];
+
+
+	/**
+	 * @return string the version of Yii framework
+	 */
+	public static function getVersion()
+	{
+		return '2.0.0-dev';
+	}
+
+	/**
+	 * Translates a path alias into an actual path.
+	 *
+	 * The translation is done according to the following procedure:
+	 *
+	 * 1. If the given alias does not start with '@', it is returned back without change;
+	 * 2. Otherwise, look for the longest registered alias that matches the beginning part
+	 *    of the given alias. If it exists, replace the matching part of the given alias with
+	 *    the corresponding registered path.
+	 * 3. Throw an exception or return false, depending on the `$throwException` parameter.
+	 *
+	 * For example, by default '@yii' is registered as the alias to the Yii framework directory,
+	 * say '/path/to/yii'. The alias '@yii/web' would then be translated into '/path/to/yii/web'.
+	 *
+	 * If you have registered two aliases '@foo' and '@foo/bar'. Then translating '@foo/bar/config'
+	 * would replace the part '@foo/bar' (instead of '@foo') with the corresponding registered path.
+	 * This is because the longest alias takes precedence.
+	 *
+	 * However, if the alias to be translated is '@foo/barbar/config', then '@foo' will be replaced
+	 * instead of '@foo/bar', because '/' serves as the boundary character.
+	 *
+	 * Note, this method does not check if the returned path exists or not.
+	 *
+	 * @param string $alias the alias to be translated.
+	 * @param boolean $throwException whether to throw an exception if the given alias is invalid.
+	 * If this is false and an invalid alias is given, false will be returned by this method.
+	 * @return string|boolean the path corresponding to the alias, false if the root alias is not previously registered.
+	 * @throws InvalidParamException if the alias is invalid while $throwException is true.
+	 * @see setAlias()
+	 */
+	public static function getAlias($alias, $throwException = true)
+	{
+		if (strncmp($alias, '@', 1)) {
+			// not an alias
+			return $alias;
+		}
+
+		$pos = strpos($alias, '/');
+		$root = $pos === false ? $alias : substr($alias, 0, $pos);
+
+		if (isset(static::$aliases[$root])) {
+			if (is_string(static::$aliases[$root])) {
+				return $pos === false ? static::$aliases[$root] : static::$aliases[$root] . substr($alias, $pos);
+			} else {
+				foreach (static::$aliases[$root] as $name => $path) {
+					if (strpos($alias . '/', $name . '/') === 0) {
+						return $path . substr($alias, strlen($name));
+					}
+				}
+			}
+		}
+
+		if ($throwException) {
+			throw new InvalidParamException("Invalid path alias: $alias");
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * Returns the root alias part of a given alias.
+	 * A root alias is an alias that has been registered via [[setAlias()]] previously.
+	 * If a given alias matches multiple root aliases, the longest one will be returned.
+	 * @param string $alias the alias
+	 * @return string|boolean the root alias, or false if no root alias is found
+	 */
+	public static function getRootAlias($alias)
+	{
+		$pos = strpos($alias, '/');
+		$root = $pos === false ? $alias : substr($alias, 0, $pos);
+
+		if (isset(static::$aliases[$root])) {
+			if (is_string(static::$aliases[$root])) {
+				return $root;
+			} else {
+				foreach (static::$aliases[$root] as $name => $path) {
+					if (strpos($alias . '/', $name . '/') === 0) {
+						return $name;
+					}
+				}
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Registers a path alias.
+	 *
+	 * A path alias is a short name representing a long path (a file path, a URL, etc.)
+	 * For example, we use '@yii' as the alias of the path to the Yii framework directory.
+	 *
+	 * A path alias must start with the character '@' so that it can be easily differentiated
+	 * from non-alias paths.
+	 *
+	 * Note that this method does not check if the given path exists or not. All it does is
+	 * to associate the alias with the path.
+	 *
+	 * Any trailing '/' and '\' characters in the given path will be trimmed.
+	 *
+	 * @param string $alias the alias name (e.g. "@yii"). It must start with a '@' character.
+	 * It may contain the forward slash '/' which serves as boundary character when performing
+	 * alias translation by [[getAlias()]].
+	 * @param string $path the path corresponding to the alias. Trailing '/' and '\' characters
+	 * will be trimmed. This can be
+	 *
+	 * - a directory or a file path (e.g. `/tmp`, `/tmp/main.txt`)
+	 * - a URL (e.g. `http://www.yiiframework.com`)
+	 * - a path alias (e.g. `@yii/base`). In this case, the path alias will be converted into the
+	 *   actual path first by calling [[getAlias()]].
+	 *
+	 * @throws InvalidParamException if $path is an invalid alias.
+	 * @see getAlias()
+	 */
+	public static function setAlias($alias, $path)
+	{
+		if (strncmp($alias, '@', 1)) {
+			$alias = '@' . $alias;
+		}
+		$pos = strpos($alias, '/');
+		$root = $pos === false ? $alias : substr($alias, 0, $pos);
+		if ($path !== null) {
+			$path = strncmp($path, '@', 1) ? rtrim($path, '\\/') : static::getAlias($path);
+			if (!isset(static::$aliases[$root])) {
+				if ($pos === false) {
+					static::$aliases[$root] = $path;
+				} else {
+					static::$aliases[$root] = [$alias => $path];
+				}
+			} elseif (is_string(static::$aliases[$root])) {
+				if ($pos === false) {
+					static::$aliases[$root] = $path;
+				} else {
+					static::$aliases[$root] = [
+						$alias => $path,
+						$root => static::$aliases[$root],
+					];
+				}
+			} else {
+				static::$aliases[$root][$alias] = $path;
+				krsort(static::$aliases[$root]);
+			}
+		} elseif (isset(static::$aliases[$root])) {
+			if (is_array(static::$aliases[$root])) {
+				unset(static::$aliases[$root][$alias]);
+			} elseif ($pos === false) {
+				unset(static::$aliases[$root]);
+			}
+		}
+	}
+
+	/**
+	 * Class autoload loader.
+	 * This method is invoked automatically when PHP sees an unknown class.
+	 * The method will attempt to include the class file according to the following procedure:
+	 *
+	 * 1. Search in [[classMap]];
+	 * 2. If the class is namespaced (e.g. `yii\base\Component`), it will attempt
+	 *    to include the file associated with the corresponding path alias
+	 *    (e.g. `@yii/base/Component.php`);
+	 *
+	 * This autoloader allows loading classes that follow the [PSR-4 standard](http://www.php-fig.org/psr/psr-4/)
+	 * and have its top-level namespace or sub-namespaces defined as path aliases.
+	 *
+	 * Example: When aliases `@yii` and `@yii/bootstrap` are defined, classes in the `yii\bootstrap` namespace
+	 * will be loaded using the `@yii/bootstrap` alias which points to the directory where bootstrap extension
+	 * files are installed and all classes from other `yii` namespaces will be loaded from the yii framework directory.
+	 *
+	 * @param string $className the fully qualified class name without a leading backslash "\"
+	 * @throws UnknownClassException if the class does not exist in the class file
+	 */
+	public static function autoload($className)
+	{
+		if (isset(static::$classMap[$className])) {
+			$classFile = static::$classMap[$className];
+			if ($classFile[0] === '@') {
+				$classFile = static::getAlias($classFile);
+			}
+		} elseif (strpos($className, '\\') !== false) {
+			$classFile = static::getAlias('@' . str_replace('\\', '/', $className) . '.php', false);
+			if ($classFile === false || !is_file($classFile)) {
+				return;
+			}
+		} else {
+			return;
+		}
+
+		include($classFile);
+
+		if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && !trait_exists($className, false)) {
+			throw new UnknownClassException("Unable to find '$className' in file: $classFile. Namespace missing?");
+		}
+	}
+
+	/**
+	 * Creates a new object using the given configuration.
+	 *
+	 * The configuration can be either a string or an array.
+	 * If a string, it is treated as the *object class*; if an array,
+	 * it must contain a `class` element specifying the *object class*, and
+	 * the rest of the name-value pairs in the array will be used to initialize
+	 * the corresponding object properties.
+	 *
+	 * Below are some usage examples:
+	 *
+	 * ~~~
+	 * $object = \Yii::createObject('app\components\GoogleMap');
+	 * $object = \Yii::createObject([
+	 *     'class' => 'app\components\GoogleMap',
+	 *     'apiKey' => 'xyz',
+	 * ]);
+	 * ~~~
+	 *
+	 * This method can be used to create any object as long as the object's constructor is
+	 * defined like the following:
+	 *
+	 * ~~~
+	 * public function __construct(..., $config = []) {
+	 * }
+	 * ~~~
+	 *
+	 * The method will pass the given configuration as the last parameter of the constructor,
+	 * and any additional parameters to this method will be passed as the rest of the constructor parameters.
+	 *
+	 * @param string|array $config the configuration. It can be either a string representing the class name
+	 * or an array representing the object configuration.
+	 * @return mixed the created object
+	 * @throws InvalidConfigException if the configuration is invalid.
+	 */
+	public static function createObject($config)
+	{
+		static $reflections = [];
+
+		if (is_string($config)) {
+			$class = $config;
+			$config = [];
+		} elseif (isset($config['class'])) {
+			$class = $config['class'];
+			unset($config['class']);
+		} else {
+			throw new InvalidConfigException('Object configuration must be an array containing a "class" element.');
+		}
+
+		$class = ltrim($class, '\\');
+
+		if (isset(static::$objectConfig[$class])) {
+			$config = array_merge(static::$objectConfig[$class], $config);
+		}
+
+		if (($n = func_num_args()) > 1) {
+			/** @var \ReflectionClass $reflection */
+			if (isset($reflections[$class])) {
+				$reflection = $reflections[$class];
+			} else {
+				$reflection = $reflections[$class] = new \ReflectionClass($class);
+			}
+			$args = func_get_args();
+			array_shift($args); // remove $config
+			if (!empty($config)) {
+				$args[] = $config;
+			}
+			return $reflection->newInstanceArgs($args);
+		} else {
+			return empty($config) ? new $class : new $class($config);
+		}
+	}
+
+	/**
+	 * Logs a trace message.
+	 * Trace messages are logged mainly for development purpose to see
+	 * the execution work flow of some code.
+	 * @param string $message the message to be logged.
+	 * @param string $category the category of the message.
+	 */
+	public static function trace($message, $category = 'application')
+	{
+		if (YII_DEBUG) {
+			static::$app->getLog()->log($message, Logger::LEVEL_TRACE, $category);
+		}
+	}
+
+	/**
+	 * Logs an error message.
+	 * An error message is typically logged when an unrecoverable error occurs
+	 * during the execution of an application.
+	 * @param string $message the message to be logged.
+	 * @param string $category the category of the message.
+	 */
+	public static function error($message, $category = 'application')
+	{
+		static::$app->getLog()->log($message, Logger::LEVEL_ERROR, $category);
+	}
+
+	/**
+	 * Logs a warning message.
+	 * A warning message is typically logged when an error occurs while the execution
+	 * can still continue.
+	 * @param string $message the message to be logged.
+	 * @param string $category the category of the message.
+	 */
+	public static function warning($message, $category = 'application')
+	{
+		static::$app->getLog()->log($message, Logger::LEVEL_WARNING, $category);
+	}
+
+	/**
+	 * Logs an informative message.
+	 * An informative message is typically logged by an application to keep record of
+	 * something important (e.g. an administrator logs in).
+	 * @param string $message the message to be logged.
+	 * @param string $category the category of the message.
+	 */
+	public static function info($message, $category = 'application')
+	{
+		static::$app->getLog()->log($message, Logger::LEVEL_INFO, $category);
+	}
+
+	/**
+	 * Marks the beginning of a code block for profiling.
+	 * This has to be matched with a call to [[endProfile]] with the same category name.
+	 * The begin- and end- calls must also be properly nested. For example,
+	 *
+	 * ~~~
+	 * \Yii::beginProfile('block1');
+	 * // some code to be profiled
+	 *     \Yii::beginProfile('block2');
+	 *     // some other code to be profiled
+	 *     \Yii::endProfile('block2');
+	 * \Yii::endProfile('block1');
+	 * ~~~
+	 * @param string $token token for the code block
+	 * @param string $category the category of this log message
+	 * @see endProfile()
+	 */
+	public static function beginProfile($token, $category = 'application')
+	{
+		static::$app->getLog()->log($token, Logger::LEVEL_PROFILE_BEGIN, $category);
+	}
+
+	/**
+	 * Marks the end of a code block for profiling.
+	 * This has to be matched with a previous call to [[beginProfile]] with the same category name.
+	 * @param string $token token for the code block
+	 * @param string $category the category of this log message
+	 * @see beginProfile()
+	 */
+	public static function endProfile($token, $category = 'application')
+	{
+		static::$app->getLog()->log($token, Logger::LEVEL_PROFILE_END, $category);
+	}
+
+	/**
+	 * Returns an HTML hyperlink that can be displayed on your Web page showing Powered by Yii" information.
+	 * @return string an HTML hyperlink that can be displayed on your Web page showing Powered by Yii" information
+	 */
+	public static function powered()
+	{
+		return 'Powered by <a href="http://www.yiiframework.com/" rel="external">Yii Framework</a>';
+	}
+
+	/**
+	 * Translates a message to the specified language.
+	 *
+	 * This is a shortcut method of [[\yii\i18n\I18N::translate()]].
+	 *
+	 * The translation will be conducted according to the message category and the target language will be used.
+	 *
+	 * In case when a translated message has different plural forms (separated by "|"), this method
+	 * will also attempt to choose an appropriate one according to a given numeric value which is
+	 * specified as the first parameter (indexed by 0) in `$params`.
+	 *
+	 * For example, if a translated message is "I have an apple.|I have {n} apples.", and the first
+	 * parameter is 2, the message returned will be "I have 2 apples.". Note that the placeholder "{n}"
+	 * will be replaced with the given number.
+	 *
+	 * For more details on how plural rules are applied, please refer to:
+	 * <http://www.unicode.org/cldr/charts/supplemental/language_plural_rules.html>
+	 *
+	 * @param string $category the message category.
+	 * @param string $message the message to be translated.
+	 * @param array $params the parameters that will be used to replace the corresponding placeholders in the message.
+	 * @param string $language the language code (e.g. `en-US`, `en`). If this is null, the current
+	 * [[\yii\base\Application::language|application language]] will be used.
+	 * @return string the translated message.
+	 */
+	public static function t($category, $message, $params = [], $language = null)
+	{
+		if (static::$app !== null) {
+			return static::$app->getI18n()->translate($category, $message, $params, $language ?: static::$app->language);
+		} else {
+			$p = [];
+			foreach ((array) $params as $name => $value) {
+				$p['{' . $name . '}'] = $value;
+			}
+			return ($p === []) ? $message : strtr($message, $p);
+		}
+	}
+
+	/**
+	 * Configures an object with the initial property values.
+	 * @param object $object the object to be configured
+	 * @param array $properties the property initial values given in terms of name-value pairs.
+	 * @return object the object itself
+	 */
+	public static function configure($object, $properties)
+	{
+		foreach ($properties as $name => $value) {
+			$object->$name = $value;
+		}
+		return $object;
+	}
+
+	/**
+	 * Returns the public member variables of an object.
+	 * This method is provided such that we can get the public member variables of an object.
+	 * It is different from "get_object_vars()" because the latter will return private
+	 * and protected variables if it is called within the object itself.
+	 * @param object $object the object to be handled
+	 * @return array the public member variables of the object
+	 */
+	public static function getObjectVars($object)
+	{
+		return get_object_vars($object);
+	}
+}

+ 138 - 0
php-yii2/app/vendor/yiisoft/yii2/CHANGELOG.md

@@ -0,0 +1,138 @@
+Yii Framework 2 Change Log
+==========================
+
+2.0.0 beta under development
+----------------------------
+
+- Bug #1326: The `visible` setting for `DetailView` doesn't work as expected (qiangxue)
+- Bug #1446: Logging while logs are processed causes infinite loop (qiangxue)
+- Bug #1497: Localized view files are not correctly returned (mintao)
+- Bug #1500: Log messages exported to files are not separated by newlines (omnilight, qiangxue)
+- Bug #1504: Debug toolbar isn't loaded successfully in some environments when xdebug is enabled (qiangxue)
+- Bug #1509: The SQL for creating Postgres RBAC tables is incorrect (qiangxue)
+- Bug #1545: It was not possible to execute db Query twice, params where missing (cebe)
+- Bug #1550: fixed the issue that JUI input widgets did not property input IDs.
+- Bug #1654: Fixed the issue that a new message source object is generated for every new message being translated (qiangxue)
+- Bug #1582: Error messages shown via client-side validation should not be double encoded (qiangxue)
+- Bug #1591: StringValidator is accessing undefined property (qiangxue)
+- Bug #1597: Added `enableAutoLogin` to basic and advanced application templates so "remember me" now works properly (samdark)
+- Bug #1631: Charset is now explicitly set to UTF-8 when serving JSON (samdark)
+- Bug #1635: `yii\jui\SliderInput` wasn't properly initialized (samdark)
+- Bug #1686: ActiveForm is creating duplicated messages in error summary (qiangxue)
+- Bug #1704: Incorrect regexp is used in `Inflector::camelize()` (qiangxue)
+- Bug #1710: OpenId auth client does not request required attributes correctly (klimov-paul)
+- Bug #1798: Fixed label attributes for array fields (zhuravljov)
+- Bug #1800: Better check for `$_SERVER['HTTPS']` in `yii\web\Request::getIsSecureConnection()` (ginus, samdark)
+- Bug #1827: Debugger toolbar is loaded twice if an action is calling `run()` to execute another action (qiangxue)
+- Bug #1868: Added ability to exclude tables from FixtureController apply/clear actions. (Ragazzo)
+- Bug #1869: Fixed tables clearing. `TRUNCATE` changed to `DELETE` to avoid postgresql tables checks (and truncating all tables) (Ragazzo)
+- Bug #1870: Validation errors weren't properly translated when using clientside validation (samdark)
+- Bug #1930: Fixed domain based URL matching for website root (samdark)
+- Bug #1937: Fixed wrong behavior or advanced app's `init --env` when called without parameter actually specified (samdark)
+- Bug #1959: `Html::activeCheckbox` wasn't respecting custom values for checked/unchecked state (klevron, samdark)
+- Bug #1965: `Controller::findLayoutFile()` returns incorrect file path when layout name starts with a slash (qiangxue)
+- Bug #1992: In module scenario that use 'site/captcha' will get wrong refreshUrl (callmez)
+- Bug #1993: afterFind event in AR is now called after relations have been populated (cebe, creocoder)
+- Bug #1998: Unchecked required checkbox never pass client validation (klevron)
+- Bug: Fixed `Call to a member function registerAssetFiles() on a non-object` in case of wrong `sourcePath` for an asset bundle (samdark)
+- Bug: Fixed incorrect event name for `yii\jui\Spinner` (samdark)
+- Bug: Json::encode() did not handle objects that implement JsonSerializable interface correctly (cebe)
+- Bug: Fixed issue with tabular input on ActiveField::radio() and ActiveField::checkbox() (jom)
+- Bug: Fixed the issue that query cache returns the same data for the same SQL but different query methods (qiangxue)
+- Bug: Fixed URL parsing so it's now properly giving 404 for URLs like `http://example.com//////site/about` (samdark)
+- Bug: Fixed `HelpController::getModuleCommands` issue where it attempts to scan a module's controller directory when it doesn't exist (jom)
+- Enh #46: Added Image extension based on [Imagine library](http://imagine.readthedocs.org) (tonydspaniard)
+- Enh #364: Improve Inflector::slug with `intl` transliteration. Improved transliteration char map. (tonydspaniard)
+- Enh #797: Added support for validating multiple columns by `UniqueValidator` and `ExistValidator` (qiangxue)
+- Enh #802: Added support for retrieving sub-array element or child object property through `ArrayHelper::getValue()` (qiangxue, cebe)
+- Enh #1293: Replaced Console::showProgress() with a better approach. See Console::startProgress() for details (cebe)
+- Enh #1406: DB Schema support for Oracle Database (p0larbeer, qiangxue)
+- Enh #1437: Added ListView::viewParams (qiangxue)
+- Enh #1469: ActiveRecord::find() now works with default conditions (default scope) applied by createQuery (cebe)
+- Enh #1476: Add yii\web\Session::handler property (nineinchnick)
+- Enh #1499: Added `ActionColumn::controller` property to support customizing the controller for handling GridView actions (qiangxue)
+- Enh #1523: Query conditions now allow to use the NOT operator (cebe)
+- Enh #1552: It is now possible to use multiple bootstrap NavBar in a single page (Alex-Code)
+- Enh #1572: Added `yii\web\Controller::createAbsoluteUrl()` (samdark)
+- Enh #1579: throw exception when the given AR relation name does not match in a case sensitive manner (qiangxue)
+- Enh #1581: Added `ActiveQuery::joinWith()` and `ActiveQuery::innerJoinWith()` to support joining with relations (qiangxue)
+- Enh #1601: Added support for tagName and encodeLabel parameters in ButtonDropdown (omnilight)
+- Enh #1611: Added `BaseActiveRecord::markAttributeDirty()` (qiangxue)
+- Enh #1633: Advanced application template now works with MongoDB by default (samdark)
+- Enh #1634: Use masked CSRF tokens to prevent BREACH exploits (qiangxue)
+- Enh #1641: Added `BaseActiveRecord::updateAttributes()` (qiangxue)
+- Enh #1646: Added postgresql `QueryBuilder::checkIntegrity` and `QueryBuilder::resetSequence` (Ragazzo)
+- Enh #1645: Added `Connection::$pdoClass` property (Ragazzo)
+- Enh #1681: Added support for automatically adjusting the "for" attribute of label generated by `ActiveField::label()` (qiangxue)
+- Enh #1706: Added support for registering a single JS/CSS file with dependency (qiangxue)
+- Enh #1773: keyPrefix property of Cache is not restricted to alnum characters anymore, however it is still recommended (cebe)
+- Enh #1809: Added support for building "EXISTS" and "NOT EXISTS" query conditions (abdrasulov)
+- Enh #1852: ActiveRecord::tableName() now returns table name using DbConnection::tablePrefix (creocoder)
+- Enh #1894: The path aliases `@webroot` and `@web` are now available right after the application is initialized (qiangxue)
+- Enh #1921: Grid view ActionColumn now allow to name buttons like `{controller/action}` (creocoder)
+- Enh #1973: `yii message/extract` is now able to generate `.po` files (SergeiKutanov, samdark)
+- Enh #1984: ActionFilter will now mark event as handled when action run is aborted (cebe)
+- Enh #2003: Added `filter` property to `ExistValidator` and `UniqueValidator` to support adding additional filtering conditions (qiangxue)
+- Enh #2043: Added support for custom request body parsers (danschmidt5189, cebe)
+- Enh #2051: Do not save null data into database when using RBAC (qiangxue)
+- Enh: Added `favicon.ico` and `robots.txt` to default application templates (samdark)
+- Enh: Added `Widget::autoIdPrefix` to support prefixing automatically generated widget IDs (qiangxue)
+- Enh: Support for file aliases in console command 'message' (omnilight)
+- Enh: Sort and Pagination can now create absolute URLs (cebe)
+- Enh: Added support for using array-typed arguments for console commands (qiangxue)
+- Enh: Added support for installing packages conforming to PSR-4 standard (qiangxue)
+- Enh: Better exception message when class cannot be loaded (samdark)
+- Enh: `init` of advanced application now allows to specify answer for overwriting files via `init --overwrite=n` (samdark)
+- Enh: Added `TableSchema::fullName` property (qiangxue)
+- Enh #1839: Added support for getting file extension and basename from uploaded file (anfrantic)
+- Enh: yii\codeception\TestCase now supports loading and using fixtures via Yii fixture framework (qiangxue)
+- Enh: Added support to parse json request data to Request::getRestParams() (cebe)
+- Chg #1519: `yii\web\User::loginRequired()` now returns the `Response` object instead of exiting the application (qiangxue)
+- Chg #1586: `QueryBuilder::buildLikeCondition()` will now escape special characters and use percentage characters by default (qiangxue)
+- Chg #1610: `Html::activeCheckboxList()` and `Html::activeRadioList()` will submit an empty string if no checkbox/radio is selected (qiangxue)
+- Chg #1643: Added default value for `Captcha::options` (qiangxue)
+- Chg #1796: Removed `yii\base\Controller::getActionParams()` (samdark)
+- Chg #1835: `CheckboxColumn` now renders checkboxes whose values are the corresponding data key values (qiangxue)
+- Chg #1821: Changed default values for yii\db\Connection username and password to null (cebe)
+- Chg #1844: `Response::sendFile()` and other file sending methods will not send the response (qiangxue)
+- Chg #1852: DbConnection::tablePrefix default value now 'tbl_' (creocoder)
+- Chg #2057: AutoTimestamp attributes defaults changed from `create_time` and `update_time` to `created_at` and `updated_at` (creocoder)
+- Chg #2063: Removed `yii\web\Request::acceptTypes` and renamed `yii\web\Request::acceptedContentTypes` to `acceptableContentTypes` (qiangxue)
+- Chg: Renamed `yii\jui\Widget::clientEventsMap` to `clientEventMap` (qiangxue)
+- Chg: Renamed `ActiveRecord::getPopulatedRelations()` to `getRelatedRecords()` (qiangxue)
+- Chg: Renamed `attributeName` and `className` to `targetAttribute` and `targetClass` for `UniqueValidator` and `ExistValidator` (qiangxue)
+- Chg: Added `yii\widgets\InputWidget::options` (qiangxue)
+- Chg: Changed the signature of `urlCreator` and button creators for `yii\gridview\ActionColumn` (qiangxue)
+- Chg: Updated HTMLPurified dependency to `4.6.*`.
+- Chg: Changed Yii autoloader to support loading PSR-4 classes only (i.e. PEAR-styled classes not supported anymore) (qiangxue)
+- Chg: Changed the directory structure according to PSR-4. You have to update your application `index.php`,
+       `index-test.php` and `yii` files to point to the new location of `Yii.php` (qiangxue, cebe)
+- Chg: Advanced app template: moved database connection DSN, login and password to `-local` config not to expose it to VCS (samdark)
+- Chg: Renamed `yii\web\Request::acceptedLanguages` to `acceptableLanguages` (qiangxue)
+- New #66: [Auth client library](https://github.com/yiisoft/yii2-authclient) OpenId, OAuth1, OAuth2 clients (klimov-paul)
+- New #1393: [Codeception testing framework integration](https://github.com/yiisoft/yii2-codeception) (Ragazzo)
+- New #1438: [MongoDB integration](https://github.com/yiisoft/yii2-mongodb) ActiveRecord and Query (klimov-paul)
+- New #1956: Implemented test fixture framework (qiangxue)
+- New: Yii framework now comes with core messages in multiple languages
+- New: Added yii\codeception\DbTestCase (qiangxue)
+
+
+2.0.0 alpha, December 1, 2013
+---------------------------
+
+- Initial release.
+- Official extensions released in this version:
+  - [Twitter bootstrap 3.0](https://github.com/yiisoft/yii2-bootstrap)
+  - [Jquery UI](https://github.com/yiisoft/yii2-jui)
+
+  - [Debug Toolbar](https://github.com/yiisoft/yii2-debug)
+  - [Gii code generator](https://github.com/yiisoft/yii2-gii)
+
+  - [Elasticsearch integration](https://github.com/yiisoft/yii2-elasticsearch): ActiveRecord and Query
+  - [Redis integration](https://github.com/yiisoft/yii2-redis): ActiveRecord, Cache and Session
+  - [Sphinx integration](https://github.com/yiisoft/yii2-sphinx): ActiveRecord and Query
+
+  - [Swiftmailer](https://github.com/yiisoft/yii2-swiftmailer)
+
+  - [Smarty View Renderer](https://github.com/yiisoft/yii2-smarty)
+  - [Twig View Renderer](https://github.com/yiisoft/yii2-twig)

+ 32 - 0
php-yii2/app/vendor/yiisoft/yii2/LICENSE.md

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

+ 24 - 0
php-yii2/app/vendor/yiisoft/yii2/README.md

@@ -0,0 +1,24 @@
+Yii PHP Framework Version 2
+===========================
+
+This is the core framework code of [Yii 2](https://github.com/yiisoft/yii2).
+
+
+Installation
+------------
+
+The preferred way to install this extension is through [composer](http://getcomposer.org/download/).
+
+Either run
+
+```
+php composer.phar require --prefer-dist "yiisoft/yii2 *"
+```
+
+or add
+
+```json
+"yiisoft/yii2": "*"
+```
+
+to the require section of your composer.json.

+ 9 - 0
php-yii2/app/vendor/yiisoft/yii2/UPGRADE.md

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

+ 26 - 0
php-yii2/app/vendor/yiisoft/yii2/Yii.php

@@ -0,0 +1,26 @@
+<?php
+/**
+ * Yii bootstrap file.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+require(__DIR__ . '/BaseYii.php');
+
+/**
+ * Yii is a helper class serving common framework functionalities.
+ *
+ * It extends from [[yii\BaseYii]] which provides the actual implementation.
+ * By writing your own Yii class, you can customize some functionalities of [[yii\BaseYii]].
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Yii extends \yii\BaseYii
+{
+}
+
+spl_autoload_register(['Yii', 'autoload'], true, true);
+Yii::$classMap = include(__DIR__ . '/classes.php');

+ 338 - 0
php-yii2/app/vendor/yiisoft/yii2/assets/jquery.maskedinput.js

@@ -0,0 +1,338 @@
+/*
+	Masked Input plugin for jQuery
+	Copyright (c) 2007-2013 Josh Bush (digitalbush.com)
+	Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)
+	Version: 1.3.1
+*/
+(function($) {
+	function getPasteEvent() {
+    var el = document.createElement('input'),
+        name = 'onpaste';
+    el.setAttribute(name, '');
+    return (typeof el[name] === 'function')?'paste':'input';
+}
+
+var pasteEventName = getPasteEvent() + ".mask",
+	ua = navigator.userAgent,
+	iPhone = /iphone/i.test(ua),
+	android=/android/i.test(ua),
+	caretTimeoutId;
+
+$.mask = {
+	//Predefined character definitions
+	definitions: {
+		'9': "[0-9]",
+		'a': "[A-Za-z]",
+		'*': "[A-Za-z0-9]"
+	},
+	dataName: "rawMaskFn",
+	placeholder: '_',
+};
+
+$.fn.extend({
+	//Helper Function for Caret positioning
+	caret: function(begin, end) {
+		var range;
+
+		if (this.length === 0 || this.is(":hidden")) {
+			return;
+		}
+
+		if (typeof begin == 'number') {
+			end = (typeof end === 'number') ? end : begin;
+			return this.each(function() {
+				if (this.setSelectionRange) {
+					this.setSelectionRange(begin, end);
+				} else if (this.createTextRange) {
+					range = this.createTextRange();
+					range.collapse(true);
+					range.moveEnd('character', end);
+					range.moveStart('character', begin);
+					range.select();
+				}
+			});
+		} else {
+			if (this[0].setSelectionRange) {
+				begin = this[0].selectionStart;
+				end = this[0].selectionEnd;
+			} else if (document.selection && document.selection.createRange) {
+				range = document.selection.createRange();
+				begin = 0 - range.duplicate().moveStart('character', -100000);
+				end = begin + range.text.length;
+			}
+			return { begin: begin, end: end };
+		}
+	},
+	unmask: function() {
+		return this.trigger("unmask");
+	},
+	mask: function(mask, settings) {
+		var input,
+			defs,
+			tests,
+			partialPosition,
+			firstNonMaskPos,
+			len;
+
+		if (!mask && this.length > 0) {
+			input = $(this[0]);
+			return input.data($.mask.dataName)();
+		}
+		settings = $.extend({
+			placeholder: $.mask.placeholder, // Load default placeholder
+			completed: null
+		}, settings);
+
+
+		defs = $.mask.definitions;
+		tests = [];
+		partialPosition = len = mask.length;
+		firstNonMaskPos = null;
+
+		$.each(mask.split(""), function(i, c) {
+			if (c == '?') {
+				len--;
+				partialPosition = i;
+			} else if (defs[c]) {
+				tests.push(new RegExp(defs[c]));
+				if (firstNonMaskPos === null) {
+					firstNonMaskPos = tests.length - 1;
+				}
+			} else {
+				tests.push(null);
+			}
+		});
+
+		return this.trigger("unmask").each(function() {
+			var input = $(this),
+				buffer = $.map(
+				mask.split(""),
+				function(c, i) {
+					if (c != '?') {
+						return defs[c] ? settings.placeholder : c;
+					}
+				}),
+				focusText = input.val();
+
+			function seekNext(pos) {
+				while (++pos < len && !tests[pos]);
+				return pos;
+			}
+
+			function seekPrev(pos) {
+				while (--pos >= 0 && !tests[pos]);
+				return pos;
+			}
+
+			function shiftL(begin,end) {
+				var i,
+					j;
+
+				if (begin<0) {
+					return;
+				}
+
+				for (i = begin, j = seekNext(end); i < len; i++) {
+					if (tests[i]) {
+						if (j < len && tests[i].test(buffer[j])) {
+							buffer[i] = buffer[j];
+							buffer[j] = settings.placeholder;
+						} else {
+							break;
+						}
+
+						j = seekNext(j);
+					}
+				}
+				writeBuffer();
+				input.caret(Math.max(firstNonMaskPos, begin));
+			}
+
+			function shiftR(pos) {
+				var i,
+					c,
+					j,
+					t;
+
+				for (i = pos, c = settings.placeholder; i < len; i++) {
+					if (tests[i]) {
+						j = seekNext(i);
+						t = buffer[i];
+						buffer[i] = c;
+						if (j < len && tests[j].test(t)) {
+							c = t;
+						} else {
+							break;
+						}
+					}
+				}
+			}
+
+			function keydownEvent(e) {
+				var k = e.which,
+					pos,
+					begin,
+					end;
+
+				//backspace, delete, and escape get special treatment
+				if (k === 8 || k === 46 || (iPhone && k === 127)) {
+					pos = input.caret();
+					begin = pos.begin;
+					end = pos.end;
+
+					if (end - begin === 0) {
+						begin=k!==46?seekPrev(begin):(end=seekNext(begin-1));
+						end=k===46?seekNext(end):end;
+					}
+					clearBuffer(begin, end);
+					shiftL(begin, end - 1);
+
+					e.preventDefault();
+				} else if (k == 27) {//escape
+					input.val(focusText);
+					input.caret(0, checkVal());
+					e.preventDefault();
+				}
+			}
+
+			function keypressEvent(e) {
+				var k = e.which,
+					pos = input.caret(),
+					p,
+					c,
+					next;
+
+				if (e.ctrlKey || e.altKey || e.metaKey || k < 32) {//Ignore
+					return;
+				} else if (k) {
+					if (pos.end - pos.begin !== 0){
+						clearBuffer(pos.begin, pos.end);
+						shiftL(pos.begin, pos.end-1);
+					}
+
+					p = seekNext(pos.begin - 1);
+					if (p < len) {
+						c = String.fromCharCode(k);
+						if (tests[p].test(c)) {
+							shiftR(p);
+
+							buffer[p] = c;
+							writeBuffer();
+							next = seekNext(p);
+
+							if(android){
+								setTimeout($.proxy($.fn.caret,input,next),0);
+							}else{
+								input.caret(next);
+							}
+
+							if (settings.completed && next >= len) {
+								settings.completed.call(input);
+							}
+						}
+					}
+					e.preventDefault();
+				}
+			}
+
+			function clearBuffer(start, end) {
+				var i;
+				for (i = start; i < end && i < len; i++) {
+					if (tests[i]) {
+						buffer[i] = settings.placeholder;
+					}
+				}
+			}
+
+			function writeBuffer() { input.val(buffer.join('')); }
+
+			function checkVal(allow) {
+				//try to place characters where they belong
+				var test = input.val(),
+					lastMatch = -1,
+					i,
+					c;
+
+				for (i = 0, pos = 0; i < len; i++) {
+					if (tests[i]) {
+						buffer[i] = settings.placeholder;
+						while (pos++ < test.length) {
+							c = test.charAt(pos - 1);
+							if (tests[i].test(c)) {
+								buffer[i] = c;
+								lastMatch = i;
+								break;
+							}
+						}
+						if (pos > test.length) {
+							break;
+						}
+					} else if (buffer[i] === test.charAt(pos) && i !== partialPosition) {
+						pos++;
+						lastMatch = i;
+					}
+				}
+				if (allow) {
+					writeBuffer();
+				} else if (lastMatch + 1 < partialPosition) {
+					input.val("");
+					clearBuffer(0, len);
+				} else {
+					writeBuffer();
+					input.val(input.val().substring(0, lastMatch + 1));
+				}
+				return (partialPosition ? i : firstNonMaskPos);
+			}
+
+			input.data($.mask.dataName,function(){
+				return $.map(buffer, function(c, i) {
+					return tests[i]&&c!=settings.placeholder ? c : null;
+				}).join('');
+			});
+
+			if (!input.attr("readonly"))
+				input
+				.one("unmask", function() {
+					input
+						.unbind(".mask")
+						.removeData($.mask.dataName);
+				})
+				.bind("focus.mask", function() {
+					clearTimeout(caretTimeoutId);
+					var pos,
+						moveCaret;
+
+					focusText = input.val();
+					pos = checkVal();
+					
+					caretTimeoutId = setTimeout(function(){
+						writeBuffer();
+						if (pos == mask.length) {
+							input.caret(0, pos);
+						} else {
+							input.caret(pos);
+						}
+					}, 10);
+				})
+				.bind("blur.mask", function() {
+					checkVal();
+					if (input.val() != focusText)
+						input.change();
+				})
+				.bind("keydown.mask", keydownEvent)
+				.bind("keypress.mask", keypressEvent)
+				.bind(pasteEventName, function() {
+					setTimeout(function() {
+						var pos=checkVal(true);
+						input.caret(pos);
+						if (settings.completed && pos == input.val().length)
+							settings.completed.call(input);
+					}, 0);
+				});
+			checkVal(); //Perform initial check for existing values
+		});
+	}
+});
+
+
+})(jQuery);

+ 278 - 0
php-yii2/app/vendor/yiisoft/yii2/assets/punycode/LICENSE-GPL.txt

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

+ 20 - 0
php-yii2/app/vendor/yiisoft/yii2/assets/punycode/LICENSE-MIT.txt

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

+ 502 - 0
php-yii2/app/vendor/yiisoft/yii2/assets/punycode/punycode.js

@@ -0,0 +1,502 @@
+/*! http://mths.be/punycode v1.2.1 by @mathias */
+;(function(root) {
+
+	/** Detect free variables */
+	var freeExports = typeof exports == 'object' && exports;
+	var freeModule = typeof module == 'object' && module &&
+		module.exports == freeExports && module;
+	var freeGlobal = typeof global == 'object' && global;
+	if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
+		root = freeGlobal;
+	}
+
+	/**
+	 * The `punycode` object.
+	 * @name punycode
+	 * @type Object
+	 */
+	var punycode,
+
+	/** Highest positive signed 32-bit float value */
+	maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
+
+	/** Bootstring parameters */
+	base = 36,
+	tMin = 1,
+	tMax = 26,
+	skew = 38,
+	damp = 700,
+	initialBias = 72,
+	initialN = 128, // 0x80
+	delimiter = '-', // '\x2D'
+
+	/** Regular expressions */
+	regexPunycode = /^xn--/,
+	regexNonASCII = /[^ -~]/, // unprintable ASCII chars + non-ASCII chars
+	regexSeparators = /\x2E|\u3002|\uFF0E|\uFF61/g, // RFC 3490 separators
+
+	/** Error messages */
+	errors = {
+		'overflow': 'Overflow: input needs wider integers to process',
+		'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
+		'invalid-input': 'Invalid input'
+	},
+
+	/** Convenience shortcuts */
+	baseMinusTMin = base - tMin,
+	floor = Math.floor,
+	stringFromCharCode = String.fromCharCode,
+
+	/** Temporary variable */
+	key;
+
+	/*--------------------------------------------------------------------------*/
+
+	/**
+	 * A generic error utility function.
+	 * @private
+	 * @param {String} type The error type.
+	 * @returns {Error} Throws a `RangeError` with the applicable error message.
+	 */
+	function error(type) {
+		throw RangeError(errors[type]);
+	}
+
+	/**
+	 * A generic `Array#map` utility function.
+	 * @private
+	 * @param {Array} array The array to iterate over.
+	 * @param {Function} callback The function that gets called for every array
+	 * item.
+	 * @returns {Array} A new array of values returned by the callback function.
+	 */
+	function map(array, fn) {
+		var length = array.length;
+		while (length--) {
+			array[length] = fn(array[length]);
+		}
+		return array;
+	}
+
+	/**
+	 * A simple `Array#map`-like wrapper to work with domain name strings.
+	 * @private
+	 * @param {String} domain The domain name.
+	 * @param {Function} callback The function that gets called for every
+	 * character.
+	 * @returns {Array} A new string of characters returned by the callback
+	 * function.
+	 */
+	function mapDomain(string, fn) {
+		return map(string.split(regexSeparators), fn).join('.');
+	}
+
+	/**
+	 * Creates an array containing the decimal code points of each Unicode
+	 * character in the string. While JavaScript uses UCS-2 internally,
+	 * this function will convert a pair of surrogate halves (each of which
+	 * UCS-2 exposes as separate characters) into a single code point,
+	 * matching UTF-16.
+	 * @see `punycode.ucs2.encode`
+	 * @see <http://mathiasbynens.be/notes/javascript-encoding>
+	 * @memberOf punycode.ucs2
+	 * @name decode
+	 * @param {String} string The Unicode input string (UCS-2).
+	 * @returns {Array} The new array of code points.
+	 */
+	function ucs2decode(string) {
+		var output = [],
+		    counter = 0,
+		    length = string.length,
+		    value,
+		    extra;
+		while (counter < length) {
+			value = string.charCodeAt(counter++);
+			if ((value & 0xF800) == 0xD800 && counter < length) {
+				// high surrogate, and there is a next character
+				extra = string.charCodeAt(counter++);
+				if ((extra & 0xFC00) == 0xDC00) { // low surrogate
+					output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
+				} else {
+					output.push(value, extra);
+				}
+			} else {
+				output.push(value);
+			}
+		}
+		return output;
+	}
+
+	/**
+	 * Creates a string based on an array of decimal code points.
+	 * @see `punycode.ucs2.decode`
+	 * @memberOf punycode.ucs2
+	 * @name encode
+	 * @param {Array} codePoints The array of decimal code points.
+	 * @returns {String} The new Unicode string (UCS-2).
+	 */
+	function ucs2encode(array) {
+		return map(array, function(value) {
+			var output = '';
+			if (value > 0xFFFF) {
+				value -= 0x10000;
+				output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
+				value = 0xDC00 | value & 0x3FF;
+			}
+			output += stringFromCharCode(value);
+			return output;
+		}).join('');
+	}
+
+	/**
+	 * Converts a basic code point into a digit/integer.
+	 * @see `digitToBasic()`
+	 * @private
+	 * @param {Number} codePoint The basic (decimal) code point.
+	 * @returns {Number} The numeric value of a basic code point (for use in
+	 * representing integers) in the range `0` to `base - 1`, or `base` if
+	 * the code point does not represent a value.
+	 */
+	function basicToDigit(codePoint) {
+		return codePoint - 48 < 10
+			? codePoint - 22
+			: codePoint - 65 < 26
+				? codePoint - 65
+				: codePoint - 97 < 26
+					? codePoint - 97
+					: base;
+	}
+
+	/**
+	 * Converts a digit/integer into a basic code point.
+	 * @see `basicToDigit()`
+	 * @private
+	 * @param {Number} digit The numeric value of a basic code point.
+	 * @returns {Number} The basic code point whose value (when used for
+	 * representing integers) is `digit`, which needs to be in the range
+	 * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
+	 * used; else, the lowercase form is used. The behavior is undefined
+	 * if flag is non-zero and `digit` has no uppercase form.
+	 */
+	function digitToBasic(digit, flag) {
+		//  0..25 map to ASCII a..z or A..Z
+		// 26..35 map to ASCII 0..9
+		return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
+	}
+
+	/**
+	 * Bias adaptation function as per section 3.4 of RFC 3492.
+	 * http://tools.ietf.org/html/rfc3492#section-3.4
+	 * @private
+	 */
+	function adapt(delta, numPoints, firstTime) {
+		var k = 0;
+		delta = firstTime ? floor(delta / damp) : delta >> 1;
+		delta += floor(delta / numPoints);
+		for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
+			delta = floor(delta / baseMinusTMin);
+		}
+		return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
+	}
+
+	/**
+	 * Converts a Punycode string of ASCII code points to a string of Unicode
+	 * code points.
+	 * @memberOf punycode
+	 * @param {String} input The Punycode string of ASCII code points.
+	 * @returns {String} The resulting string of Unicode code points.
+	 */
+	function decode(input) {
+		// Don't use UCS-2
+		var output = [],
+		    inputLength = input.length,
+		    out,
+		    i = 0,
+		    n = initialN,
+		    bias = initialBias,
+		    basic,
+		    j,
+		    index,
+		    oldi,
+		    w,
+		    k,
+		    digit,
+		    t,
+		    length,
+		    /** Cached calculation results */
+		    baseMinusT;
+
+		// Handle the basic code points: let `basic` be the number of input code
+		// points before the last delimiter, or `0` if there is none, then copy
+		// the first basic code points to the output.
+
+		basic = input.lastIndexOf(delimiter);
+		if (basic < 0) {
+			basic = 0;
+		}
+
+		for (j = 0; j < basic; ++j) {
+			// if it's not a basic code point
+			if (input.charCodeAt(j) >= 0x80) {
+				error('not-basic');
+			}
+			output.push(input.charCodeAt(j));
+		}
+
+		// Main decoding loop: start just after the last delimiter if any basic code
+		// points were copied; start at the beginning otherwise.
+
+		for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
+
+			// `index` is the index of the next character to be consumed.
+			// Decode a generalized variable-length integer into `delta`,
+			// which gets added to `i`. The overflow checking is easier
+			// if we increase `i` as we go, then subtract off its starting
+			// value at the end to obtain `delta`.
+			for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
+
+				if (index >= inputLength) {
+					error('invalid-input');
+				}
+
+				digit = basicToDigit(input.charCodeAt(index++));
+
+				if (digit >= base || digit > floor((maxInt - i) / w)) {
+					error('overflow');
+				}
+
+				i += digit * w;
+				t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+
+				if (digit < t) {
+					break;
+				}
+
+				baseMinusT = base - t;
+				if (w > floor(maxInt / baseMinusT)) {
+					error('overflow');
+				}
+
+				w *= baseMinusT;
+
+			}
+
+			out = output.length + 1;
+			bias = adapt(i - oldi, out, oldi == 0);
+
+			// `i` was supposed to wrap around from `out` to `0`,
+			// incrementing `n` each time, so we'll fix that now:
+			if (floor(i / out) > maxInt - n) {
+				error('overflow');
+			}
+
+			n += floor(i / out);
+			i %= out;
+
+			// Insert `n` at position `i` of the output
+			output.splice(i++, 0, n);
+
+		}
+
+		return ucs2encode(output);
+	}
+
+	/**
+	 * Converts a string of Unicode code points to a Punycode string of ASCII
+	 * code points.
+	 * @memberOf punycode
+	 * @param {String} input The string of Unicode code points.
+	 * @returns {String} The resulting Punycode string of ASCII code points.
+	 */
+	function encode(input) {
+		var n,
+		    delta,
+		    handledCPCount,
+		    basicLength,
+		    bias,
+		    j,
+		    m,
+		    q,
+		    k,
+		    t,
+		    currentValue,
+		    output = [],
+		    /** `inputLength` will hold the number of code points in `input`. */
+		    inputLength,
+		    /** Cached calculation results */
+		    handledCPCountPlusOne,
+		    baseMinusT,
+		    qMinusT;
+
+		// Convert the input in UCS-2 to Unicode
+		input = ucs2decode(input);
+
+		// Cache the length
+		inputLength = input.length;
+
+		// Initialize the state
+		n = initialN;
+		delta = 0;
+		bias = initialBias;
+
+		// Handle the basic code points
+		for (j = 0; j < inputLength; ++j) {
+			currentValue = input[j];
+			if (currentValue < 0x80) {
+				output.push(stringFromCharCode(currentValue));
+			}
+		}
+
+		handledCPCount = basicLength = output.length;
+
+		// `handledCPCount` is the number of code points that have been handled;
+		// `basicLength` is the number of basic code points.
+
+		// Finish the basic string - if it is not empty - with a delimiter
+		if (basicLength) {
+			output.push(delimiter);
+		}
+
+		// Main encoding loop:
+		while (handledCPCount < inputLength) {
+
+			// All non-basic code points < n have been handled already. Find the next
+			// larger one:
+			for (m = maxInt, j = 0; j < inputLength; ++j) {
+				currentValue = input[j];
+				if (currentValue >= n && currentValue < m) {
+					m = currentValue;
+				}
+			}
+
+			// Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
+			// but guard against overflow
+			handledCPCountPlusOne = handledCPCount + 1;
+			if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
+				error('overflow');
+			}
+
+			delta += (m - n) * handledCPCountPlusOne;
+			n = m;
+
+			for (j = 0; j < inputLength; ++j) {
+				currentValue = input[j];
+
+				if (currentValue < n && ++delta > maxInt) {
+					error('overflow');
+				}
+
+				if (currentValue == n) {
+					// Represent delta as a generalized variable-length integer
+					for (q = delta, k = base; /* no condition */; k += base) {
+						t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+						if (q < t) {
+							break;
+						}
+						qMinusT = q - t;
+						baseMinusT = base - t;
+						output.push(
+							stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
+						);
+						q = floor(qMinusT / baseMinusT);
+					}
+
+					output.push(stringFromCharCode(digitToBasic(q, 0)));
+					bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
+					delta = 0;
+					++handledCPCount;
+				}
+			}
+
+			++delta;
+			++n;
+
+		}
+		return output.join('');
+	}
+
+	/**
+	 * Converts a Punycode string representing a domain name to Unicode. Only the
+	 * Punycoded parts of the domain name will be converted, i.e. it doesn't
+	 * matter if you call it on a string that has already been converted to
+	 * Unicode.
+	 * @memberOf punycode
+	 * @param {String} domain The Punycode domain name to convert to Unicode.
+	 * @returns {String} The Unicode representation of the given Punycode
+	 * string.
+	 */
+	function toUnicode(domain) {
+		return mapDomain(domain, function(string) {
+			return regexPunycode.test(string)
+				? decode(string.slice(4).toLowerCase())
+				: string;
+		});
+	}
+
+	/**
+	 * Converts a Unicode string representing a domain name to Punycode. Only the
+	 * non-ASCII parts of the domain name will be converted, i.e. it doesn't
+	 * matter if you call it with a domain that's already in ASCII.
+	 * @memberOf punycode
+	 * @param {String} domain The domain name to convert, as a Unicode string.
+	 * @returns {String} The Punycode representation of the given domain name.
+	 */
+	function toASCII(domain) {
+		return mapDomain(domain, function(string) {
+			return regexNonASCII.test(string)
+				? 'xn--' + encode(string)
+				: string;
+		});
+	}
+
+	/*--------------------------------------------------------------------------*/
+
+	/** Define the public API */
+	punycode = {
+		/**
+		 * A string representing the current Punycode.js version number.
+		 * @memberOf punycode
+		 * @type String
+		 */
+		'version': '1.2.1',
+		/**
+		 * An object of methods to convert from JavaScript's internal character
+		 * representation (UCS-2) to decimal Unicode code points, and back.
+		 * @see <http://mathiasbynens.be/notes/javascript-encoding>
+		 * @memberOf punycode
+		 * @type Object
+		 */
+		'ucs2': {
+			'decode': ucs2decode,
+			'encode': ucs2encode
+		},
+		'decode': decode,
+		'encode': encode,
+		'toASCII': toASCII,
+		'toUnicode': toUnicode
+	};
+
+	/** Expose `punycode` */
+	// Some AMD build optimizers, like r.js, check for specific condition patterns
+	// like the following:
+	if (
+		typeof define == 'function' &&
+		typeof define.amd == 'object' &&
+		define.amd
+	) {
+		define(function() {
+			return punycode;
+		});
+	}	else if (freeExports && !freeExports.nodeType) {
+		if (freeModule) { // in Node.js or RingoJS v0.8.0+
+			freeModule.exports = punycode;
+		} else { // in Narwhal or RingoJS v0.7.0-
+			for (key in punycode) {
+				punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
+			}
+		}
+	} else { // in Rhino or a web browser
+		root.punycode = punycode;
+	}
+
+}(this));

File diff suppressed because it is too large
+ 1 - 0
php-yii2/app/vendor/yiisoft/yii2/assets/punycode/punycode.min.js


+ 404 - 0
php-yii2/app/vendor/yiisoft/yii2/assets/yii.activeForm.js

@@ -0,0 +1,404 @@
+/**
+ * Yii form widget.
+ *
+ * This is the JavaScript widget used by the yii\widgets\ActiveForm widget.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+(function ($) {
+	
+	$.fn.yiiActiveForm = function (method) {
+		if (methods[method]) {
+			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+		} else if (typeof method === 'object' || !method) {
+			return methods.init.apply(this, arguments);
+		} else {
+			$.error('Method ' + method + ' does not exist on jQuery.yiiActiveForm');
+			return false;
+		}
+	};
+
+	var defaults = {
+		// the jQuery selector for the error summary
+		errorSummary: undefined,
+		// whether to perform validation before submitting the form.
+		validateOnSubmit: true,
+		// the container CSS class representing the corresponding attribute has validation error
+		errorCssClass: 'error',
+		// the container CSS class representing the corresponding attribute passes validation
+		successCssClass: 'success',
+		// the container CSS class representing the corresponding attribute is being validated
+		validatingCssClass: 'validating',
+		// the URL for performing AJAX-based validation. If not set, it will use the the form's action
+		validationUrl: undefined,
+		// a callback that is called before submitting the form. The signature of the callback should be:
+		// function ($form) { ...return false to cancel submission...}
+		beforeSubmit: undefined,
+		// a callback that is called before validating each attribute. The signature of the callback should be:
+		// function ($form, attribute, messages) { ...return false to cancel the validation...}
+		beforeValidate: undefined,
+		// a callback that is called after an attribute is validated. The signature of the callback should be:
+		// function ($form, attribute, messages)
+		afterValidate: undefined,
+		// the GET parameter name indicating an AJAX-based validation
+		ajaxVar: 'ajax'
+	};
+
+	var attributeDefaults = {
+		// attribute name or expression (e.g. "[0]content" for tabular input)
+		name: undefined,
+		// the jQuery selector of the container of the input field
+		container: undefined,
+		// the jQuery selector of the input field
+		input: undefined,
+		// the jQuery selector of the error tag
+		error: undefined,
+		// whether to perform validation when a change is detected on the input
+		validateOnChange: false,
+		// whether to perform validation when the user is typing.
+		validateOnType: false,
+		// number of milliseconds that the validation should be delayed when a user is typing in the input field.
+		validationDelay: 200,
+		// whether to enable AJAX-based validation.
+		enableAjaxValidation: false,
+		// function (attribute, value, messages), the client-side validation function.
+		validate: undefined,
+		// status of the input field, 0: empty, not entered before, 1: validated, 2: pending validation, 3: validating
+		status: 0,
+		// the value of the input
+		value: undefined
+	};
+
+	var methods = {
+		init: function (attributes, options) {
+			return this.each(function () {
+				var $form = $(this);
+				if ($form.data('yiiActiveForm')) {
+					return;
+				}
+
+				var settings = $.extend({}, defaults, options || {});
+				if (settings.validationUrl === undefined) {
+					settings.validationUrl = $form.prop('action');
+				}
+				$.each(attributes, function (i) {
+					attributes[i] = $.extend({value: getValue($form, this)}, attributeDefaults, this);
+				});
+				$form.data('yiiActiveForm', {
+					settings: settings,
+					attributes: attributes,
+					submitting: false,
+					validated: false
+				});
+
+				watchAttributes($form, attributes);
+
+				/**
+				 * Clean up error status when the form is reset.
+				 * Note that $form.on('reset', ...) does work because the "reset" event does not bubble on IE.
+				 */
+				$form.bind('reset.yiiActiveForm', methods.resetForm);
+
+				if (settings.validateOnSubmit) {
+					$form.on('mouseup.yiiActiveForm keyup.yiiActiveForm', ':submit', function () {
+						$form.data('yiiActiveForm').submitObject = $(this);
+					});
+					$form.on('submit', methods.submitForm);
+				}
+			});
+		},
+
+		destroy: function () {
+			return this.each(function () {
+				$(window).unbind('.yiiActiveForm');
+				$(this).removeData('yiiActiveForm');
+			});
+		},
+
+		data: function() {
+			return this.data('yiiActiveForm');
+		},
+
+		submitForm: function () {
+			var $form = $(this),
+				data = $form.data('yiiActiveForm');
+			if (data.validated) {
+				// continue submitting the form since validation passes
+				return true;
+			}
+
+			if (data.settings.timer !== undefined) {
+				clearTimeout(data.settings.timer);
+			}
+			data.submitting = true;
+			if (!data.settings.beforeSubmit || data.settings.beforeSubmit($form)) {
+				validate($form, function (messages) {
+					var errors = [];
+					$.each(data.attributes, function () {
+						if (updateInput($form, this, messages)) {
+							errors.push(this.input);
+						}
+					});
+					updateSummary($form, messages);
+					if (errors.length) {
+						var top = $form.find(errors.join(',')).first().offset().top;
+						var wtop = $(window).scrollTop();
+						if (top < wtop || top > wtop + $(window).height) {
+							$(window).scrollTop(top);
+						}
+					} else {
+						data.validated = true;
+						var $button = data.submitObject || $form.find(':submit:first');
+						// TODO: if the submission is caused by "change" event, it will not work
+						if ($button.length) {
+							$button.click();
+						} else {
+							// no submit button in the form
+							$form.submit();
+						}
+						return;
+					}
+					data.submitting = false;
+				}, function () {
+					data.submitting = false;
+				});
+			} else {
+				data.submitting = false;
+			}
+			return false;
+		},
+
+		resetForm: function () {
+			var $form = $(this);
+			var data = $form.data('yiiActiveForm');
+			// Because we bind directly to a form reset event instead of a reset button (that may not exist),
+			// when this function is executed form input values have not been reset yet.
+			// Therefore we do the actual reset work through setTimeout.
+			setTimeout(function () {
+				$.each(data.attributes, function () {
+					// Without setTimeout() we would get the input values that are not reset yet.
+					this.value = getValue($form, this);
+					this.status = 0;
+					var $container = $form.find(this.container);
+					$container.removeClass(
+						data.settings.validatingCssClass + ' ' +
+							data.settings.errorCssClass + ' ' +
+							data.settings.successCssClass
+					);
+					$container.find(this.error).html('');
+				});
+				$form.find(data.settings.summary).hide().find('ul').html('');
+			}, 1);
+		}
+	};
+
+	var watchAttributes = function ($form, attributes) {
+		$.each(attributes, function (i, attribute) {
+			var $input = findInput($form, attribute);
+			if (attribute.validateOnChange) {
+				$input.on('change.yiiActiveForm', function () {
+					validateAttribute($form, attribute, false);
+				}).on('blur.yiiActiveForm', function () {
+					if (attribute.status == 0 || attribute.status == 1) {
+						validateAttribute($form, attribute, !attribute.status);
+					}
+				});
+			}
+			if (attribute.validateOnType) {
+				$input.on('keyup.yiiActiveForm', function () {
+					if (attribute.value !== getValue($form, attribute)) {
+						validateAttribute($form, attribute, false);
+					}
+				});
+			}
+		});
+	};
+
+	var validateAttribute = function ($form, attribute, forceValidate) {
+		var data = $form.data('yiiActiveForm');
+
+		if (forceValidate) {
+			attribute.status = 2;
+		}
+		$.each(data.attributes, function () {
+			if (this.value !== getValue($form, this)) {
+				this.status = 2;
+				forceValidate = true;
+			}
+		});
+		if (!forceValidate) {
+			return;
+		}
+
+		if (data.settings.timer !== undefined) {
+			clearTimeout(data.settings.timer);
+		}
+		data.settings.timer = setTimeout(function () {
+			if (data.submitting || $form.is(':hidden')) {
+				return;
+			}
+			$.each(data.attributes, function () {
+				if (this.status === 2) {
+					this.status = 3;
+					$form.find(this.container).addClass(data.settings.validatingCssClass);
+				}
+			});
+			validate($form, function (messages) {
+				var hasError = false;
+				$.each(data.attributes, function () {
+					if (this.status === 2 || this.status === 3) {
+						hasError = updateInput($form, this, messages) || hasError;
+					}
+				});
+			});
+		}, data.settings.validationDelay);
+	};
+	
+	/**
+	 * Performs validation.
+	 * @param $form jQuery the jquery representation of the form
+	 * @param successCallback function the function to be invoked if the validation completes
+	 * @param errorCallback function the function to be invoked if the ajax validation request fails
+	 */
+	var validate = function ($form, successCallback, errorCallback) {
+		var data = $form.data('yiiActiveForm'),
+			needAjaxValidation = false,
+			messages = {};
+
+		$.each(data.attributes, function () {
+			if (data.submitting || this.status === 2 || this.status === 3) {
+				var msg = [];
+				if (!data.settings.beforeValidate || data.settings.beforeValidate($form, this, msg)) {
+					if (this.validate) {
+						this.validate(this, getValue($form, this), msg);
+					}
+					if (msg.length) {
+						messages[this.name] = msg;
+					} else if (this.enableAjaxValidation) {
+						needAjaxValidation = true;
+					}
+				}
+			}
+		});
+
+		if (needAjaxValidation && (!data.submitting || $.isEmptyObject(messages))) {
+			// Perform ajax validation when at least one input needs it.
+			// If the validation is triggered by form submission, ajax validation
+			// should be done only when all inputs pass client validation
+			var $button = data.submitObject,
+				extData = '&' + data.settings.ajaxVar + '=' + $form.prop('id');
+			if ($button && $button.length && $button.prop('name')) {
+				extData += '&' + $button.prop('name') + '=' + $button.prop('value');
+			}
+			$.ajax({
+				url: data.settings.validationUrl,
+				type: $form.prop('method'),
+				data: $form.serialize() + extData,
+				dataType: 'json',
+				success: function (msgs) {
+					if (msgs !== null && typeof msgs === 'object') {
+						$.each(data.attributes, function () {
+							if (!this.enableAjaxValidation) {
+								delete msgs[this.name];
+							}
+						});
+						successCallback($.extend({}, messages, msgs));
+					} else {
+						successCallback(messages);
+					}
+				},
+				error: errorCallback
+			});
+		} else if (data.submitting) {
+			// delay callback so that the form can be submitted without problem
+			setTimeout(function () {
+				successCallback(messages);
+			}, 200);
+		} else {
+			successCallback(messages);
+		}
+	};
+
+	/**
+	 * Updates the error message and the input container for a particular attribute.
+	 * @param $form the form jQuery object
+	 * @param attribute object the configuration for a particular attribute.
+	 * @param messages array the validation error messages
+	 * @return boolean whether there is a validation error for the specified attribute
+	 */
+	var updateInput = function ($form, attribute, messages) {
+		var data = $form.data('yiiActiveForm'),
+			$input = findInput($form, attribute),
+			hasError = false;
+
+		if (data.settings.afterValidate) {
+			data.settings.afterValidate($form, attribute, messages);
+		}
+		attribute.status = 1;
+		if ($input.length) {
+			hasError = messages && $.isArray(messages[attribute.name]) && messages[attribute.name].length;
+			var $container = $form.find(attribute.container);
+			var $error = $container.find(attribute.error);
+			if (hasError) {
+				$error.text(messages[attribute.name][0]);
+				$container.removeClass(data.settings.validatingCssClass + ' ' + data.settings.successCssClass)
+					.addClass(data.settings.errorCssClass);
+			} else {
+				$error.text('');
+				$container.removeClass(data.settings.validatingCssClass + ' ' + data.settings.errorCssClass + ' ')
+					.addClass(data.settings.successCssClass);
+			}
+			attribute.value = getValue($form, attribute);
+		}
+		return hasError;
+	};
+
+	/**
+	 * Updates the error summary.
+	 * @param $form the form jQuery object
+	 * @param messages array the validation error messages
+	 */
+	var updateSummary = function ($form, messages) {
+		var data = $form.data('yiiActiveForm'),
+			$summary = $form.find(data.settings.errorSummary),
+			$ul = $summary.find('ul').html('');
+
+		if ($summary.length && messages) {
+			$.each(data.attributes, function () {
+				if ($.isArray(messages[this.name]) && messages[this.name].length) {
+					$ul.append($('<li/>').text(messages[this.name][0]));
+				}
+			});
+			$summary.toggle($ul.find('li').length > 0);
+		}
+	};
+
+	var getValue = function ($form, attribute) {
+		var $input = findInput($form, attribute);
+		var type = $input.prop('type');
+		if (type === 'checkbox' || type === 'radio') {
+			var $realInput = $input.filter(':checked');
+			if (!$realInput.length) {
+				$realInput = $form.find('input[type=hidden][name="'+$input.prop('name')+'"]');
+			}
+			return $realInput.val();
+		} else {
+			return $input.val();
+		}
+	};
+
+	var findInput = function ($form, attribute) {
+		var $input = $form.find(attribute.input);
+		if ($input.length && $input[0].tagName.toLowerCase() === 'div') {
+			// checkbox list or radio list
+			return $input.find('input');
+		} else {
+			return $input;
+		}
+	};
+
+})(window.jQuery);

+ 72 - 0
php-yii2/app/vendor/yiisoft/yii2/assets/yii.captcha.js

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

+ 139 - 0
php-yii2/app/vendor/yiisoft/yii2/assets/yii.gridView.js

@@ -0,0 +1,139 @@
+/**
+ * Yii GridView widget.
+ *
+ * This is the JavaScript widget used by the yii\grid\GridView widget.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+(function ($) {
+	$.fn.yiiGridView = function (method) {
+		if (methods[method]) {
+			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+		} else if (typeof method === 'object' || !method) {
+			return methods.init.apply(this, arguments);
+		} else {
+			$.error('Method ' + method + ' does not exist on jQuery.yiiGridView');
+			return false;
+		}
+	};
+
+	var defaults = {
+		filterUrl: undefined,
+		filterSelector: undefined
+	};
+
+	var methods = {
+		init: function (options) {
+			return this.each(function () {
+				var $e = $(this);
+				var settings = $.extend({}, defaults, options || {});
+				$e.data('yiiGridView', {
+					settings: settings
+				});
+
+				var enterPressed = false;
+				$(document).on('change.yiiGridView keydown.yiiGridView', settings.filterSelector, function (event) {
+					if (event.type === 'keydown') {
+						if (event.keyCode !== 13) {
+							return; // only react to enter key
+						} else {
+							enterPressed = true;
+						}
+					} else {
+						// prevent processing for both keydown and change events
+						if (enterPressed) {
+							enterPressed = false;
+							return;
+						}
+					}
+					var data = $(settings.filterSelector).serialize();
+					var url = settings.filterUrl;
+					if (url.indexOf('?') >= 0) {
+						url += '&' + data;
+					} else {
+						url += '?' + data;
+					}
+					window.location.href = url;
+					return false;
+				});
+			});
+		},
+
+		setSelectionColumn: function (options) {
+			var $grid = $(this);
+			var data = $grid.data('yiiGridView');
+			data.selectionColumn = options.name;
+			if (!options.multiple) {
+				return;
+			}
+			$grid.on('click.yiiGridView', "input[name='" + options.checkAll + "']", function () {
+				$grid.find("input[name='" + options.name + "']:enabled").prop('checked', this.checked);
+			});
+			$grid.on('click.yiiGridView', "input[name='" + options.name + "']:enabled", function () {
+				var all = $grid.find("input[name='" + options.name + "']").length == $grid.find("input[name='" + options.name + "']:checked").length;
+				$grid.find("input[name='" + options.checkAll + "']").prop('checked', all);
+			});
+		},
+
+		getSelectedRows: function () {
+			var $grid = $(this);
+			var data = $grid.data('yiiGridView');
+			var keys = [];
+			if (data.selectionColumn) {
+				$grid.find("input[name='" + data.selectionColumn + "']:checked").each(function () {
+					keys.push($(this).parent().closest('tr').data('key'));
+				});
+			}
+			return keys;
+		},
+
+		destroy: function () {
+			return this.each(function () {
+				$(window).unbind('.yiiGridView');
+				$(this).removeData('yiiGridView');
+			});
+		},
+
+		data: function() {
+			return this.data('yiiGridView');
+		}
+	};
+
+	var enterPressed = false;
+
+	var filterChanged = function (event) {
+		if (event.type === 'keydown') {
+			if (event.keyCode !== 13) {
+				return; // only react to enter key
+			} else {
+				enterPressed = true;
+			}
+		} else {
+			// prevent processing for both keydown and change events
+			if (enterPressed) {
+				enterPressed = false;
+				return;
+			}
+		}
+		var data = $(settings.filterSelector).serialize();
+		if (settings.pageVar !== undefined) {
+			data += '&' + settings.pageVar + '=1';
+		}
+		if (settings.enableHistory && settings.ajaxUpdate !== false && window.History.enabled) {
+			// Ajaxify this link
+			var url = $('#' + id).yiiGridView('getUrl'),
+				params = $.deparam.querystring($.param.querystring(url, data));
+
+			delete params[settings.ajaxVar];
+			window.History.pushState(null, document.title, decodeURIComponent($.param.querystring(url.substr(0, url.indexOf('?')), params)));
+		} else {
+			$('#' + id).yiiGridView('update', {data: data});
+		}
+		return false;
+	};
+})(window.jQuery);
+

+ 245 - 0
php-yii2/app/vendor/yiisoft/yii2/assets/yii.js

@@ -0,0 +1,245 @@
+/**
+ * Yii JavaScript module.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+
+/**
+ * yii is the root module for all Yii JavaScript modules.
+ * It implements a mechanism of organizing JavaScript code in modules through the function "yii.initModule()".
+ *
+ * Each module should be named as "x.y.z", where "x" stands for the root module (for the Yii core code, this is "yii").
+ *
+ * A module may be structured as follows:
+ *
+ * ~~~
+ * yii.sample = (function($) {
+ *     var pub = {
+ *         // whether this module is currently active. If false, init() will not be called for this module
+ *         // it will also not be called for all its child modules. If this property is undefined, it means true.
+ *         isActive: true,
+ *         init: function() {
+ *             // ... module initialization code go here ...
+ *         },
+ *
+ *         // ... other public functions and properties go here ...
+ *     };
+ *
+ *     // ... private functions and properties go here ...
+ *
+ *     return pub;
+ * })(jQuery);
+ * ~~~
+ *
+ * Using this structure, you can define public and private functions/properties for a module.
+ * Private functions/properties are only visible within the module, while public functions/properties
+ * may be accessed outside of the module. For example, you can access "yii.sample.isActive".
+ *
+ * You must call "yii.initModule()" once for the root module of all your modules.
+ */
+yii = (function ($) {
+	var pub = {
+		/**
+		 * List of scripts that can be loaded multiple times via AJAX requests. Each script can be represented
+		 * as either an absolute URL or a relative one.
+		 */
+		reloadableScripts: [],
+		/**
+		 * The selector for clickable elements that need to support confirmation and form submission.
+		 */
+		clickableSelector: 'a, button, input[type="submit"], input[type="button"], input[type="reset"], input[type="image"]',
+		/**
+		 * The selector for changeable elements that need to support confirmation and form submission.
+		 */
+		changeableSelector: 'select, input, textarea',
+
+		/**
+		 * @return string|undefined the CSRF variable name. Undefined is returned if CSRF validation is not enabled.
+		 */
+		getCsrfVar: function () {
+			return $('meta[name=csrf-var]').prop('content');
+		},
+
+		/**
+		 * @return string|undefined the CSRF token. Undefined is returned if CSRF validation is not enabled.
+		 */
+		getCsrfToken: function () {
+			return $('meta[name=csrf-token]').prop('content');
+		},
+
+		/**
+		 * Displays a confirmation dialog.
+		 * The default implementation simply displays a js confirmation dialog.
+		 * You may override this by setting `yii.confirm`.
+		 * @param message the confirmation message.
+		 * @return boolean whether the user confirms with the message in the dialog
+		 */
+		confirm: function (message) {
+			return confirm(message);
+		},
+
+		/**
+		 * Returns a value indicating whether to allow executing the action defined for the specified element.
+		 * This method recognizes the `data-confirm` attribute of the element and uses it
+		 * as the message in a confirmation dialog. The method will return true if this special attribute
+		 * is not defined or if the user confirms the message.
+		 * @param $e the jQuery representation of the element
+		 * @return boolean whether to allow executing the action defined for the specified element.
+		 */
+		allowAction: function ($e) {
+			var message = $e.data('confirm');
+			return message === undefined || pub.confirm(message);
+		},
+
+		/**
+		 * Handles the action triggered by user.
+		 * This method recognizes the `data-method` attribute of the element. If the attribute exists,
+		 * the method will submit the form containing this element. If there is no containing form, a form
+		 * will be created and submitted using the method given by this attribute value (e.g. "post", "put").
+		 * For hyperlinks, the form action will take the value of the "href" attribute of the link.
+		 * For other elements, either the containing form action or the current page URL will be used
+		 * as the form action URL.
+		 *
+		 * If the `data-method` attribute is not defined, the default element action will be performed.
+		 *
+		 * @param $e the jQuery representation of the element
+		 * @return boolean whether to execute the default action for the element.
+		 */
+		handleAction: function ($e) {
+			var method = $e.data('method');
+			if (method === undefined) {
+				return true;
+			}
+
+			var $form = $e.closest('form');
+			var newForm = !$form.length;
+			if (newForm) {
+				var action = $e.prop('href');
+				if (!action || !action.match(/(^\/|:\/\/)/)) {
+					action = window.location.href;
+				}
+				$form = $('<form method="' + method + '" action="' + action + '"></form>');
+				var target = $e.prop('target');
+				if (target) {
+					$form.attr('target', target);
+				}
+				if (!method.match(/(get|post)/i)) {
+					$form.append('<input name="_method" value="' + method + '" type="hidden">');
+				}
+				var csrfVar = pub.getCsrfVar();
+				if (csrfVar) {
+					$form.append('<input name="' + csrfVar + '" value="' + pub.getCsrfToken() + '" type="hidden">');
+				}
+				$form.hide().appendTo('body');
+			}
+
+			var activeFormData = $form.data('yiiActiveForm');
+			if (activeFormData) {
+				// remember who triggers the form submission. This is used by yii.activeForm.js
+				activeFormData.submitObject = $e;
+			}
+
+			$form.trigger('submit');
+
+			if (newForm) {
+				$form.remove();
+			}
+
+			return false;
+		},
+
+		initModule: function (module) {
+			if (module.isActive === undefined || module.isActive) {
+				if ($.isFunction(module.init)) {
+					module.init();
+				}
+				$.each(module, function () {
+					if ($.isPlainObject(this)) {
+						pub.initModule(this);
+					}
+				});
+			}
+		},
+
+		init: function () {
+			initCsrfHandler();
+			initRedirectHandler();
+			initScriptFilter();
+			initDataMethods();
+		}
+	};
+
+	function initRedirectHandler() {
+		// handle AJAX redirection
+		$(document).ajaxComplete(function (event, xhr, settings) {
+			var url = xhr.getResponseHeader('X-Redirect');
+			if (url) {
+				window.location = url;
+			}
+		});
+	}
+
+	function initCsrfHandler() {
+		// automatically send CSRF token for all AJAX requests
+		$.ajaxPrefilter(function (options, originalOptions, xhr) {
+			if (!options.crossDomain && pub.getCsrfVar()) {
+				xhr.setRequestHeader('X-CSRF-Token', pub.getCsrfToken());
+			}
+		});
+	}
+
+	function initDataMethods() {
+		var $document = $(document);
+		// handle data-confirm and data-method for clickable elements
+		$document.on('click.yii', pub.clickableSelector, function (event) {
+			var $this = $(this);
+			if (pub.allowAction($this)) {
+				return pub.handleAction($this);
+			} else {
+				event.stopImmediatePropagation();
+				return false;
+			}
+		});
+
+		// handle data-confirm and data-method for changeable elements
+		$document.on('change.yii', pub.changeableSelector, function (event) {
+			var $this = $(this);
+			if (pub.allowAction($this)) {
+				return pub.handleAction($this);
+			} else {
+				event.stopImmediatePropagation();
+				return false;
+			}
+		});
+	}
+
+	function initScriptFilter() {
+		var hostInfo = location.protocol + '//' + location.host;
+		var loadedScripts = $('script[src]').map(function () {
+			return this.src.charAt(0) === '/' ? hostInfo + this.src : this.src;
+		}).toArray();
+		$.ajaxPrefilter('script', function (options, originalOptions, xhr) {
+			var url = options.url.charAt(0) === '/' ? hostInfo + options.url : options.url;
+			if ($.inArray(url, loadedScripts) === -1) {
+				loadedScripts.push(url);
+			} else {
+				var found = $.inArray(url, $.map(pub.reloadableScripts, function (script) {
+					return script.charAt(0) === '/' ? hostInfo + script : script;
+				})) !== -1;
+				if (!found) {
+					xhr.abort();
+				}
+			}
+		});
+	}
+
+	return pub;
+})(jQuery);
+
+jQuery(document).ready(function () {
+	yii.initModule(yii);
+});

+ 227 - 0
php-yii2/app/vendor/yiisoft/yii2/assets/yii.validation.js

@@ -0,0 +1,227 @@
+/**
+ * Yii validation module.
+ *
+ * This JavaScript module provides the validation methods for the built-in validators.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+
+yii.validation = (function ($) {
+	var isEmpty = function (value, trim) {
+		return value === null || value === undefined || value == []
+			|| value === '' || trim && $.trim(value) === '';
+	};
+
+	var addMessage = function (messages, message, value) {
+		messages.push(message.replace(/\{value\}/g, value));
+	};
+
+	return {
+		required: function (value, messages, options) {
+			var valid = false;
+			if (options.requiredValue === undefined) {
+				if (options.strict && value !== undefined || !options.strict && !isEmpty(value, true)) {
+					valid = true;
+				}
+			} else if (!options.strict && value == options.requiredValue || options.strict && value === options.requiredValue) {
+				valid = true;
+			}
+
+			if (!valid) {
+				addMessage(messages, options.message, value);
+			}
+		},
+
+		boolean: function (value, messages, options) {
+			if (options.skipOnEmpty && isEmpty(value)) {
+				return;
+			}
+			var valid = !options.strict && (value == options.trueValue || value == options.falseValue)
+				|| options.strict && (value === options.trueValue || value === options.falseValue);
+
+			if (!valid) {
+				addMessage(messages, options.message, value);
+			}
+		},
+
+		string: function (value, messages, options) {
+			if (options.skipOnEmpty && isEmpty(value)) {
+				return;
+			}
+
+			if (typeof value !== 'string') {
+				addMessage(messages, options.message, value);
+				return;
+			}
+
+			if (options.min !== undefined && value.length < options.min) {
+				addMessage(messages, options.tooShort, value);
+			}
+			if (options.max !== undefined && value.length > options.max) {
+				addMessage(messages, options.tooLong, value);
+			}
+			if (options.is !== undefined && value.length != options.is) {
+				addMessage(messages, options.is, value);
+			}
+		},
+
+		number: function (value, messages, options) {
+			if (options.skipOnEmpty && isEmpty(value)) {
+				return;
+			}
+
+			if (typeof value === 'string' && !value.match(options.pattern)) {
+				addMessage(messages, options.message, value);
+				return;
+			}
+
+			if (options.min !== undefined && value < options.min) {
+				addMessage(messages, options.tooSmall, value);
+			}
+			if (options.max !== undefined && value > options.max) {
+				addMessage(messages, options.tooBig, value);
+			}
+		},
+
+		range: function (value, messages, options) {
+			if (options.skipOnEmpty && isEmpty(value)) {
+				return;
+			}
+			var valid = !options.not && $.inArray(value, options.range) > -1
+				|| options.not && $.inArray(value, options.range) == -1;
+
+			if (!valid) {
+				addMessage(messages, options.message, value);
+			}
+		},
+
+		regularExpression: function (value, messages, options) {
+			if (options.skipOnEmpty && isEmpty(value)) {
+				return;
+			}
+
+			if (!options.not && !value.match(options.pattern) || options.not && value.match(options.pattern)) {
+				addMessage(messages, options.message, value);
+			}
+		},
+
+		email: function (value, messages, options) {
+			if (options.skipOnEmpty && isEmpty(value)) {
+				return;
+			}
+
+			var valid = true;
+
+			if (options.enableIDN) {
+				var regexp = /^(.*<?)(.*)@(.*)(>?)$/,
+					matches = regexp.exec(value);
+				if (matches === null) {
+					valid = false;
+				} else {
+					value = matches[1] + punycode.toASCII(matches[2]) + '@' + punycode.toASCII(matches[3]) + matches[4];
+				}
+			}
+
+			if (!valid || !(value.match(options.pattern) || (options.allowName && value.match(options.fullPattern)))) {
+				addMessage(messages, options.message, value);
+			}
+		},
+
+		url: function (value, messages, options) {
+			if (options.skipOnEmpty && isEmpty(value)) {
+				return;
+			}
+
+			if (options.defaultScheme && !value.match(/:\/\//)) {
+				value = options.defaultScheme + '://' + value;
+			}
+
+			var valid = true;
+
+			if (options.enableIDN) {
+				var regexp = /^([^:]+):\/\/([^\/]+)(.*)$/,
+					matches = regexp.exec(value);
+				if (matches === null) {
+					valid = false;
+				} else {
+					value = matches[1] + '://' + punycode.toASCII(matches[2]) + matches[3];
+				}
+			}
+
+			if (!valid || !value.match(options.pattern)) {
+				addMessage(messages, options.message, value);
+			}
+		},
+
+		captcha: function (value, messages, options) {
+			if (options.skipOnEmpty && isEmpty(value)) {
+				return;
+			}
+
+			// CAPTCHA may be updated via AJAX and the updated hash is stored in body data
+			var hash = $('body').data(options.hashKey);
+			if (hash == null) {
+				hash = options.hash;
+			} else {
+				hash = hash[options.caseSensitive ? 0 : 1];
+			}
+			var v = options.caseSensitive ? value : value.toLowerCase();
+			for (var i = v.length - 1, h = 0; i >= 0; --i) {
+				h += v.charCodeAt(i);
+			}
+			if (h != hash) {
+				addMessage(messages, options.message, value);
+			}
+		},
+
+		compare: function (value, messages, options) {
+			if (options.skipOnEmpty && isEmpty(value)) {
+				return;
+			}
+
+			var compareValue, valid = true;
+			if (options.compareAttribute === undefined) {
+				compareValue = options.compareValue;
+			} else {
+				compareValue = $('#' + options.compareAttribute).val();
+			}
+			switch (options.operator) {
+				case '==':
+					valid = value == compareValue;
+					break;
+				case '===':
+					valid = value === compareValue;
+					break;
+				case '!=':
+					valid = value != compareValue;
+					break;
+				case '!==':
+					valid = value !== compareValue;
+					break;
+				case '>':
+					valid = value > compareValue;
+					break;
+				case '>=':
+					valid = value >= compareValue;
+					break;
+				case '<':
+					valid = value < compareValue;
+					break;
+				case '<=':
+					valid = value <= compareValue;
+					break;
+				default:
+					valid = false;
+					break;
+			}
+
+			if (!valid) {
+				addMessage(messages, options.message, value);
+			}
+		}
+	};
+})(jQuery);

+ 89 - 0
php-yii2/app/vendor/yiisoft/yii2/base/Action.php

@@ -0,0 +1,89 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+use Yii;
+
+/**
+ * Action is the base class for all controller action classes.
+ *
+ * Action provides a way to divide a complex controller into
+ * smaller actions in separate class files.
+ *
+ * Derived classes must implement a method named `run()`. This method
+ * will be invoked by the controller when the action is requested.
+ * The `run()` method can have parameters which will be filled up
+ * with user input values automatically according to their names.
+ * For example, if the `run()` method is declared as follows:
+ *
+ * ~~~
+ * public function run($id, $type = 'book') { ... }
+ * ~~~
+ *
+ * And the parameters provided for the action are: `['id' => 1]`.
+ * Then the `run()` method will be invoked as `run(1)` automatically.
+ *
+ * @property string $uniqueId The unique ID of this action among the whole application. This property is
+ * read-only.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Action extends Component
+{
+	/**
+	 * @var string ID of the action
+	 */
+	public $id;
+	/**
+	 * @var Controller the controller that owns this action
+	 */
+	public $controller;
+
+	/**
+	 * Constructor.
+	 * @param string $id the ID of this action
+	 * @param Controller $controller the controller that owns this action
+	 * @param array $config name-value pairs that will be used to initialize the object properties
+	 */
+	public function __construct($id, $controller, $config = [])
+	{
+		$this->id = $id;
+		$this->controller = $controller;
+		parent::__construct($config);
+	}
+
+	/**
+	 * Returns the unique ID of this action among the whole application.
+	 * @return string the unique ID of this action among the whole application.
+	 */
+	public function getUniqueId()
+	{
+		return $this->controller->getUniqueId() . '/' . $this->id;
+	}
+
+	/**
+	 * Runs this action with the specified parameters.
+	 * This method is mainly invoked by the controller.
+	 * @param array $params the parameters to be bound to the action's run() method.
+	 * @return mixed the result of the action
+	 * @throws InvalidConfigException if the action class does not have a run() method
+	 */
+	public function runWithParams($params)
+	{
+		if (!method_exists($this, 'run')) {
+			throw new InvalidConfigException(get_class($this) . ' must define a "run()" method.');
+		}
+		$args = $this->controller->bindActionParams($this, $params);
+		Yii::trace('Running action: ' . get_class($this) . '::run()', __METHOD__);
+		if (Yii::$app->requestedParams === null) {
+			Yii::$app->requestedParams = $args;
+		}
+		return call_user_func_array([$this, 'run'], $args);
+	}
+}

+ 45 - 0
php-yii2/app/vendor/yiisoft/yii2/base/ActionEvent.php

@@ -0,0 +1,45 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * ActionEvent represents the event parameter used for an action event.
+ *
+ * By setting the [[isValid]] property, one may control whether to continue running the action.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class ActionEvent extends Event
+{
+	/**
+	 * @var Action the action currently being executed
+	 */
+	public $action;
+	/**
+	 * @var mixed the action result. Event handlers may modify this property to change the action result.
+	 */
+	public $result;
+	/**
+	 * @var boolean whether to continue running the action. Event handlers of
+	 * [[Controller::EVENT_BEFORE_ACTION]] may set this property to decide whether
+	 * to continue running the current action.
+	 */
+	public $isValid = true;
+
+	/**
+	 * Constructor.
+	 * @param Action $action the action associated with this action event.
+	 * @param array $config name-value pairs that will be used to initialize the object properties
+	 */
+	public function __construct($action, $config = [])
+	{
+		$this->action = $action;
+		parent::__construct($config);
+	}
+}

+ 102 - 0
php-yii2/app/vendor/yiisoft/yii2/base/ActionFilter.php

@@ -0,0 +1,102 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * ActionFilter provides a base implementation for action filters that can be added to a controller
+ * to handle the `beforeAction` event.
+ *
+ * Check implementation of [[AccessControl]], [[PageCache]] and [[HttpCache]] as examples on how to use it.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class ActionFilter extends Behavior
+{
+	/**
+	 * @var array list of action IDs that this filter should apply to. If this property is not set,
+	 * then the filter applies to all actions, unless they are listed in [[except]].
+	 * If an action ID appears in both [[only]] and [[except]], this filter will NOT apply to it.
+	 * @see except
+	 */
+	public $only;
+	/**
+	 * @var array list of action IDs that this filter should not apply to.
+	 * @see only
+	 */
+	public $except = [];
+
+	/**
+	 * Declares event handlers for the [[owner]]'s events.
+	 * @return array events (array keys) and the corresponding event handler methods (array values).
+	 */
+	public function events()
+	{
+		return [
+			Controller::EVENT_BEFORE_ACTION => 'beforeFilter',
+			Controller::EVENT_AFTER_ACTION => 'afterFilter',
+		];
+	}
+
+	/**
+	 * @param ActionEvent $event
+	 * @return boolean
+	 */
+	public function beforeFilter($event)
+	{
+		if ($this->isActive($event->action)) {
+			$event->isValid = $this->beforeAction($event->action);
+			if (!$event->isValid) {
+				$event->handled = true;
+			}
+		}
+		return $event->isValid;
+	}
+
+	/**
+	 * @param ActionEvent $event
+	 * @return boolean
+	 */
+	public function afterFilter($event)
+	{
+		if ($this->isActive($event->action)) {
+			$this->afterAction($event->action, $event->result);
+		}
+	}
+
+	/**
+	 * This method is invoked right before an action is to be executed (after all possible filters.)
+	 * You may override this method to do last-minute preparation for the action.
+	 * @param Action $action the action to be executed.
+	 * @return boolean whether the action should continue to be executed.
+	 */
+	public function beforeAction($action)
+	{
+		return true;
+	}
+
+	/**
+	 * This method is invoked right after an action is executed.
+	 * You may override this method to do some postprocessing for the action.
+	 * @param Action $action the action just executed.
+	 * @param mixed $result the action execution result
+	 */
+	public function afterAction($action, &$result)
+	{
+	}
+
+	/**
+	 * Returns a value indicating whether the filer is active for the given action.
+	 * @param Action $action the action being filtered
+	 * @return boolean whether the filer is active for the given action.
+	 */
+	protected function isActive($action)
+	{
+		return !in_array($action->id, $this->except, true) && (empty($this->only) || in_array($action->id, $this->only, true));
+	}
+}

+ 646 - 0
php-yii2/app/vendor/yiisoft/yii2/base/Application.php

@@ -0,0 +1,646 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+use Yii;
+use yii\helpers\Console;
+use yii\web\HttpException;
+
+/**
+ * Application is the base class for all application classes.
+ *
+ * @property \yii\rbac\Manager $authManager The auth manager for this application. This property is read-only.
+ * @property string $basePath The root directory of the application.
+ * @property \yii\caching\Cache $cache The cache application component. Null if the component is not enabled.
+ * This property is read-only.
+ * @property \yii\db\Connection $db The database connection. This property is read-only.
+ * @property ErrorHandler $errorHandler The error handler application component. This property is read-only.
+ * @property \yii\base\Formatter $formatter The formatter application component. This property is read-only.
+ * @property \yii\i18n\I18N $i18n The internationalization component. This property is read-only.
+ * @property \yii\log\Logger $log The log component. This property is read-only.
+ * @property \yii\mail\MailerInterface $mail The mailer interface. This property is read-only.
+ * @property \yii\web\Request|\yii\console\Request $request The request component. This property is read-only.
+ * @property string $runtimePath The directory that stores runtime files. Defaults to the "runtime"
+ * subdirectory under [[basePath]].
+ * @property string $timeZone The time zone used by this application.
+ * @property string $uniqueId The unique ID of the module. This property is read-only.
+ * @property \yii\web\UrlManager $urlManager The URL manager for this application. This property is read-only.
+ * @property string $vendorPath The directory that stores vendor files. Defaults to "vendor" directory under
+ * [[basePath]].
+ * @property View|\yii\web\View $view The view object that is used to render various view files. This property
+ * is read-only.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+abstract class Application extends Module
+{
+	/**
+	 * @event Event an event raised before the application starts to handle a request.
+	 */
+	const EVENT_BEFORE_REQUEST = 'beforeRequest';
+	/**
+	 * @event Event an event raised after the application successfully handles a request (before the response is sent out).
+	 */
+	const EVENT_AFTER_REQUEST = 'afterRequest';
+	/**
+	 * @event ActionEvent an event raised before executing a controller action.
+	 * You may set [[ActionEvent::isValid]] to be false to cancel the action execution.
+	 */
+	const EVENT_BEFORE_ACTION = 'beforeAction';
+	/**
+	 * @event ActionEvent an event raised after executing a controller action.
+	 */
+	const EVENT_AFTER_ACTION = 'afterAction';
+
+	/**
+	 * @var string the namespace that controller classes are in. If not set,
+	 * it will use the "app\controllers" namespace.
+	 */
+	public $controllerNamespace = 'app\\controllers';
+
+	/**
+	 * @var string the application name.
+	 */
+	public $name = 'My Application';
+	/**
+	 * @var string the version of this application.
+	 */
+	public $version = '1.0';
+	/**
+	 * @var string the charset currently used for the application.
+	 */
+	public $charset = 'UTF-8';
+	/**
+	 * @var string the language that is meant to be used for end users.
+	 * @see sourceLanguage
+	 */
+	public $language = 'en-US';
+	/**
+	 * @var string the language that the application is written in. This mainly refers to
+	 * the language that the messages and view files are written in.
+	 * @see language
+	 */
+	public $sourceLanguage = 'en-US';
+	/**
+	 * @var Controller the currently active controller instance
+	 */
+	public $controller;
+	/**
+	 * @var string|boolean the layout that should be applied for views in this application. Defaults to 'main'.
+	 * If this is false, layout will be disabled.
+	 */
+	public $layout = 'main';
+	/**
+	 * @var integer the size of the reserved memory. A portion of memory is pre-allocated so that
+	 * when an out-of-memory issue occurs, the error handler is able to handle the error with
+	 * the help of this reserved memory. If you set this value to be 0, no memory will be reserved.
+	 * Defaults to 256KB.
+	 */
+	public $memoryReserveSize = 262144;
+	/**
+	 * @var string the requested route
+	 */
+	public $requestedRoute;
+	/**
+	 * @var Action the requested Action. If null, it means the request cannot be resolved into an action.
+	 */
+	public $requestedAction;
+	/**
+	 * @var array the parameters supplied to the requested action.
+	 */
+	public $requestedParams;
+	/**
+	 * @var array list of installed Yii extensions. Each array element represents a single extension
+	 * with the following structure:
+	 *
+	 * ~~~
+	 * [
+	 *     'name' => 'extension name',
+	 *     'version' => 'version number',
+	 *     'bootstrap' => 'BootstrapClassName',
+	 * ]
+	 * ~~~
+	 */
+	public $extensions = [];
+	/**
+	 * @var \Exception the exception that is being handled currently. When this is not null,
+	 * it means the application is handling some exception and extra care should be taken.
+	 */
+	public $exception;
+
+	/**
+	 * @var string Used to reserve memory for fatal error handler.
+	 */
+	private $_memoryReserve;
+
+	/**
+	 * Constructor.
+	 * @param array $config name-value pairs that will be used to initialize the object properties.
+	 * Note that the configuration must contain both [[id]] and [[basePath]].
+	 * @throws InvalidConfigException if either [[id]] or [[basePath]] configuration is missing.
+	 */
+	public function __construct($config = [])
+	{
+		Yii::$app = $this;
+
+		$this->preInit($config);
+		$this->registerErrorHandlers();
+		$this->registerCoreComponents();
+
+		Component::__construct($config);
+	}
+
+	/**
+	 * Pre-initializes the application.
+	 * This method is called at the beginning of the application constructor.
+	 * It initializes several important application properties.
+	 * If you override this method, please make sure you call the parent implementation.
+	 * @param array $config the application configuration
+	 * @throws InvalidConfigException if either [[id]] or [[basePath]] configuration is missing.
+	 */
+	public function preInit(&$config)
+	{
+		if (!isset($config['id'])) {
+			throw new InvalidConfigException('The "id" configuration is required.');
+		}
+		if (isset($config['basePath'])) {
+			$this->setBasePath($config['basePath']);
+			unset($config['basePath']);
+		} else {
+			throw new InvalidConfigException('The "basePath" configuration is required.');
+		}
+
+		if (isset($config['vendorPath'])) {
+			$this->setVendorPath($config['vendorPath']);
+			unset($config['vendorPath']);
+		} else {
+			// set "@vendor"
+			$this->getVendorPath();
+		}
+		if (isset($config['runtimePath'])) {
+			$this->setRuntimePath($config['runtimePath']);
+			unset($config['runtimePath']);
+		} else {
+			// set "@runtime"
+			$this->getRuntimePath();
+		}
+
+		if (isset($config['timeZone'])) {
+			$this->setTimeZone($config['timeZone']);
+			unset($config['timeZone']);
+		} elseif (!ini_get('date.timezone')) {
+			$this->setTimeZone('UTC');
+		}
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function init()
+	{
+		$this->initExtensions($this->extensions);
+		parent::init();
+	}
+
+	/**
+	 * Initializes the extensions.
+	 * @param array $extensions the extensions to be initialized. Please refer to [[extensions]]
+	 * for the structure of the extension array.
+	 */
+	protected function initExtensions($extensions)
+	{
+		foreach ($extensions as $extension) {
+			if (!empty($extension['alias'])) {
+				foreach ($extension['alias'] as $name => $path) {
+					Yii::setAlias($name, $path);
+				}
+			}
+			if (isset($extension['bootstrap'])) {
+				/** @var Extension $class */
+				$class = $extension['bootstrap'];
+				$class::init();
+			}
+		}
+	}
+
+	/**
+	 * Loads components that are declared in [[preload]].
+	 * @throws InvalidConfigException if a component or module to be preloaded is unknown
+	 */
+	public function preloadComponents()
+	{
+		$this->getComponent('log');
+		parent::preloadComponents();
+	}
+
+	/**
+	 * Registers error handlers.
+	 */
+	public function registerErrorHandlers()
+	{
+		if (YII_ENABLE_ERROR_HANDLER) {
+			ini_set('display_errors', 0);
+			set_exception_handler([$this, 'handleException']);
+			set_error_handler([$this, 'handleError'], error_reporting());
+			if ($this->memoryReserveSize > 0) {
+				$this->_memoryReserve = str_repeat('x', $this->memoryReserveSize);
+			}
+			register_shutdown_function([$this, 'handleFatalError']);
+		}
+	}
+
+	/**
+	 * Returns an ID that uniquely identifies this module among all modules within the current application.
+	 * Since this is an application instance, it will always return an empty string.
+	 * @return string the unique ID of the module.
+	 */
+	public function getUniqueId()
+	{
+		return '';
+	}
+
+	/**
+	 * Sets the root directory of the application and the @app alias.
+	 * This method can only be invoked at the beginning of the constructor.
+	 * @param string $path the root directory of the application.
+	 * @property string the root directory of the application.
+	 * @throws InvalidParamException if the directory does not exist.
+	 */
+	public function setBasePath($path)
+	{
+		parent::setBasePath($path);
+		Yii::setAlias('@app', $this->getBasePath());
+	}
+
+	/**
+	 * Runs the application.
+	 * This is the main entrance of an application.
+	 * @return integer the exit status (0 means normal, non-zero values mean abnormal)
+	 */
+	public function run()
+	{
+		$this->trigger(self::EVENT_BEFORE_REQUEST);
+		$response = $this->handleRequest($this->getRequest());
+		$this->trigger(self::EVENT_AFTER_REQUEST);
+		$response->send();
+		return $response->exitStatus;
+	}
+
+	/**
+	 * Handles the specified request.
+	 *
+	 * This method should return an instance of [[Response]] or its child class
+	 * which represents the handling result of the request.
+	 *
+	 * @param Request $request the request to be handled
+	 * @return Response the resulting response
+	 */
+	abstract public function handleRequest($request);
+
+
+	private $_runtimePath;
+
+	/**
+	 * Returns the directory that stores runtime files.
+	 * @return string the directory that stores runtime files.
+	 * Defaults to the "runtime" subdirectory under [[basePath]].
+	 */
+	public function getRuntimePath()
+	{
+		if ($this->_runtimePath === null) {
+			$this->setRuntimePath($this->getBasePath() . DIRECTORY_SEPARATOR . 'runtime');
+		}
+		return $this->_runtimePath;
+	}
+
+	/**
+	 * Sets the directory that stores runtime files.
+	 * @param string $path the directory that stores runtime files.
+	 */
+	public function setRuntimePath($path)
+	{
+		$this->_runtimePath = Yii::getAlias($path);
+		Yii::setAlias('@runtime', $this->_runtimePath);
+	}
+
+	private $_vendorPath;
+
+	/**
+	 * Returns the directory that stores vendor files.
+	 * @return string the directory that stores vendor files.
+	 * Defaults to "vendor" directory under [[basePath]].
+	 */
+	public function getVendorPath()
+	{
+		if ($this->_vendorPath === null) {
+			$this->setVendorPath($this->getBasePath() . DIRECTORY_SEPARATOR . 'vendor');
+		}
+		return $this->_vendorPath;
+	}
+
+	/**
+	 * Sets the directory that stores vendor files.
+	 * @param string $path the directory that stores vendor files.
+	 */
+	public function setVendorPath($path)
+	{
+		$this->_vendorPath = Yii::getAlias($path);
+		Yii::setAlias('@vendor', $this->_vendorPath);
+	}
+
+	/**
+	 * Returns the time zone used by this application.
+	 * This is a simple wrapper of PHP function date_default_timezone_get().
+	 * If time zone is not configured in php.ini or application config,
+	 * it will be set to UTC by default.
+	 * @return string the time zone used by this application.
+	 * @see http://php.net/manual/en/function.date-default-timezone-get.php
+	 */
+	public function getTimeZone()
+	{
+		return date_default_timezone_get();
+	}
+
+	/**
+	 * Sets the time zone used by this application.
+	 * This is a simple wrapper of PHP function date_default_timezone_set().
+	 * Refer to the [php manual](http://www.php.net/manual/en/timezones.php) for available timezones.
+	 * @param string $value the time zone used by this application.
+	 * @see http://php.net/manual/en/function.date-default-timezone-set.php
+	 */
+	public function setTimeZone($value)
+	{
+		date_default_timezone_set($value);
+	}
+
+	/**
+	 * Returns the database connection component.
+	 * @return \yii\db\Connection the database connection
+	 */
+	public function getDb()
+	{
+		return $this->getComponent('db');
+	}
+
+	/**
+	 * Returns the log component.
+	 * @return \yii\log\Logger the log component
+	 */
+	public function getLog()
+	{
+		return $this->getComponent('log');
+	}
+
+	/**
+	 * Returns the error handler component.
+	 * @return ErrorHandler the error handler application component.
+	 */
+	public function getErrorHandler()
+	{
+		return $this->getComponent('errorHandler');
+	}
+
+	/**
+	 * Returns the cache component.
+	 * @return \yii\caching\Cache the cache application component. Null if the component is not enabled.
+	 */
+	public function getCache()
+	{
+		return $this->getComponent('cache');
+	}
+
+	/**
+	 * Returns the formatter component.
+	 * @return \yii\base\Formatter the formatter application component.
+	 */
+	public function getFormatter()
+	{
+		return $this->getComponent('formatter');
+	}
+
+	/**
+	 * Returns the request component.
+	 * @return \yii\web\Request|\yii\console\Request the request component
+	 */
+	public function getRequest()
+	{
+		return $this->getComponent('request');
+	}
+
+	/**
+	 * Returns the view object.
+	 * @return View|\yii\web\View the view object that is used to render various view files.
+	 */
+	public function getView()
+	{
+		return $this->getComponent('view');
+	}
+
+	/**
+	 * Returns the URL manager for this application.
+	 * @return \yii\web\UrlManager the URL manager for this application.
+	 */
+	public function getUrlManager()
+	{
+		return $this->getComponent('urlManager');
+	}
+
+	/**
+	 * Returns the internationalization (i18n) component
+	 * @return \yii\i18n\I18N the internationalization component
+	 */
+	public function getI18n()
+	{
+		return $this->getComponent('i18n');
+	}
+
+	/**
+	 * Returns the mailer component.
+	 * @return \yii\mail\MailerInterface the mailer interface
+	 */
+	public function getMail()
+	{
+		return $this->getComponent('mail');
+	}
+
+	/**
+	 * Returns the auth manager for this application.
+	 * @return \yii\rbac\Manager the auth manager for this application.
+	 */
+	public function getAuthManager()
+	{
+		return $this->getComponent('authManager');
+	}
+
+	/**
+	 * Registers the core application components.
+	 * @see setComponents
+	 */
+	public function registerCoreComponents()
+	{
+		$this->setComponents([
+			'log' => ['class' => 'yii\log\Logger'],
+			'errorHandler' => ['class' => 'yii\base\ErrorHandler'],
+			'formatter' => ['class' => 'yii\base\Formatter'],
+			'i18n' => ['class' => 'yii\i18n\I18N'],
+			'mail' => ['class' => 'yii\swiftmailer\Mailer'],
+			'urlManager' => ['class' => 'yii\web\UrlManager'],
+			'view' => ['class' => 'yii\web\View'],
+		]);
+	}
+
+	/**
+	 * Handles uncaught PHP exceptions.
+	 *
+	 * This method is implemented as a PHP exception handler.
+	 *
+	 * @param \Exception $exception the exception that is not caught
+	 */
+	public function handleException($exception)
+	{
+		$this->exception = $exception;
+
+		// disable error capturing to avoid recursive errors while handling exceptions
+		restore_error_handler();
+		restore_exception_handler();
+		try {
+			$this->logException($exception);
+			if (($handler = $this->getErrorHandler()) !== null) {
+				$handler->handle($exception);
+			} else {
+				echo $this->renderException($exception);
+			}
+		} catch (\Exception $e) {
+			// exception could be thrown in ErrorHandler::handle()
+			$msg = (string)$e;
+			$msg .= "\nPrevious exception:\n";
+			$msg .= (string)$exception;
+			if (YII_DEBUG) {
+				if (PHP_SAPI === 'cli') {
+					echo $msg . "\n";
+				} else {
+					echo '<pre>' . htmlspecialchars($msg, ENT_QUOTES, $this->charset) . '</pre>';
+				}
+			}
+			$msg .= "\n\$_SERVER = " . var_export($_SERVER, true);
+			error_log($msg);
+			exit(1);
+		}
+	}
+
+	/**
+	 * Handles PHP execution errors such as warnings, notices.
+	 *
+	 * This method is used as a PHP error handler. It will simply raise an `ErrorException`.
+	 *
+	 * @param integer $code the level of the error raised
+	 * @param string $message the error message
+	 * @param string $file the filename that the error was raised in
+	 * @param integer $line the line number the error was raised at
+	 *
+	 * @throws ErrorException
+	 */
+	public function handleError($code, $message, $file, $line)
+	{
+		if (error_reporting() !== 0) {
+			// load ErrorException manually here because autoloading them will not work
+			// when error occurs while autoloading a class
+			if (!class_exists('\\yii\\base\\Exception', false)) {
+				require_once(__DIR__ . '/Exception.php');
+			}
+			if (!class_exists('\\yii\\base\\ErrorException', false)) {
+				require_once(__DIR__ . '/ErrorException.php');
+			}
+			$exception = new ErrorException($message, $code, $code, $file, $line);
+
+			// in case error appeared in __toString method we can't throw any exception
+			$trace = debug_backtrace(false);
+			array_shift($trace);
+			foreach ($trace as $frame) {
+				if ($frame['function'] == '__toString') {
+					$this->handleException($exception);
+					exit(1);
+				}
+			}
+
+			throw $exception;
+		}
+	}
+
+	/**
+	 * Handles fatal PHP errors
+	 */
+	public function handleFatalError()
+	{
+		unset($this->_memoryReserve);
+
+		// load ErrorException manually here because autoloading them will not work
+		// when error occurs while autoloading a class
+		if (!class_exists('\\yii\\base\\Exception', false)) {
+			require_once(__DIR__ . '/Exception.php');
+		}
+		if (!class_exists('\\yii\\base\\ErrorException', false)) {
+			require_once(__DIR__ . '/ErrorException.php');
+		}
+
+		$error = error_get_last();
+
+		if (ErrorException::isFatalError($error)) {
+			$exception = new ErrorException($error['message'], $error['type'], $error['type'], $error['file'], $error['line']);
+			$this->exception = $exception;
+			// use error_log because it's too late to use Yii log
+			error_log($exception);
+
+			if (($handler = $this->getErrorHandler()) !== null) {
+				$handler->handle($exception);
+			} else {
+				echo $this->renderException($exception);
+			}
+
+			exit(1);
+		}
+	}
+
+	/**
+	 * Renders an exception without using rich format.
+	 * @param \Exception $exception the exception to be rendered.
+	 * @return string the rendering result
+	 */
+	public function renderException($exception)
+	{
+		if ($exception instanceof Exception && ($exception instanceof UserException || !YII_DEBUG)) {
+			$message = $exception->getName() . ': ' . $exception->getMessage();
+			if (Yii::$app->controller instanceof \yii\console\Controller) {
+				$message = Yii::$app->controller->ansiFormat($message, Console::FG_RED);
+			}
+		} else {
+			$message = YII_DEBUG ? (string)$exception : 'Error: ' . $exception->getMessage();
+		}
+		if (PHP_SAPI === 'cli') {
+			return $message . "\n";
+		} else {
+			return '<pre>' . htmlspecialchars($message, ENT_QUOTES, $this->charset) . '</pre>';
+		}
+	}
+
+	/**
+	 * Logs the given exception
+	 * @param \Exception $exception the exception to be logged
+	 */
+	protected function logException($exception)
+	{
+		$category = get_class($exception);
+		if ($exception instanceof HttpException) {
+			$category = 'yii\\web\\HttpException:' . $exception->statusCode;
+		} elseif ($exception instanceof \ErrorException) {
+			$category .= ':' . $exception->getSeverity();
+		}
+		Yii::error((string)$exception, $category);
+	}
+}

+ 80 - 0
php-yii2/app/vendor/yiisoft/yii2/base/ArrayAccessTrait.php

@@ -0,0 +1,80 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * ArrayAccessTrait provides the implementation for `IteratorAggregate`, `ArrayAccess` and `Countable`.
+ *
+ * Note that ArrayAccessTrait requires the class using it contain a property named `data` which should be an array.
+ * The data will be exposed by ArrayAccessTrait to support accessing the class object like an array.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+trait ArrayAccessTrait
+{
+	/**
+	 * Returns an iterator for traversing the data.
+	 * This method is required by the SPL interface `IteratorAggregate`.
+	 * It will be implicitly called when you use `foreach` to traverse the collection.
+	 * @return \ArrayIterator an iterator for traversing the cookies in the collection.
+	 */
+	public function getIterator()
+	{
+		return new \ArrayIterator($this->data);
+	}
+
+	/**
+	 * Returns the number of data items.
+	 * This method is required by Countable interface.
+	 * @return integer number of data elements.
+	 */
+	public function count()
+	{
+		return count($this->data);
+	}
+
+	/**
+	 * This method is required by the interface ArrayAccess.
+	 * @param mixed $offset the offset to check on
+	 * @return boolean
+	 */
+	public function offsetExists($offset)
+	{
+		return isset($this->data[$offset]);
+	}
+
+	/**
+	 * This method is required by the interface ArrayAccess.
+	 * @param integer $offset the offset to retrieve element.
+	 * @return mixed the element at the offset, null if no element is found at the offset
+	 */
+	public function offsetGet($offset)
+	{
+		return isset($this->data[$offset]) ? $this->data[$offset] : null;
+	}
+
+	/**
+	 * This method is required by the interface ArrayAccess.
+	 * @param integer $offset the offset to set element
+	 * @param mixed $item the element value
+	 */
+	public function offsetSet($offset, $item)
+	{
+		$this->data[$offset] = $item;
+	}
+
+	/**
+	 * This method is required by the interface ArrayAccess.
+	 * @param mixed $offset the offset to unset element
+	 */
+	public function offsetUnset($offset)
+	{
+		unset($this->data[$offset]);
+	}
+}

+ 23 - 0
php-yii2/app/vendor/yiisoft/yii2/base/Arrayable.php

@@ -0,0 +1,23 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * Arrayable should be implemented by classes that need to be represented in array format.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+interface Arrayable
+{
+	/**
+	 * Converts the object into an array.
+	 * @return array the array representation of this object
+	 */
+	public function toArray();
+}

+ 91 - 0
php-yii2/app/vendor/yiisoft/yii2/base/Behavior.php

@@ -0,0 +1,91 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * Behavior is the base class for all behavior classes.
+ *
+ * A behavior can be used to enhance the functionality of an existing component without modifying its code.
+ * In particular, it can "inject" its own methods and properties into the component
+ * and make them directly accessible via the component. It can also respond to the events triggered in the component
+ * and thus intercept the normal code execution.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Behavior extends \yii\base\Object
+{
+	/**
+	 * @var Component the owner of this behavior
+	 */
+	public $owner;
+
+	/**
+	 * Declares event handlers for the [[owner]]'s events.
+	 *
+	 * Child classes may override this method to declare what PHP callbacks should
+	 * be attached to the events of the [[owner]] component.
+	 *
+	 * The callbacks will be attached to the [[owner]]'s events when the behavior is
+	 * attached to the owner; and they will be detached from the events when
+	 * the behavior is detached from the component.
+	 *
+	 * The callbacks can be any of the followings:
+	 *
+	 * - method in this behavior: `'handleClick'`, equivalent to `[$this, 'handleClick']`
+	 * - object method: `[$object, 'handleClick']`
+	 * - static method: `['Page', 'handleClick']`
+	 * - anonymous function: `function($event) { ... }`
+	 *
+	 * The following is an example:
+	 *
+	 * ~~~
+	 * [
+	 *	 Model::EVENT_BEFORE_VALIDATE => 'myBeforeValidate',
+	 *	 Model::EVENT_AFTER_VALIDATE => 'myAfterValidate',
+	 * ]
+	 * ~~~
+	 *
+	 * @return array events (array keys) and the corresponding event handler methods (array values).
+	 */
+	public function events()
+	{
+		return [];
+	}
+
+	/**
+	 * Attaches the behavior object to the component.
+	 * The default implementation will set the [[owner]] property
+	 * and attach event handlers as declared in [[events]].
+	 * Make sure you call the parent implementation if you override this method.
+	 * @param Component $owner the component that this behavior is to be attached to.
+	 */
+	public function attach($owner)
+	{
+		$this->owner = $owner;
+		foreach ($this->events() as $event => $handler) {
+			$owner->on($event, is_string($handler) ? [$this, $handler] : $handler);
+		}
+	}
+
+	/**
+	 * Detaches the behavior object from the component.
+	 * The default implementation will unset the [[owner]] property
+	 * and detach event handlers declared in [[events]].
+	 * Make sure you call the parent implementation if you override this method.
+	 */
+	public function detach()
+	{
+		if ($this->owner) {
+			foreach ($this->events() as $event => $handler) {
+				$this->owner->off($event, is_string($handler) ? [$this, $handler] : $handler);
+			}
+			$this->owner = null;
+		}
+	}
+}

+ 581 - 0
php-yii2/app/vendor/yiisoft/yii2/base/Component.php

@@ -0,0 +1,581 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+use Yii;
+
+/**
+ * Component is the base class that implements the *property*, *event* and *behavior* features.
+ *
+ * @include @yii/base/Component.md
+ *
+ * @property Behavior[] $behaviors List of behaviors attached to this component. This property is read-only.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Component extends Object
+{
+	/**
+	 * @var array the attached event handlers (event name => handlers)
+	 */
+	private $_events = [];
+	/**
+	 * @var Behavior[] the attached behaviors (behavior name => behavior)
+	 */
+	private $_behaviors;
+
+	/**
+	 * Returns the value of a component property.
+	 * This method will check in the following order and act accordingly:
+	 *
+	 *  - a property defined by a getter: return the getter result
+	 *  - a property of a behavior: return the behavior property value
+	 *
+	 * Do not call this method directly as it is a PHP magic method that
+	 * will be implicitly called when executing `$value = $component->property;`.
+	 * @param string $name the property name
+	 * @return mixed the property value or the value of a behavior's property
+	 * @throws UnknownPropertyException if the property is not defined
+	 * @throws InvalidCallException if the property is write-only.
+	 * @see __set()
+	 */
+	public function __get($name)
+	{
+		$getter = 'get' . $name;
+		if (method_exists($this, $getter)) {
+			// read property, e.g. getName()
+			return $this->$getter();
+		} else {
+			// behavior property
+			$this->ensureBehaviors();
+			foreach ($this->_behaviors as $behavior) {
+				if ($behavior->canGetProperty($name)) {
+					return $behavior->$name;
+				}
+			}
+		}
+		if (method_exists($this, 'set' . $name)) {
+			throw new InvalidCallException('Getting write-only property: ' . get_class($this) . '::' . $name);
+		} else {
+			throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name);
+		}
+	}
+
+	/**
+	 * Sets the value of a component property.
+	 * This method will check in the following order and act accordingly:
+	 *
+	 *  - a property defined by a setter: set the property value
+	 *  - an event in the format of "on xyz": attach the handler to the event "xyz"
+	 *  - a behavior in the format of "as xyz": attach the behavior named as "xyz"
+	 *  - a property of a behavior: set the behavior property value
+	 *
+	 * Do not call this method directly as it is a PHP magic method that
+	 * will be implicitly called when executing `$component->property = $value;`.
+	 * @param string $name the property name or the event name
+	 * @param mixed $value the property value
+	 * @throws UnknownPropertyException if the property is not defined
+	 * @throws InvalidCallException if the property is read-only.
+	 * @see __get()
+	 */
+	public function __set($name, $value)
+	{
+		$setter = 'set' . $name;
+		if (method_exists($this, $setter)) {
+			// set property
+			$this->$setter($value);
+			return;
+		} elseif (strncmp($name, 'on ', 3) === 0) {
+			// on event: attach event handler
+			$this->on(trim(substr($name, 3)), $value);
+			return;
+		} elseif (strncmp($name, 'as ', 3) === 0) {
+			// as behavior: attach behavior
+			$name = trim(substr($name, 3));
+			$this->attachBehavior($name, $value instanceof Behavior ? $value : Yii::createObject($value));
+			return;
+		} else {
+			// behavior property
+			$this->ensureBehaviors();
+			foreach ($this->_behaviors as $behavior) {
+				if ($behavior->canSetProperty($name)) {
+					$behavior->$name = $value;
+					return;
+				}
+			}
+		}
+		if (method_exists($this, 'get' . $name)) {
+			throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name);
+		} else {
+			throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name);
+		}
+	}
+
+	/**
+	 * Checks if a property value is null.
+	 * This method will check in the following order and act accordingly:
+	 *
+	 *  - a property defined by a setter: return whether the property value is null
+	 *  - a property of a behavior: return whether the property value is null
+	 *
+	 * Do not call this method directly as it is a PHP magic method that
+	 * will be implicitly called when executing `isset($component->property)`.
+	 * @param string $name the property name or the event name
+	 * @return boolean whether the named property is null
+	 */
+	public function __isset($name)
+	{
+		$getter = 'get' . $name;
+		if (method_exists($this, $getter)) {
+			return $this->$getter() !== null;
+		} else {
+			// behavior property
+			$this->ensureBehaviors();
+			foreach ($this->_behaviors as $behavior) {
+				if ($behavior->canGetProperty($name)) {
+					return $behavior->$name !== null;
+				}
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Sets a component property to be null.
+	 * This method will check in the following order and act accordingly:
+	 *
+	 *  - a property defined by a setter: set the property value to be null
+	 *  - a property of a behavior: set the property value to be null
+	 *
+	 * Do not call this method directly as it is a PHP magic method that
+	 * will be implicitly called when executing `unset($component->property)`.
+	 * @param string $name the property name
+	 * @throws InvalidCallException if the property is read only.
+	 */
+	public function __unset($name)
+	{
+		$setter = 'set' . $name;
+		if (method_exists($this, $setter)) {
+			$this->$setter(null);
+			return;
+		} else {
+			// behavior property
+			$this->ensureBehaviors();
+			foreach ($this->_behaviors as $behavior) {
+				if ($behavior->canSetProperty($name)) {
+					$behavior->$name = null;
+					return;
+				}
+			}
+		}
+		if (method_exists($this, 'get' . $name)) {
+			throw new InvalidCallException('Unsetting read-only property: ' . get_class($this) . '.' . $name);
+		}
+	}
+
+	/**
+	 * Calls the named method which is not a class method.
+	 *
+	 * This method will check if any attached behavior has
+	 * the named method and will execute it if available.
+	 *
+	 * Do not call this method directly as it is a PHP magic method that
+	 * will be implicitly called when an unknown method is being invoked.
+	 * @param string $name the method name
+	 * @param array $params method parameters
+	 * @return mixed the method return value
+	 * @throws UnknownMethodException when calling unknown method
+	 */
+	public function __call($name, $params)
+	{
+		$this->ensureBehaviors();
+		foreach ($this->_behaviors as $object) {
+			if ($object->hasMethod($name)) {
+				return call_user_func_array([$object, $name], $params);
+			}
+		}
+
+		throw new UnknownMethodException('Calling unknown method: ' . get_class($this) . "::$name()");
+	}
+
+	/**
+	 * This method is called after the object is created by cloning an existing one.
+	 * It removes all behaviors because they are attached to the old object.
+	 */
+	public function __clone()
+	{
+		$this->_events = [];
+		$this->_behaviors = null;
+	}
+
+	/**
+	 * Returns a value indicating whether a property is defined for this component.
+	 * A property is defined if:
+	 *
+	 * - the class has a getter or setter method associated with the specified name
+	 *   (in this case, property name is case-insensitive);
+	 * - the class has a member variable with the specified name (when `$checkVars` is true);
+	 * - an attached behavior has a property of the given name (when `$checkBehaviors` is true).
+	 *
+	 * @param string $name the property name
+	 * @param boolean $checkVars whether to treat member variables as properties
+	 * @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component
+	 * @return boolean whether the property is defined
+	 * @see canGetProperty()
+	 * @see canSetProperty()
+	 */
+	public function hasProperty($name, $checkVars = true, $checkBehaviors = true)
+	{
+		return $this->canGetProperty($name, $checkVars, $checkBehaviors) || $this->canSetProperty($name, false, $checkBehaviors);
+	}
+
+	/**
+	 * Returns a value indicating whether a property can be read.
+	 * A property can be read if:
+	 *
+	 * - the class has a getter method associated with the specified name
+	 *   (in this case, property name is case-insensitive);
+	 * - the class has a member variable with the specified name (when `$checkVars` is true);
+	 * - an attached behavior has a readable property of the given name (when `$checkBehaviors` is true).
+	 *
+	 * @param string $name the property name
+	 * @param boolean $checkVars whether to treat member variables as properties
+	 * @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component
+	 * @return boolean whether the property can be read
+	 * @see canSetProperty()
+	 */
+	public function canGetProperty($name, $checkVars = true, $checkBehaviors = true)
+	{
+		if (method_exists($this, 'get' . $name) || $checkVars && property_exists($this, $name)) {
+			return true;
+		} elseif ($checkBehaviors) {
+			$this->ensureBehaviors();
+			foreach ($this->_behaviors as $behavior) {
+				if ($behavior->canGetProperty($name, $checkVars)) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Returns a value indicating whether a property can be set.
+	 * A property can be written if:
+	 *
+	 * - the class has a setter method associated with the specified name
+	 *   (in this case, property name is case-insensitive);
+	 * - the class has a member variable with the specified name (when `$checkVars` is true);
+	 * - an attached behavior has a writable property of the given name (when `$checkBehaviors` is true).
+	 *
+	 * @param string $name the property name
+	 * @param boolean $checkVars whether to treat member variables as properties
+	 * @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component
+	 * @return boolean whether the property can be written
+	 * @see canGetProperty()
+	 */
+	public function canSetProperty($name, $checkVars = true, $checkBehaviors = true)
+	{
+		if (method_exists($this, 'set' . $name) || $checkVars && property_exists($this, $name)) {
+			return true;
+		} elseif ($checkBehaviors) {
+			$this->ensureBehaviors();
+			foreach ($this->_behaviors as $behavior) {
+				if ($behavior->canSetProperty($name, $checkVars)) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Returns a value indicating whether a method is defined.
+	 * A method is defined if:
+	 *
+	 * - the class has a method with the specified name
+	 * - an attached behavior has a method with the given name (when `$checkBehaviors` is true).
+	 *
+	 * @param string $name the property name
+	 * @param boolean $checkBehaviors whether to treat behaviors' methods as methods of this component
+	 * @return boolean whether the property is defined
+	 */
+	public function hasMethod($name, $checkBehaviors = true)
+	{
+		if (method_exists($this, $name)) {
+			return true;
+		} elseif ($checkBehaviors) {
+			$this->ensureBehaviors();
+			foreach ($this->_behaviors as $behavior) {
+				if ($behavior->hasMethod($name)) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Returns a list of behaviors that this component should behave as.
+	 *
+	 * Child classes may override this method to specify the behaviors they want to behave as.
+	 *
+	 * The return value of this method should be an array of behavior objects or configurations
+	 * indexed by behavior names. A behavior configuration can be either a string specifying
+	 * the behavior class or an array of the following structure:
+	 *
+	 * ~~~
+	 * 'behaviorName' => [
+	 *     'class' => 'BehaviorClass',
+	 *     'property1' => 'value1',
+	 *     'property2' => 'value2',
+	 * ]
+	 * ~~~
+	 *
+	 * Note that a behavior class must extend from [[Behavior]]. Behavior names can be strings
+	 * or integers. If the former, they uniquely identify the behaviors. If the latter, the corresponding
+	 * behaviors are anonymous and their properties and methods will NOT be made available via the component
+	 * (however, the behaviors can still respond to the component's events).
+	 *
+	 * Behaviors declared in this method will be attached to the component automatically (on demand).
+	 *
+	 * @return array the behavior configurations.
+	 */
+	public function behaviors()
+	{
+		return [];
+	}
+
+	/**
+	 * Returns a value indicating whether there is any handler attached to the named event.
+	 * @param string $name the event name
+	 * @return boolean whether there is any handler attached to the event.
+	 */
+	public function hasEventHandlers($name)
+	{
+		$this->ensureBehaviors();
+		return !empty($this->_events[$name]) || Event::hasHandlers($this, $name);
+	}
+
+	/**
+	 * Attaches an event handler to an event.
+	 *
+	 * The event handler must be a valid PHP callback. The followings are
+	 * some examples:
+	 *
+	 * ~~~
+	 * function ($event) { ... }         // anonymous function
+	 * [$object, 'handleClick']          // $object->handleClick()
+	 * ['Page', 'handleClick']           // Page::handleClick()
+	 * 'handleClick'                     // global function handleClick()
+	 * ~~~
+	 *
+	 * The event handler must be defined with the following signature,
+	 *
+	 * ~~~
+	 * function ($event)
+	 * ~~~
+	 *
+	 * where `$event` is an [[Event]] object which includes parameters associated with the event.
+	 *
+	 * @param string $name the event name
+	 * @param callback $handler the event handler
+	 * @param mixed $data the data to be passed to the event handler when the event is triggered.
+	 * When the event handler is invoked, this data can be accessed via [[Event::data]].
+	 * @see off()
+	 */
+	public function on($name, $handler, $data = null)
+	{
+		$this->ensureBehaviors();
+		$this->_events[$name][] = [$handler, $data];
+	}
+
+	/**
+	 * Detaches an existing event handler from this component.
+	 * This method is the opposite of [[on()]].
+	 * @param string $name event name
+	 * @param callback $handler the event handler to be removed.
+	 * If it is null, all handlers attached to the named event will be removed.
+	 * @return boolean if a handler is found and detached
+	 * @see on()
+	 */
+	public function off($name, $handler = null)
+	{
+		$this->ensureBehaviors();
+		if (empty($this->_events[$name])) {
+			return false;
+		}
+		if ($handler === null) {
+			unset($this->_events[$name]);
+			return true;
+		} else {
+			$removed = false;
+			foreach ($this->_events[$name] as $i => $event) {
+				if ($event[0] === $handler) {
+					unset($this->_events[$name][$i]);
+					$removed = true;
+				}
+			}
+			if ($removed) {
+				$this->_events[$name] = array_values($this->_events[$name]);
+			}
+			return $removed;
+		}
+	}
+
+	/**
+	 * Triggers an event.
+	 * This method represents the happening of an event. It invokes
+	 * all attached handlers for the event including class-level handlers.
+	 * @param string $name the event name
+	 * @param Event $event the event parameter. If not set, a default [[Event]] object will be created.
+	 */
+	public function trigger($name, Event $event = null)
+	{
+		$this->ensureBehaviors();
+		if (!empty($this->_events[$name])) {
+			if ($event === null) {
+				$event = new Event;
+			}
+			if ($event->sender === null) {
+				$event->sender = $this;
+			}
+			$event->handled = false;
+			$event->name = $name;
+			foreach ($this->_events[$name] as $handler) {
+				$event->data = $handler[1];
+				call_user_func($handler[0], $event);
+				// stop further handling if the event is handled
+				if ($event->handled) {
+					return;
+				}
+			}
+		}
+		// invoke class-level attached handlers
+		Event::trigger($this, $name, $event);
+	}
+
+	/**
+	 * Returns the named behavior object.
+	 * @param string $name the behavior name
+	 * @return Behavior the behavior object, or null if the behavior does not exist
+	 */
+	public function getBehavior($name)
+	{
+		$this->ensureBehaviors();
+		return isset($this->_behaviors[$name]) ? $this->_behaviors[$name] : null;
+	}
+
+	/**
+	 * Returns all behaviors attached to this component.
+	 * @return Behavior[] list of behaviors attached to this component
+	 */
+	public function getBehaviors()
+	{
+		$this->ensureBehaviors();
+		return $this->_behaviors;
+	}
+
+	/**
+	 * Attaches a behavior to this component.
+	 * This method will create the behavior object based on the given
+	 * configuration. After that, the behavior object will be attached to
+	 * this component by calling the [[Behavior::attach()]] method.
+	 * @param string $name the name of the behavior.
+	 * @param string|array|Behavior $behavior the behavior configuration. This can be one of the following:
+	 *
+	 *  - a [[Behavior]] object
+	 *  - a string specifying the behavior class
+	 *  - an object configuration array that will be passed to [[Yii::createObject()]] to create the behavior object.
+	 *
+	 * @return Behavior the behavior object
+	 * @see detachBehavior()
+	 */
+	public function attachBehavior($name, $behavior)
+	{
+		$this->ensureBehaviors();
+		return $this->attachBehaviorInternal($name, $behavior);
+	}
+
+	/**
+	 * Attaches a list of behaviors to the component.
+	 * Each behavior is indexed by its name and should be a [[Behavior]] object,
+	 * a string specifying the behavior class, or an configuration array for creating the behavior.
+	 * @param array $behaviors list of behaviors to be attached to the component
+	 * @see attachBehavior()
+	 */
+	public function attachBehaviors($behaviors)
+	{
+		$this->ensureBehaviors();
+		foreach ($behaviors as $name => $behavior) {
+			$this->attachBehaviorInternal($name, $behavior);
+		}
+	}
+
+	/**
+	 * Detaches a behavior from the component.
+	 * The behavior's [[Behavior::detach()]] method will be invoked.
+	 * @param string $name the behavior's name.
+	 * @return Behavior the detached behavior. Null if the behavior does not exist.
+	 */
+	public function detachBehavior($name)
+	{
+		$this->ensureBehaviors();
+		if (isset($this->_behaviors[$name])) {
+			$behavior = $this->_behaviors[$name];
+			unset($this->_behaviors[$name]);
+			$behavior->detach();
+			return $behavior;
+		} else {
+			return null;
+		}
+	}
+
+	/**
+	 * Detaches all behaviors from the component.
+	 */
+	public function detachBehaviors()
+	{
+		$this->ensureBehaviors();
+		foreach ($this->_behaviors as $name => $behavior) {
+			$this->detachBehavior($name);
+		}
+	}
+
+	/**
+	 * Makes sure that the behaviors declared in [[behaviors()]] are attached to this component.
+	 */
+	public function ensureBehaviors()
+	{
+		if ($this->_behaviors === null) {
+			$this->_behaviors = [];
+			foreach ($this->behaviors() as $name => $behavior) {
+				$this->attachBehaviorInternal($name, $behavior);
+			}
+		}
+	}
+
+	/**
+	 * Attaches a behavior to this component.
+	 * @param string $name the name of the behavior.
+	 * @param string|array|Behavior $behavior the behavior to be attached
+	 * @return Behavior the attached behavior.
+	 */
+	private function attachBehaviorInternal($name, $behavior)
+	{
+		if (!($behavior instanceof Behavior)) {
+			$behavior = Yii::createObject($behavior);
+		}
+		if (isset($this->_behaviors[$name])) {
+			$this->_behaviors[$name]->detach();
+		}
+		$behavior->attach($this);
+		return $this->_behaviors[$name] = $behavior;
+	}
+}

+ 417 - 0
php-yii2/app/vendor/yiisoft/yii2/base/Controller.php

@@ -0,0 +1,417 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+use Yii;
+
+/**
+ * Controller is the base class for classes containing controller logic.
+ *
+ * @property array $actionParams The request parameters (name-value pairs) to be used for action parameter
+ * binding. This property is read-only.
+ * @property string $route The route (module ID, controller ID and action ID) of the current request. This
+ * property is read-only.
+ * @property string $uniqueId The controller ID that is prefixed with the module ID (if any). This property is
+ * read-only.
+ * @property View $view The view object that can be used to render views or view files.
+ * @property string $viewPath The directory containing the view files for this controller. This property is
+ * read-only.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Controller extends Component implements ViewContextInterface
+{
+	/**
+	 * @event ActionEvent an event raised right before executing a controller action.
+	 * You may set [[ActionEvent::isValid]] to be false to cancel the action execution.
+	 */
+	const EVENT_BEFORE_ACTION = 'beforeAction';
+	/**
+	 * @event ActionEvent an event raised right after executing a controller action.
+	 */
+	const EVENT_AFTER_ACTION = 'afterAction';
+	/**
+	 * @var string the ID of this controller.
+	 */
+	public $id;
+	/**
+	 * @var Module $module the module that this controller belongs to.
+	 */
+	public $module;
+	/**
+	 * @var string the ID of the action that is used when the action ID is not specified
+	 * in the request. Defaults to 'index'.
+	 */
+	public $defaultAction = 'index';
+	/**
+	 * @var string|boolean the name of the layout to be applied to this controller's views.
+	 * This property mainly affects the behavior of [[render()]].
+	 * Defaults to null, meaning the actual layout value should inherit that from [[module]]'s layout value.
+	 * If false, no layout will be applied.
+	 */
+	public $layout;
+	/**
+	 * @var Action the action that is currently being executed. This property will be set
+	 * by [[run()]] when it is called by [[Application]] to run an action.
+	 */
+	public $action;
+	/**
+	 * @var View the view object that can be used to render views or view files.
+	 */
+	private $_view;
+
+
+	/**
+	 * @param string $id the ID of this controller.
+	 * @param Module $module the module that this controller belongs to.
+	 * @param array $config name-value pairs that will be used to initialize the object properties.
+	 */
+	public function __construct($id, $module, $config = [])
+	{
+		$this->id = $id;
+		$this->module = $module;
+		parent::__construct($config);
+	}
+
+	/**
+	 * Declares external actions for the controller.
+	 * This method is meant to be overwritten to declare external actions for the controller.
+	 * It should return an array, with array keys being action IDs, and array values the corresponding
+	 * action class names or action configuration arrays. For example,
+	 *
+	 * ~~~
+	 * return [
+	 *     'action1' => 'app\components\Action1',
+	 *     'action2' => [
+	 *         'class' => 'app\components\Action2',
+	 *         'property1' => 'value1',
+	 *         'property2' => 'value2',
+	 *     ],
+	 * ];
+	 * ~~~
+	 *
+	 * [[\Yii::createObject()]] will be used later to create the requested action
+	 * using the configuration provided here.
+	 */
+	public function actions()
+	{
+		return [];
+	}
+
+	/**
+	 * Runs an action within this controller with the specified action ID and parameters.
+	 * If the action ID is empty, the method will use [[defaultAction]].
+	 * @param string $id the ID of the action to be executed.
+	 * @param array $params the parameters (name-value pairs) to be passed to the action.
+	 * @return mixed the result of the action.
+	 * @throws InvalidRouteException if the requested action ID cannot be resolved into an action successfully.
+	 * @see createAction()
+	 */
+	public function runAction($id, $params = [])
+	{
+		$action = $this->createAction($id);
+		if ($action !== null) {
+			Yii::trace("Route to run: " . $action->getUniqueId(), __METHOD__);
+			if (Yii::$app->requestedAction === null) {
+				Yii::$app->requestedAction = $action;
+			}
+			$oldAction = $this->action;
+			$this->action = $action;
+			$result = null;
+			$event = new ActionEvent($action);
+			Yii::$app->trigger(Application::EVENT_BEFORE_ACTION, $event);
+			if ($event->isValid && $this->module->beforeAction($action) && $this->beforeAction($action)) {
+				$result = $action->runWithParams($params);
+				$this->afterAction($action, $result);
+				$this->module->afterAction($action, $result);
+				$event = new ActionEvent($action);
+				$event->result = &$result;
+				Yii::$app->trigger(Application::EVENT_AFTER_ACTION, $event);
+			}
+			$this->action = $oldAction;
+			return $result;
+		} else {
+			throw new InvalidRouteException('Unable to resolve the request: ' . $this->getUniqueId() . '/' . $id);
+		}
+	}
+
+	/**
+	 * Runs a request specified in terms of a route.
+	 * The route can be either an ID of an action within this controller or a complete route consisting
+	 * of module IDs, controller ID and action ID. If the route starts with a slash '/', the parsing of
+	 * the route will start from the application; otherwise, it will start from the parent module of this controller.
+	 * @param string $route the route to be handled, e.g., 'view', 'comment/view', '/admin/comment/view'.
+	 * @param array $params the parameters to be passed to the action.
+	 * @return mixed the result of the action.
+	 * @see runAction()
+	 */
+	public function run($route, $params = [])
+	{
+		$pos = strpos($route, '/');
+		if ($pos === false) {
+			return $this->runAction($route, $params);
+		} elseif ($pos > 0) {
+			return $this->module->runAction($route, $params);
+		} else {
+			return Yii::$app->runAction(ltrim($route, '/'), $params);
+		}
+	}
+
+	/**
+	 * Binds the parameters to the action.
+	 * This method is invoked by [[Action]] when it begins to run with the given parameters.
+	 * @param Action $action the action to be bound with parameters.
+	 * @param array $params the parameters to be bound to the action.
+	 * @return array the valid parameters that the action can run with.
+	 */
+	public function bindActionParams($action, $params)
+	{
+		return [];
+	}
+
+	/**
+	 * Creates an action based on the given action ID.
+	 * The method first checks if the action ID has been declared in [[actions()]]. If so,
+	 * it will use the configuration declared there to create the action object.
+	 * If not, it will look for a controller method whose name is in the format of `actionXyz`
+	 * where `Xyz` stands for the action ID. If found, an [[InlineAction]] representing that
+	 * method will be created and returned.
+	 * @param string $id the action ID.
+	 * @return Action the newly created action instance. Null if the ID doesn't resolve into any action.
+	 */
+	public function createAction($id)
+	{
+		if ($id === '') {
+			$id = $this->defaultAction;
+		}
+
+		$actionMap = $this->actions();
+		if (isset($actionMap[$id])) {
+			return Yii::createObject($actionMap[$id], $id, $this);
+		} elseif (preg_match('/^[a-z0-9\\-_]+$/', $id) && strpos($id, '--') === false && trim($id, '-') === $id) {
+			$methodName = 'action' . str_replace(' ', '', ucwords(implode(' ', explode('-', $id))));
+			if (method_exists($this, $methodName)) {
+				$method = new \ReflectionMethod($this, $methodName);
+				if ($method->getName() === $methodName) {
+					return new InlineAction($id, $this, $methodName);
+				}
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * This method is invoked right before an action is to be executed (after all possible filters).
+	 * You may override this method to do last-minute preparation for the action.
+	 * If you override this method, please make sure you call the parent implementation first.
+	 * @param Action $action the action to be executed.
+	 * @return boolean whether the action should continue to be executed.
+	 */
+	public function beforeAction($action)
+	{
+		$event = new ActionEvent($action);
+		$this->trigger(self::EVENT_BEFORE_ACTION, $event);
+		return $event->isValid;
+	}
+
+	/**
+	 * This method is invoked right after an action is executed.
+	 * You may override this method to do some postprocessing for the action.
+	 * If you override this method, please make sure you call the parent implementation first.
+	 * @param Action $action the action just executed.
+	 * @param mixed $result the action return result.
+	 */
+	public function afterAction($action, &$result)
+	{
+		$event = new ActionEvent($action);
+		$event->result = &$result;
+		$this->trigger(self::EVENT_AFTER_ACTION, $event);
+	}
+
+	/**
+	 * @return string the controller ID that is prefixed with the module ID (if any).
+	 */
+	public function getUniqueId()
+	{
+		return $this->module instanceof Application ? $this->id : $this->module->getUniqueId() . '/' . $this->id;
+	}
+
+	/**
+	 * Returns the route of the current request.
+	 * @return string the route (module ID, controller ID and action ID) of the current request.
+	 */
+	public function getRoute()
+	{
+		return $this->action !== null ? $this->action->getUniqueId() : $this->getUniqueId();
+	}
+
+	/**
+	 * Renders a view and applies layout if available.
+	 *
+	 * The view to be rendered can be specified in one of the following formats:
+	 *
+	 * - path alias (e.g. "@app/views/site/index");
+	 * - absolute path within application (e.g. "//site/index"): the view name starts with double slashes.
+	 *   The actual view file will be looked for under the [[Application::viewPath|view path]] of the application.
+	 * - absolute path within module (e.g. "/site/index"): the view name starts with a single slash.
+	 *   The actual view file will be looked for under the [[Module::viewPath|view path]] of [[module]].
+	 * - relative path (e.g. "index"): the actual view file will be looked for under [[viewPath]].
+	 *
+	 * To determine which layout should be applied, the following two steps are conducted:
+	 *
+	 * 1. In the first step, it determines the layout name and the context module:
+	 *
+	 * - If [[layout]] is specified as a string, use it as the layout name and [[module]] as the context module;
+	 * - If [[layout]] is null, search through all ancestor modules of this controller and find the first
+	 *   module whose [[Module::layout|layout]] is not null. The layout and the corresponding module
+	 *   are used as the layout name and the context module, respectively. If such a module is not found
+	 *   or the corresponding layout is not a string, it will return false, meaning no applicable layout.
+	 *
+	 * 2. In the second step, it determines the actual layout file according to the previously found layout name
+	 *    and context module. The layout name can be:
+	 *
+	 * - a path alias (e.g. "@app/views/layouts/main");
+	 * - an absolute path (e.g. "/main"): the layout name starts with a slash. The actual layout file will be
+	 *   looked for under the [[Application::layoutPath|layout path]] of the application;
+	 * - a relative path (e.g. "main"): the actual layout layout file will be looked for under the
+	 *   [[Module::layoutPath|layout path]] of the context module.
+	 *
+	 * If the layout name does not contain a file extension, it will use the default one `.php`.
+	 *
+	 * @param string $view the view name. Please refer to [[findViewFile()]] on how to specify a view name.
+	 * @param array $params the parameters (name-value pairs) that should be made available in the view.
+	 * These parameters will not be available in the layout.
+	 * @return string the rendering result.
+	 * @throws InvalidParamException if the view file or the layout file does not exist.
+	 */
+	public function render($view, $params = [])
+	{
+		$output = $this->getView()->render($view, $params, $this);
+		$layoutFile = $this->findLayoutFile($this->getView());
+		if ($layoutFile !== false) {
+			return $this->getView()->renderFile($layoutFile, ['content' => $output], $this);
+		} else {
+			return $output;
+		}
+	}
+
+	/**
+	 * Renders a view.
+	 * This method differs from [[render()]] in that it does not apply any layout.
+	 * @param string $view the view name. Please refer to [[render()]] on how to specify a view name.
+	 * @param array $params the parameters (name-value pairs) that should be made available in the view.
+	 * @return string the rendering result.
+	 * @throws InvalidParamException if the view file does not exist.
+	 */
+	public function renderPartial($view, $params = [])
+	{
+		return $this->getView()->render($view, $params, $this);
+	}
+
+	/**
+	 * Renders a view file.
+	 * @param string $file the view file to be rendered. This can be either a file path or a path alias.
+	 * @param array $params the parameters (name-value pairs) that should be made available in the view.
+	 * @return string the rendering result.
+	 * @throws InvalidParamException if the view file does not exist.
+	 */
+	public function renderFile($file, $params = [])
+	{
+		return $this->getView()->renderFile($file, $params, $this);
+	}
+
+	/**
+	 * Returns the view object that can be used to render views or view files.
+	 * The [[render()]], [[renderPartial()]] and [[renderFile()]] methods will use
+	 * this view object to implement the actual view rendering.
+	 * If not set, it will default to the "view" application component.
+	 * @return View the view object that can be used to render views or view files.
+	 */
+	public function getView()
+	{
+		if ($this->_view === null) {
+			$this->_view = Yii::$app->getView();
+		}
+		return $this->_view;
+	}
+
+	/**
+	 * Sets the view object to be used by this controller.
+	 * @param View $view the view object that can be used to render views or view files.
+	 */
+	public function setView($view)
+	{
+		$this->_view = $view;
+	}
+
+	/**
+	 * Returns the directory containing view files for this controller.
+	 * The default implementation returns the directory named as controller [[id]] under the [[module]]'s
+	 * [[viewPath]] directory.
+	 * @return string the directory containing the view files for this controller.
+	 */
+	public function getViewPath()
+	{
+		return $this->module->getViewPath() . DIRECTORY_SEPARATOR . $this->id;
+	}
+
+	/**
+	 * Finds the view file based on the given view name.
+	 * @param string $view the view name or the path alias of the view file. Please refer to [[render()]]
+	 * on how to specify this parameter.
+	 * @return string the view file path. Note that the file may not exist.
+	 */
+	public function findViewFile($view)
+	{
+		return $this->getViewPath() . DIRECTORY_SEPARATOR . $view;
+	}
+
+	/**
+	 * Finds the applicable layout file.
+	 * @param View $view the view object to render the layout file.
+	 * @return string|boolean the layout file path, or false if layout is not needed.
+	 * Please refer to [[render()]] on how to specify this parameter.
+	 * @throws InvalidParamException if an invalid path alias is used to specify the layout.
+	 */
+	protected function findLayoutFile($view)
+	{
+		$module = $this->module;
+		if (is_string($this->layout)) {
+			$layout = $this->layout;
+		} elseif ($this->layout === null) {
+			while ($module !== null && $module->layout === null) {
+				$module = $module->module;
+			}
+			if ($module !== null && is_string($module->layout)) {
+				$layout = $module->layout;
+			}
+		}
+
+		if (!isset($layout)) {
+			return false;
+		}
+
+		if (strncmp($layout, '@', 1) === 0) {
+			$file = Yii::getAlias($layout);
+		} elseif (strncmp($layout, '/', 1) === 0) {
+			$file = Yii::$app->getLayoutPath() . DIRECTORY_SEPARATOR . substr($layout, 1);
+		} else {
+			$file = $module->getLayoutPath() . DIRECTORY_SEPARATOR . $layout;
+		}
+
+		if (pathinfo($file, PATHINFO_EXTENSION) !== '') {
+			return $file;
+		}
+		$path = $file . '.' . $view->defaultExtension;
+		if ($view->defaultExtension !== 'php' && !is_file($path)) {
+			$path = $file . '.php';
+		}
+		return $path;
+	}
+}

+ 108 - 0
php-yii2/app/vendor/yiisoft/yii2/base/ErrorException.php

@@ -0,0 +1,108 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+use Yii;
+
+/**
+ * ErrorException represents a PHP error.
+ *
+ * @author Alexander Makarov <[email protected]>
+ * @since 2.0
+ */
+class ErrorException extends Exception
+{
+	protected $severity;
+
+	/**
+	 * Constructs the exception.
+	 * @link http://php.net/manual/en/errorexception.construct.php
+	 * @param $message [optional]
+	 * @param $code [optional]
+	 * @param $severity [optional]
+	 * @param $filename [optional]
+	 * @param $lineno [optional]
+	 * @param $previous [optional]
+	 */
+	public function __construct($message = '', $code = 0, $severity = 1, $filename = __FILE__, $lineno = __LINE__, \Exception $previous = null)
+	{
+		parent::__construct($message, $code, $previous);
+		$this->severity = $severity;
+		$this->file = $filename;
+		$this->line = $lineno;
+
+		if (function_exists('xdebug_get_function_stack')) {
+			$trace = array_slice(array_reverse(xdebug_get_function_stack()), 3, -1);
+			foreach ($trace as &$frame) {
+				if (!isset($frame['function'])) {
+					$frame['function'] = 'unknown';
+				}
+
+				// XDebug < 2.1.1: http://bugs.xdebug.org/view.php?id=695
+				if (!isset($frame['type']) || $frame['type'] === 'static') {
+					$frame['type'] = '::';
+				} elseif ($frame['type'] === 'dynamic') {
+					$frame['type'] = '->';
+				}
+
+				// XDebug has a different key name
+				if (isset($frame['params']) && !isset($frame['args'])) {
+					$frame['args'] = $frame['params'];
+				}
+			}
+
+			$ref = new \ReflectionProperty('Exception', 'trace');
+			$ref->setAccessible(true);
+			$ref->setValue($this, $trace);
+		}
+	}
+
+	/**
+	 * Returns if error is one of fatal type.
+	 *
+	 * @param array $error error got from error_get_last()
+	 * @return bool if error is one of fatal type
+	 */
+	public static function isFatalError($error)
+	{
+		return isset($error['type']) && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING]);
+	}
+
+	/**
+	 * Gets the exception severity.
+	 * @link http://php.net/manual/en/errorexception.getseverity.php
+	 * @return int the severity level of the exception.
+	 */
+	final public function getSeverity()
+	{
+		return $this->severity;
+	}
+
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		$names = [
+			E_ERROR => 'PHP Fatal Error',
+			E_PARSE => 'PHP Parse Error',
+			E_CORE_ERROR => 'PHP Core Error',
+			E_COMPILE_ERROR => 'PHP Compile Error',
+			E_USER_ERROR => 'PHP User Error',
+			E_WARNING => 'PHP Warning',
+			E_CORE_WARNING => 'PHP Core Warning',
+			E_COMPILE_WARNING => 'PHP Compile Warning',
+			E_USER_WARNING => 'PHP User Warning',
+			E_STRICT => 'PHP Strict Warning',
+			E_NOTICE => 'PHP Notice',
+			E_RECOVERABLE_ERROR => 'PHP Recoverable Error',
+			E_DEPRECATED => 'PHP Deprecated Warning',
+		];
+		return isset($names[$this->getCode()]) ? $names[$this->getCode()] : 'Error';
+	}
+}

+ 331 - 0
php-yii2/app/vendor/yiisoft/yii2/base/ErrorHandler.php

@@ -0,0 +1,331 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+use Yii;
+use yii\web\HttpException;
+
+/**
+ * ErrorHandler handles uncaught PHP errors and exceptions.
+ *
+ * ErrorHandler displays these errors using appropriate views based on the
+ * nature of the errors and the mode the application runs at.
+ *
+ * ErrorHandler is configured as an application component in [[yii\base\Application]] by default.
+ * You can access that instance via `Yii::$app->errorHandler`.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @author Timur Ruziev <[email protected]>
+ * @since 2.0
+ */
+class ErrorHandler extends Component
+{
+	/**
+	 * @var integer maximum number of source code lines to be displayed. Defaults to 25.
+	 */
+	public $maxSourceLines = 25;
+	/**
+	 * @var integer maximum number of trace source code lines to be displayed. Defaults to 10.
+	 */
+	public $maxTraceSourceLines = 10;
+	/**
+	 * @var boolean whether to discard any existing page output before error display. Defaults to true.
+	 */
+	public $discardExistingOutput = true;
+	/**
+	 * @var string the route (e.g. 'site/error') to the controller action that will be used
+	 * to display external errors. Inside the action, it can retrieve the error information
+	 * by Yii::$app->exception. This property defaults to null, meaning ErrorHandler
+	 * will handle the error display.
+	 */
+	public $errorAction;
+	/**
+	 * @var string the path of the view file for rendering exceptions without call stack information.
+	 */
+	public $errorView = '@yii/views/errorHandler/error.php';
+	/**
+	 * @var string the path of the view file for rendering exceptions.
+	 */
+	public $exceptionView = '@yii/views/errorHandler/exception.php';
+	/**
+	 * @var string the path of the view file for rendering exceptions and errors call stack element.
+	 */
+	public $callStackItemView = '@yii/views/errorHandler/callStackItem.php';
+	/**
+	 * @var string the path of the view file for rendering previous exceptions.
+	 */
+	public $previousExceptionView = '@yii/views/errorHandler/previousException.php';
+	/**
+	 * @var \Exception the exception that is being handled currently.
+	 */
+	public $exception;
+
+
+	/**
+	 * Handles exception.
+	 * @param \Exception $exception to be handled.
+	 */
+	public function handle($exception)
+	{
+		$this->exception = $exception;
+		if ($this->discardExistingOutput) {
+			$this->clearOutput();
+		}
+		$this->renderException($exception);
+	}
+
+	/**
+	 * Renders the exception.
+	 * @param \Exception $exception the exception to be handled.
+	 */
+	protected function renderException($exception)
+	{
+		if (Yii::$app instanceof \yii\console\Application || YII_ENV_TEST) {
+			echo Yii::$app->renderException($exception);
+			return;
+		}
+
+		$useErrorView = !YII_DEBUG || $exception instanceof UserException;
+
+		$response = Yii::$app->getResponse();
+		$response->getHeaders()->removeAll();
+
+		if ($useErrorView && $this->errorAction !== null) {
+			$result = Yii::$app->runAction($this->errorAction);
+			if ($result instanceof Response) {
+				$response = $result;
+			} else {
+				$response->data = $result;
+			}
+		} elseif ($response->format === \yii\web\Response::FORMAT_HTML) {
+			if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
+				// AJAX request
+				$response->data = Yii::$app->renderException($exception);
+			} else {
+				// if there is an error during error rendering it's useful to
+				// display PHP error in debug mode instead of a blank screen
+				if (YII_DEBUG) {
+					ini_set('display_errors', 1);
+				}
+				$file = $useErrorView ? $this->errorView : $this->exceptionView;
+				$response->data = $this->renderFile($file, [
+					'exception' => $exception,
+				]);
+			}
+		} elseif ($exception instanceof Arrayable) {
+			$response->data = $exception;
+		} else {
+			$response->data = [
+				'type' => get_class($exception),
+				'name' => 'Exception',
+				'message' => $exception->getMessage(),
+				'code' => $exception->getCode(),
+			];
+		}
+
+		if ($exception instanceof HttpException) {
+			$response->setStatusCode($exception->statusCode);
+		} else {
+			$response->setStatusCode(500);
+		}
+
+		$response->send();
+	}
+
+	/**
+	 * Converts special characters to HTML entities.
+	 * @param string $text to encode.
+	 * @return string encoded original text.
+	 */
+	public function htmlEncode($text)
+	{
+		return htmlspecialchars($text, ENT_QUOTES, Yii::$app->charset);
+	}
+
+	/**
+	 * Removes all output echoed before calling this method.
+	 */
+	public function clearOutput()
+	{
+		// the following manual level counting is to deal with zlib.output_compression set to On
+		for ($level = ob_get_level(); $level > 0; --$level) {
+			if (!@ob_end_clean()) {
+				ob_clean();
+			}
+		}
+	}
+
+	/**
+	 * Adds informational links to the given PHP type/class.
+	 * @param string $code type/class name to be linkified.
+	 * @return string linkified with HTML type/class name.
+	 */
+	public function addTypeLinks($code)
+	{
+		$html = '';
+		if (strpos($code, '\\') !== false) {
+			// namespaced class
+			foreach (explode('\\', $code) as $part) {
+				$html .= '<a href="http://yiiframework.com/doc/api/2.0/' . $this->htmlEncode($part) . '" target="_blank">' . $this->htmlEncode($part) . '</a>\\';
+			}
+			$html = rtrim($html, '\\');
+		} elseif (strpos($code, '()') !== false) {
+			// method/function call
+			$html = preg_replace_callback('/^(.*)\(\)$/', function ($matches) {
+				return '<a href="http://yiiframework.com/doc/api/2.0/' . $this->htmlEncode($matches[1]) . '" target="_blank">' .
+					$this->htmlEncode($matches[1]) . '</a>()';
+			}, $code);
+		}
+		return $html;
+	}
+
+	/**
+	 * Renders a view file as a PHP script.
+	 * @param string $_file_ the view file.
+	 * @param array $_params_ the parameters (name-value pairs) that will be extracted and made available in the view file.
+	 * @return string the rendering result
+	 */
+	public function renderFile($_file_, $_params_)
+	{
+		$_params_['handler'] = $this;
+		if ($this->exception instanceof ErrorException) {
+			ob_start();
+			ob_implicit_flush(false);
+			extract($_params_, EXTR_OVERWRITE);
+			require(Yii::getAlias($_file_));
+			return ob_get_clean();
+		} else {
+			return Yii::$app->getView()->renderFile($_file_, $_params_, $this);
+		}
+	}
+
+	/**
+	 * Renders the previous exception stack for a given Exception.
+	 * @param \Exception $exception the exception whose precursors should be rendered.
+	 * @return string HTML content of the rendered previous exceptions.
+	 * Empty string if there are none.
+	 */
+	public function renderPreviousExceptions($exception)
+	{
+		if (($previous = $exception->getPrevious()) !== null) {
+			return $this->renderFile($this->previousExceptionView, ['exception' => $previous]);
+		} else {
+			return '';
+		}
+	}
+
+	/**
+	 * Renders a single call stack element.
+	 * @param string|null $file name where call has happened.
+	 * @param integer|null $line number on which call has happened.
+	 * @param string|null $class called class name.
+	 * @param string|null $method called function/method name.
+	 * @param integer $index number of the call stack element.
+	 * @return string HTML content of the rendered call stack element.
+	 */
+	public function renderCallStackItem($file, $line, $class, $method, $index)
+	{
+		$lines = [];
+		$begin = $end = 0;
+		if ($file !== null && $line !== null) {
+			$line--; // adjust line number from one-based to zero-based
+			$lines = @file($file);
+			if ($line < 0 || $lines === false || ($lineCount = count($lines)) < $line + 1) {
+				return '';
+			}
+
+			$half = (int)(($index == 0 ? $this->maxSourceLines : $this->maxTraceSourceLines) / 2);
+			$begin = $line - $half > 0 ? $line - $half : 0;
+			$end = $line + $half < $lineCount ? $line + $half : $lineCount - 1;
+		}
+
+		return $this->renderFile($this->callStackItemView, [
+			'file' => $file,
+			'line' => $line,
+			'class' => $class,
+			'method' => $method,
+			'index' => $index,
+			'lines' => $lines,
+			'begin' => $begin,
+			'end' => $end,
+		]);
+	}
+
+	/**
+	 * Renders the request information.
+	 * @return string the rendering result
+	 */
+	public function renderRequest()
+	{
+		$request = '';
+		foreach (['_GET', '_POST', '_SERVER', '_FILES', '_COOKIE', '_SESSION', '_ENV'] as $name) {
+			if (!empty($GLOBALS[$name])) {
+				$request .= '$' . $name . ' = ' . var_export($GLOBALS[$name], true) . ";\n\n";
+			}
+		}
+		return '<pre>' . rtrim($request, "\n") . '</pre>';
+	}
+
+	/**
+	 * Determines whether given name of the file belongs to the framework.
+	 * @param string $file name to be checked.
+	 * @return boolean whether given name of the file belongs to the framework.
+	 */
+	public function isCoreFile($file)
+	{
+		return $file === null || strpos(realpath($file), YII_PATH . DIRECTORY_SEPARATOR) === 0;
+	}
+
+	/**
+	 * Creates HTML containing link to the page with the information on given HTTP status code.
+	 * @param integer $statusCode to be used to generate information link.
+	 * @param string $statusDescription Description to display after the the status code.
+	 * @return string generated HTML with HTTP status code information.
+	 */
+	public function createHttpStatusLink($statusCode, $statusDescription)
+	{
+		return '<a href="http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#' . (int)$statusCode . '" target="_blank">HTTP ' . (int)$statusCode . ' &ndash; ' . $statusDescription . '</a>';
+	}
+
+	/**
+	 * Creates string containing HTML link which refers to the home page of determined web-server software
+	 * and its full name.
+	 * @return string server software information hyperlink.
+	 */
+	public function createServerInformationLink()
+	{
+		static $serverUrls = [
+			'http://httpd.apache.org/' => ['apache'],
+			'http://nginx.org/' => ['nginx'],
+			'http://lighttpd.net/' => ['lighttpd'],
+			'http://gwan.com/' => ['g-wan', 'gwan'],
+			'http://iis.net/' => ['iis', 'services'],
+			'http://php.net/manual/en/features.commandline.webserver.php' => ['development'],
+		];
+		if (isset($_SERVER['SERVER_SOFTWARE'])) {
+			foreach ($serverUrls as $url => $keywords) {
+				foreach ($keywords as $keyword) {
+					if (stripos($_SERVER['SERVER_SOFTWARE'], $keyword) !== false) {
+						return '<a href="' . $url . '" target="_blank">' . $this->htmlEncode($_SERVER['SERVER_SOFTWARE']) . '</a>';
+					}
+				}
+			}
+		}
+		return '';
+	}
+
+	/**
+	 * Creates string containing HTML link which refers to the page with the current version
+	 * of the framework and version number text.
+	 * @return string framework version information hyperlink.
+	 */
+	public function createFrameworkVersionLink()
+	{
+		return '<a href="http://github.com/yiisoft/yii2/" target="_blank">' . $this->htmlEncode(Yii::getVersion()) . '</a>';
+	}
+}

+ 185 - 0
php-yii2/app/vendor/yiisoft/yii2/base/Event.php

@@ -0,0 +1,185 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * Event is the base class for all event classes.
+ *
+ * It encapsulates the parameters associated with an event.
+ * The [[sender]] property describes who raises the event.
+ * And the [[handled]] property indicates if the event is handled.
+ * If an event handler sets [[handled]] to be true, the rest of the
+ * uninvoked handlers will no longer be called to handle the event.
+ *
+ * Additionally, when attaching an event handler, extra data may be passed
+ * and be available via the [[data]] property when the event handler is invoked.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Event extends Object
+{
+	/**
+	 * @var string the event name. This property is set by [[Component::trigger()]] and [[trigger()]].
+	 * Event handlers may use this property to check what event it is handling.
+	 */
+	public $name;
+	/**
+	 * @var object the sender of this event. If not set, this property will be
+	 * set as the object whose "trigger()" method is called.
+	 * This property may also be a `null` when this event is a
+	 * class-level event which is triggered in a static context.
+	 */
+	public $sender;
+	/**
+	 * @var boolean whether the event is handled. Defaults to false.
+	 * When a handler sets this to be true, the event processing will stop and
+	 * ignore the rest of the uninvoked event handlers.
+	 */
+	public $handled = false;
+	/**
+	 * @var mixed the data that is passed to [[Component::on()]] when attaching an event handler.
+	 * Note that this varies according to which event handler is currently executing.
+	 */
+	public $data;
+
+	private static $_events = [];
+
+	/**
+	 * Attaches an event handler to a class-level event.
+	 *
+	 * When a class-level event is triggered, event handlers attached
+	 * to that class and all parent classes will be invoked.
+	 *
+	 * For example, the following code attaches an event handler to `ActiveRecord`'s
+	 * `afterInsert` event:
+	 *
+	 * ~~~
+	 * Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
+	 *     Yii::trace(get_class($event->sender) . ' is inserted.');
+	 * });
+	 * ~~~
+	 *
+	 * The handler will be invoked for EVERY successful ActiveRecord insertion.
+	 *
+	 * For more details about how to declare an event handler, please refer to [[Component::on()]].
+	 *
+	 * @param string $class the fully qualified class name to which the event handler needs to attach.
+	 * @param string $name the event name.
+	 * @param callback $handler the event handler.
+	 * @param mixed $data the data to be passed to the event handler when the event is triggered.
+	 * When the event handler is invoked, this data can be accessed via [[Event::data]].
+	 * @see off()
+	 */
+	public static function on($class, $name, $handler, $data = null)
+	{
+		self::$_events[$name][ltrim($class, '\\')][] = [$handler, $data];
+	}
+
+	/**
+	 * Detaches an event handler from a class-level event.
+	 *
+	 * This method is the opposite of [[on()]].
+	 *
+	 * @param string $class the fully qualified class name from which the event handler needs to be detached.
+	 * @param string $name the event name.
+	 * @param callback $handler the event handler to be removed.
+	 * If it is null, all handlers attached to the named event will be removed.
+	 * @return boolean whether a handler is found and detached.
+	 * @see on()
+	 */
+	public static function off($class, $name, $handler = null)
+	{
+		$class = ltrim($class, '\\');
+		if (empty(self::$_events[$name][$class])) {
+			return false;
+		}
+		if ($handler === null) {
+			unset(self::$_events[$name][$class]);
+			return true;
+		} else {
+			$removed = false;
+			foreach (self::$_events[$name][$class] as $i => $event) {
+				if ($event[0] === $handler) {
+					unset(self::$_events[$name][$class][$i]);
+					$removed = true;
+				}
+			}
+			if ($removed) {
+				self::$_events[$name][$class] = array_values(self::$_events[$name][$class]);
+			}
+			return $removed;
+		}
+	}
+
+	/**
+	 * Returns a value indicating whether there is any handler attached to the specified class-level event.
+	 * Note that this method will also check all parent classes to see if there is any handler attached
+	 * to the named event.
+	 * @param string|object $class the object or the fully qualified class name specifying the class-level event.
+	 * @param string $name the event name.
+	 * @return boolean whether there is any handler attached to the event.
+	 */
+	public static function hasHandlers($class, $name)
+	{
+		if (empty(self::$_events[$name])) {
+			return false;
+		}
+		if (is_object($class)) {
+			$class = get_class($class);
+		} else {
+			$class = ltrim($class, '\\');
+		}
+		do {
+			if (!empty(self::$_events[$name][$class])) {
+				return true;
+			}
+		} while (($class = get_parent_class($class)) !== false);
+		return false;
+	}
+
+	/**
+	 * Triggers a class-level event.
+	 * This method will cause invocation of event handlers that are attached to the named event
+	 * for the specified class and all its parent classes.
+	 * @param string|object $class the object or the fully qualified class name specifying the class-level event.
+	 * @param string $name the event name.
+	 * @param Event $event the event parameter. If not set, a default [[Event]] object will be created.
+	 */
+	public static function trigger($class, $name, $event = null)
+	{
+		if (empty(self::$_events[$name])) {
+			return;
+		}
+		if ($event === null) {
+			$event = new static;
+		}
+		$event->handled = false;
+		$event->name = $name;
+
+		if (is_object($class)) {
+			if ($event->sender === null) {
+				$event->sender = $class;
+			}
+			$class = get_class($class);
+		} else {
+			$class = ltrim($class, '\\');
+		}
+		do {
+			if (!empty(self::$_events[$name][$class])) {
+				foreach (self::$_events[$name][$class] as $handler) {
+					$event->data = $handler[1];
+					call_user_func($handler[0], $event);
+					if ($event->handled) {
+						return;
+					}
+				}
+			}
+		} while (($class = get_parent_class($class)) !== false);
+	}
+}

+ 53 - 0
php-yii2/app/vendor/yiisoft/yii2/base/Exception.php

@@ -0,0 +1,53 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * Exception represents a generic exception for all purposes.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Exception extends \Exception implements Arrayable
+{
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return 'Exception';
+	}
+
+	/**
+	 * Returns the array representation of this object.
+	 * @return array the array representation of this object.
+	 */
+	public function toArray()
+	{
+		return $this->toArrayRecursive($this);
+	}
+
+	/**
+	 * Returns the array representation of the exception and all previous exceptions recursively.
+	 * @param \Exception $exception object
+	 * @return array the array representation of the exception.
+	 */
+	protected function toArrayRecursive($exception)
+	{
+		$array = [
+			'type' => get_class($exception),
+			'name' => $exception instanceof self ? $exception->getName() : 'Exception',
+			'message' => $exception->getMessage(),
+			'code' => $exception->getCode(),
+		];
+		if (($prev = $exception->getPrevious()) !== null) {
+			$array['previous'] = $this->toArrayRecursive($prev);
+		}
+		return $array;
+	}
+}

+ 29 - 0
php-yii2/app/vendor/yiisoft/yii2/base/Extension.php

@@ -0,0 +1,29 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * Extension is the base class that may be extended by individual extensions.
+ *
+ * Extension serves as the bootstrap class for extensions. When an extension
+ * is installed via composer, the [[init()]] method of its Extension class (if any)
+ * will be invoked during the application initialization stage.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Extension
+{
+	/**
+	 * Initializes the extension.
+	 * This method is invoked at the end of [[Application::init()]].
+	 */
+	public static function init()
+	{
+	}
+}

+ 384 - 0
php-yii2/app/vendor/yiisoft/yii2/base/Formatter.php

@@ -0,0 +1,384 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+use Yii;
+use DateTime;
+use yii\helpers\HtmlPurifier;
+use yii\helpers\Html;
+
+/**
+ * Formatter provides a set of commonly used data formatting methods.
+ *
+ * The formatting methods provided by Formatter are all named in the form of `asXyz()`.
+ * The behavior of some of them may be configured via the properties of Formatter. For example,
+ * by configuring [[dateFormat]], one may control how [[asDate()]] formats the value into a date string.
+ *
+ * Formatter is configured as an application component in [[yii\base\Application]] by default.
+ * You can access that instance via `Yii::$app->formatter`.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Formatter extends Component
+{
+	/**
+	 * @var string the default format string to be used to format a date using PHP date() function.
+	 */
+	public $dateFormat = 'Y/m/d';
+	/**
+	 * @var string the default format string to be used to format a time using PHP date() function.
+	 */
+	public $timeFormat = 'h:i:s A';
+	/**
+	 * @var string the default format string to be used to format a date and time using PHP date() function.
+	 */
+	public $datetimeFormat = 'Y/m/d h:i:s A';
+	/**
+	 * @var string the text to be displayed when formatting a null. Defaults to '<span class="not-set">(not set)</span>'.
+	 */
+	public $nullDisplay;
+	/**
+	 * @var array the text to be displayed when formatting a boolean value. The first element corresponds
+	 * to the text display for false, the second element for true. Defaults to `['No', 'Yes']`.
+	 */
+	public $booleanFormat;
+	/**
+	 * @var string the character displayed as the decimal point when formatting a number.
+	 * If not set, "." will be used.
+	 */
+	public $decimalSeparator;
+	/**
+	 * @var string the character displayed as the thousands separator character when formatting a number.
+	 * If not set, "," will be used.
+	 */
+	public $thousandSeparator;
+
+
+	/**
+	 * Initializes the component.
+	 */
+	public function init()
+	{
+		if (empty($this->booleanFormat)) {
+			$this->booleanFormat = [Yii::t('yii', 'No'), Yii::t('yii', 'Yes')];
+		}
+		if ($this->nullDisplay === null) {
+			$this->nullDisplay = '<span class="not-set">' . Yii::t('yii', '(not set)') . '</span>';
+		}
+	}
+
+	/**
+	 * Formats the value based on the given format type.
+	 * This method will call one of the "as" methods available in this class to do the formatting.
+	 * For type "xyz", the method "asXyz" will be used. For example, if the format is "html",
+	 * then [[asHtml()]] will be used. Format names are case insensitive.
+	 * @param mixed $value the value to be formatted
+	 * @param string|array $format the format of the value, e.g., "html", "text". To specify additional
+	 * parameters of the formatting method, you may use an array. The first element of the array
+	 * specifies the format name, while the rest of the elements will be used as the parameters to the formatting
+	 * method. For example, a format of `['date', 'Y-m-d']` will cause the invocation of `asDate($value, 'Y-m-d')`.
+	 * @return string the formatting result
+	 * @throws InvalidParamException if the type is not supported by this class.
+	 */
+	public function format($value, $format)
+	{
+		if (is_array($format)) {
+			if (!isset($format[0])) {
+				throw new InvalidParamException('The $format array must contain at least one element.');
+			}
+			$f = $format[0];
+			$format[0] = $value;
+			$params = $format;
+			$format = $f;
+		} else {
+			$params = [$value];
+		}
+		$method = 'as' . $format;
+		if (method_exists($this, $method)) {
+			return call_user_func_array([$this, $method], $params);
+		} else {
+			throw new InvalidParamException("Unknown type: $format");
+		}
+	}
+
+	/**
+	 * Formats the value as is without any formatting.
+	 * This method simply returns back the parameter without any format.
+	 * @param mixed $value the value to be formatted
+	 * @return string the formatted result
+	 */
+	public function asRaw($value)
+	{
+		if ($value === null) {
+			return $this->nullDisplay;
+		}
+		return $value;
+	}
+
+	/**
+	 * Formats the value as an HTML-encoded plain text.
+	 * @param mixed $value the value to be formatted
+	 * @return string the formatted result
+	 */
+	public function asText($value)
+	{
+		if ($value === null) {
+			return $this->nullDisplay;
+		}
+		return Html::encode($value);
+	}
+
+	/**
+	 * Formats the value as an HTML-encoded plain text with newlines converted into breaks.
+	 * @param mixed $value the value to be formatted
+	 * @return string the formatted result
+	 */
+	public function asNtext($value)
+	{
+		if ($value === null) {
+			return $this->nullDisplay;
+		}
+		return nl2br(Html::encode($value));
+	}
+
+	/**
+	 * Formats the value as HTML-encoded text paragraphs.
+	 * Each text paragraph is enclosed within a `<p>` tag.
+	 * One or multiple consecutive empty lines divide two paragraphs.
+	 * @param mixed $value the value to be formatted
+	 * @return string the formatted result
+	 */
+	public function asParagraphs($value)
+	{
+		if ($value === null) {
+			return $this->nullDisplay;
+		}
+		return str_replace('<p></p>', '',
+			'<p>' . preg_replace('/[\r\n]{2,}/', "</p>\n<p>", Html::encode($value)) . '</p>'
+		);
+	}
+
+	/**
+	 * Formats the value as HTML text.
+	 * The value will be purified using [[HtmlPurifier]] to avoid XSS attacks.
+	 * Use [[asRaw()]] if you do not want any purification of the value.
+	 * @param mixed $value the value to be formatted
+	 * @param array|null $config the configuration for the HTMLPurifier class.
+	 * @return string the formatted result
+	 */
+	public function asHtml($value, $config = null)
+	{
+		if ($value === null) {
+			return $this->nullDisplay;
+		}
+		return HtmlPurifier::process($value, $config);
+	}
+
+	/**
+	 * Formats the value as a mailto link.
+	 * @param mixed $value the value to be formatted
+	 * @return string the formatted result
+	 */
+	public function asEmail($value)
+	{
+		if ($value === null) {
+			return $this->nullDisplay;
+		}
+		return Html::mailto(Html::encode($value), $value);
+	}
+
+	/**
+	 * Formats the value as an image tag.
+	 * @param mixed $value the value to be formatted
+	 * @return string the formatted result
+	 */
+	public function asImage($value)
+	{
+		if ($value === null) {
+			return $this->nullDisplay;
+		}
+		return Html::img($value);
+	}
+
+	/**
+	 * Formats the value as a hyperlink.
+	 * @param mixed $value the value to be formatted
+	 * @return string the formatted result
+	 */
+	public function asUrl($value)
+	{
+		if ($value === null) {
+			return $this->nullDisplay;
+		}
+		$url = $value;
+		if (strpos($url, 'http://') !== 0 && strpos($url, 'https://') !== 0) {
+			$url = 'http://' . $url;
+		}
+		return Html::a(Html::encode($value), $url);
+	}
+
+	/**
+	 * Formats the value as a boolean.
+	 * @param mixed $value the value to be formatted
+	 * @return string the formatted result
+	 * @see booleanFormat
+	 */
+	public function asBoolean($value)
+	{
+		if ($value === null) {
+			return $this->nullDisplay;
+		}
+		return $value ? $this->booleanFormat[1] : $this->booleanFormat[0];
+	}
+
+	/**
+	 * Formats the value as a date.
+	 * @param integer|string|DateTime $value the value to be formatted. The following
+	 * types of value are supported:
+	 *
+	 * - an integer representing a UNIX timestamp
+	 * - a string that can be parsed into a UNIX timestamp via `strtotime()`
+	 * - a PHP DateTime object
+	 *
+	 * @param string $format the format used to convert the value into a date string.
+	 * If null, [[dateFormat]] will be used. The format string should be one
+	 * that can be recognized by the PHP `date()` function.
+	 * @return string the formatted result
+	 * @see dateFormat
+	 */
+	public function asDate($value, $format = null)
+	{
+		if ($value === null) {
+			return $this->nullDisplay;
+		}
+		$value = $this->normalizeDatetimeValue($value);
+		return date($format === null ? $this->dateFormat : $format, $value);
+	}
+
+	/**
+	 * Formats the value as a time.
+	 * @param integer|string|DateTime $value the value to be formatted. The following
+	 * types of value are supported:
+	 *
+	 * - an integer representing a UNIX timestamp
+	 * - a string that can be parsed into a UNIX timestamp via `strtotime()`
+	 * - a PHP DateTime object
+	 *
+	 * @param string $format the format used to convert the value into a date string.
+	 * If null, [[timeFormat]] will be used. The format string should be one
+	 * that can be recognized by the PHP `date()` function.
+	 * @return string the formatted result
+	 * @see timeFormat
+	 */
+	public function asTime($value, $format = null)
+	{
+		if ($value === null) {
+			return $this->nullDisplay;
+		}
+		$value = $this->normalizeDatetimeValue($value);
+		return date($format === null ? $this->timeFormat : $format, $value);
+	}
+
+	/**
+	 * Formats the value as a datetime.
+	 * @param integer|string|DateTime $value the value to be formatted. The following
+	 * types of value are supported:
+	 *
+	 * - an integer representing a UNIX timestamp
+	 * - a string that can be parsed into a UNIX timestamp via `strtotime()`
+	 * - a PHP DateTime object
+	 *
+	 * @param string $format the format used to convert the value into a date string.
+	 * If null, [[datetimeFormat]] will be used. The format string should be one
+	 * that can be recognized by the PHP `date()` function.
+	 * @return string the formatted result
+	 * @see datetimeFormat
+	 */
+	public function asDatetime($value, $format = null)
+	{
+		if ($value === null) {
+			return $this->nullDisplay;
+		}
+		$value = $this->normalizeDatetimeValue($value);
+		return date($format === null ? $this->datetimeFormat : $format, $value);
+	}
+
+	/**
+	 * Normalizes the given datetime value as one that can be taken by various date/time formatting methods.
+	 * @param mixed $value the datetime value to be normalized.
+	 * @return integer the normalized datetime value
+	 */
+	protected function normalizeDatetimeValue($value)
+	{
+		if (is_string($value)) {
+			return is_numeric($value) || $value === '' ? (int)$value : strtotime($value);
+		} elseif ($value instanceof DateTime) {
+			return $value->getTimestamp();
+		} else {
+			return (int)$value;
+		}
+	}
+
+	/**
+	 * Formats the value as an integer.
+	 * @param mixed $value the value to be formatted
+	 * @return string the formatting result.
+	 */
+	public function asInteger($value)
+	{
+		if ($value === null) {
+			return $this->nullDisplay;
+		}
+		if (is_string($value) && preg_match('/^(-?\d+)/', $value, $matches)) {
+			return $matches[1];
+		} else {
+			$value = (int)$value;
+			return "$value";
+		}
+	}
+
+	/**
+	 * Formats the value as a double number.
+	 * Property [[decimalSeparator]] will be used to represent the decimal point.
+	 * @param mixed $value the value to be formatted
+	 * @param integer $decimals the number of digits after the decimal point
+	 * @return string the formatting result.
+	 * @see decimalSeparator
+	 */
+	public function asDouble($value, $decimals = 2)
+	{
+		if ($value === null) {
+			return $this->nullDisplay;
+		}
+		if ($this->decimalSeparator === null) {
+			return sprintf("%.{$decimals}f", $value);
+		} else {
+			return str_replace('.', $this->decimalSeparator, sprintf("%.{$decimals}f", $value));
+		}
+	}
+
+	/**
+	 * Formats the value as a number with decimal and thousand separators.
+	 * This method calls the PHP number_format() function to do the formatting.
+	 * @param mixed $value the value to be formatted
+	 * @param integer $decimals the number of digits after the decimal point
+	 * @return string the formatted result
+	 * @see decimalSeparator
+	 * @see thousandSeparator
+	 */
+	public function asNumber($value, $decimals = 0)
+	{
+		if ($value === null) {
+			return $this->nullDisplay;
+		}
+		$ds = isset($this->decimalSeparator) ? $this->decimalSeparator: '.';
+		$ts = isset($this->thousandSeparator) ? $this->thousandSeparator: ',';
+		return number_format($value, $decimals, $ds, $ts);
+	}
+}

+ 55 - 0
php-yii2/app/vendor/yiisoft/yii2/base/InlineAction.php

@@ -0,0 +1,55 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+use Yii;
+
+/**
+ * InlineAction represents an action that is defined as a controller method.
+ *
+ * The name of the controller method is available via [[actionMethod]] which
+ * is set by the [[controller]] who creates this action.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class InlineAction extends Action
+{
+	/**
+	 * @var string the controller method that  this inline action is associated with
+	 */
+	public $actionMethod;
+
+	/**
+	 * @param string $id the ID of this action
+	 * @param Controller $controller the controller that owns this action
+	 * @param string $actionMethod the controller method that  this inline action is associated with
+	 * @param array $config name-value pairs that will be used to initialize the object properties
+	 */
+	public function __construct($id, $controller, $actionMethod, $config = [])
+	{
+		$this->actionMethod = $actionMethod;
+		parent::__construct($id, $controller, $config);
+	}
+
+	/**
+	 * Runs this action with the specified parameters.
+	 * This method is mainly invoked by the controller.
+	 * @param array $params action parameters
+	 * @return mixed the result of the action
+	 */
+	public function runWithParams($params)
+	{
+		$args = $this->controller->bindActionParams($this, $params);
+		Yii::trace('Running action: ' . get_class($this->controller) . '::' . $this->actionMethod . '()', __METHOD__);
+		if (Yii::$app->requestedParams === null) {
+			Yii::$app->requestedParams = $args;
+		}
+		return call_user_func_array([$this->controller, $this->actionMethod], $args);
+	}
+}

+ 25 - 0
php-yii2/app/vendor/yiisoft/yii2/base/InvalidCallException.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * InvalidCallException represents an exception caused by calling a method in a wrong way.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class InvalidCallException extends Exception
+{
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return 'Invalid Call';
+	}
+}

+ 25 - 0
php-yii2/app/vendor/yiisoft/yii2/base/InvalidConfigException.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * InvalidConfigException represents an exception caused by incorrect object configuration.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class InvalidConfigException extends Exception
+{
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return 'Invalid Configuration';
+	}
+}

+ 25 - 0
php-yii2/app/vendor/yiisoft/yii2/base/InvalidParamException.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * InvalidParamException represents an exception caused by invalid parameters passed to a method.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class InvalidParamException extends Exception
+{
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return 'Invalid Parameter';
+	}
+}

+ 25 - 0
php-yii2/app/vendor/yiisoft/yii2/base/InvalidRouteException.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * InvalidRouteException represents an exception caused by an invalid route.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class InvalidRouteException extends UserException
+{
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return 'Invalid Route';
+	}
+}

+ 35 - 0
php-yii2/app/vendor/yiisoft/yii2/base/MailEvent.php

@@ -0,0 +1,35 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * ActionEvent represents the event parameter used for an action event.
+ *
+ * By setting the [[isValid]] property, one may control whether to continue running the action.
+ *
+ * @author Mark Jebri <[email protected]>
+ * @since 2.0
+ */
+class MailEvent extends Event
+{
+
+	/**
+	 * @var \yii\mail\MessageInterface mail message being send
+	 */
+	public $message;
+	/**
+	 * @var boolean if message send was successful
+	 */
+	public $isSuccessful;
+	/**
+	 * @var boolean whether to continue sending an email. Event handlers of
+	 * [[\yii\mail\BaseMailer::EVENT_BEFORE_SEND]] may set this property to decide whether
+	 * to continue send or not.
+	 */
+	public $isValid = true;
+}

+ 858 - 0
php-yii2/app/vendor/yiisoft/yii2/base/Model.php

@@ -0,0 +1,858 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+use Yii;
+use ArrayAccess;
+use ArrayObject;
+use ArrayIterator;
+use ReflectionClass;
+use IteratorAggregate;
+use yii\helpers\Inflector;
+use yii\validators\RequiredValidator;
+use yii\validators\Validator;
+
+/**
+ * Model is the base class for data models.
+ *
+ * Model implements the following commonly used features:
+ *
+ * - attribute declaration: by default, every public class member is considered as
+ *   a model attribute
+ * - attribute labels: each attribute may be associated with a label for display purpose
+ * - massive attribute assignment
+ * - scenario-based validation
+ *
+ * Model also raises the following events when performing data validation:
+ *
+ * - [[EVENT_BEFORE_VALIDATE]]: an event raised at the beginning of [[validate()]]
+ * - [[EVENT_AFTER_VALIDATE]]: an event raised at the end of [[validate()]]
+ *
+ * You may directly use Model to store model data, or extend it with customization.
+ * You may also customize Model by attaching [[ModelBehavior|model behaviors]].
+ *
+ * @property \yii\validators\Validator[] $activeValidators The validators applicable to the current
+ * [[scenario]]. This property is read-only.
+ * @property array $attributes Attribute values (name => value).
+ * @property array $errors An array of errors for all attributes. Empty array is returned if no error. The
+ * result is a two-dimensional array. See [[getErrors()]] for detailed description. This property is read-only.
+ * @property array $firstErrors The first errors. An empty array will be returned if there is no error. This
+ * property is read-only.
+ * @property ArrayIterator $iterator An iterator for traversing the items in the list. This property is
+ * read-only.
+ * @property string $scenario The scenario that this model is in. Defaults to [[DEFAULT_SCENARIO]].
+ * @property ArrayObject|\yii\validators\Validator[] $validators All the validators declared in the model.
+ * This property is read-only.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Model extends Component implements IteratorAggregate, ArrayAccess
+{
+	/**
+	 * The name of the default scenario.
+	 */
+	const DEFAULT_SCENARIO = 'default';
+
+	/**
+	 * @event ModelEvent an event raised at the beginning of [[validate()]]. You may set
+	 * [[ModelEvent::isValid]] to be false to stop the validation.
+	 */
+	const EVENT_BEFORE_VALIDATE = 'beforeValidate';
+	/**
+	 * @event Event an event raised at the end of [[validate()]]
+	 */
+	const EVENT_AFTER_VALIDATE = 'afterValidate';
+
+	/**
+	 * @var array validation errors (attribute name => array of errors)
+	 */
+	private $_errors;
+	/**
+	 * @var ArrayObject list of validators
+	 */
+	private $_validators;
+	/**
+	 * @var string current scenario
+	 */
+	private $_scenario = self::DEFAULT_SCENARIO;
+
+	/**
+	 * Returns the validation rules for attributes.
+	 *
+	 * Validation rules are used by [[validate()]] to check if attribute values are valid.
+	 * Child classes may override this method to declare different validation rules.
+	 *
+	 * Each rule is an array with the following structure:
+	 *
+	 * ~~~
+	 * [
+	 *     ['attribute1', 'attribute2'],
+	 *     'validator type',
+	 *     'on' => ['scenario1', 'scenario2'],
+	 *     ...other parameters...
+	 * ]
+	 * ~~~
+	 *
+	 * where
+	 *
+	 *  - attribute list: required, specifies the attributes array to be validated, for single attribute you can pass string;
+	 *  - validator type: required, specifies the validator to be used. It can be the name of a model
+	 *    class method, the name of a built-in validator, or a validator class name (or its path alias).
+	 *  - on: optional, specifies the [[scenario|scenarios]] array when the validation
+	 *    rule can be applied. If this option is not set, the rule will apply to all scenarios.
+	 *  - additional name-value pairs can be specified to initialize the corresponding validator properties.
+	 *    Please refer to individual validator class API for possible properties.
+	 *
+	 * A validator can be either an object of a class extending [[Validator]], or a model class method
+	 * (called *inline validator*) that has the following signature:
+	 *
+	 * ~~~
+	 * // $params refers to validation parameters given in the rule
+	 * function validatorName($attribute, $params)
+	 * ~~~
+	 *
+	 * In the above `$attribute` refers to currently validated attribute name while `$params` contains an array of
+	 * validator configuration options such as `max` in case of `string` validator. Currently validate attribute value
+	 * can be accessed as `$this->[$attribute]`.
+	 *
+	 * Yii also provides a set of [[Validator::builtInValidators|built-in validators]].
+	 * They each has an alias name which can be used when specifying a validation rule.
+	 *
+	 * Below are some examples:
+	 *
+	 * ~~~
+	 * [
+	 *     // built-in "required" validator
+	 *     [['username', 'password'], 'required'],
+	 *     // built-in "string" validator customized with "min" and "max" properties
+	 *     ['username', 'string', 'min' => 3, 'max' => 12],
+	 *     // built-in "compare" validator that is used in "register" scenario only
+	 *     ['password', 'compare', 'compareAttribute' => 'password2', 'on' => 'register'],
+	 *     // an inline validator defined via the "authenticate()" method in the model class
+	 *     ['password', 'authenticate', 'on' => 'login'],
+	 *     // a validator of class "DateRangeValidator"
+	 *     ['dateRange', 'DateRangeValidator'],
+	 * ];
+	 * ~~~
+	 *
+	 * Note, in order to inherit rules defined in the parent class, a child class needs to
+	 * merge the parent rules with child rules using functions such as `array_merge()`.
+	 *
+	 * @return array validation rules
+	 * @see scenarios()
+	 */
+	public function rules()
+	{
+		return [];
+	}
+
+	/**
+	 * Returns a list of scenarios and the corresponding active attributes.
+	 * An active attribute is one that is subject to validation in the current scenario.
+	 * The returned array should be in the following format:
+	 *
+	 * ~~~
+	 * [
+	 *     'scenario1' => ['attribute11', 'attribute12', ...],
+	 *     'scenario2' => ['attribute21', 'attribute22', ...],
+	 *     ...
+	 * ]
+	 * ~~~
+	 *
+	 * By default, an active attribute is considered safe and can be massively assigned.
+	 * If an attribute should NOT be massively assigned (thus considered unsafe),
+	 * please prefix the attribute with an exclamation character (e.g. '!rank').
+	 *
+	 * The default implementation of this method will return all scenarios found in the [[rules()]]
+	 * declaration. A special scenario named [[DEFAULT_SCENARIO]] will contain all attributes
+	 * found in the [[rules()]]. Each scenario will be associated with the attributes that
+	 * are being validated by the validation rules that apply to the scenario.
+	 *
+	 * @return array a list of scenarios and the corresponding active attributes.
+	 */
+	public function scenarios()
+	{
+		$scenarios = [self::DEFAULT_SCENARIO => []];
+		foreach ($this->getValidators() as $validator) {
+			foreach ($validator->on as $scenario) {
+				$scenarios[$scenario] = [];
+			}
+			foreach ($validator->except as $scenario) {
+				$scenarios[$scenario] = [];
+			}
+		}
+		$names = array_keys($scenarios);
+
+		foreach ($this->getValidators() as $validator) {
+			if (empty($validator->on) && empty($validator->except)) {
+				foreach ($names as $name) {
+					foreach ($validator->attributes as $attribute) {
+						$scenarios[$name][$attribute] = true;
+					}
+				}
+			} elseif (empty($validator->on)) {
+				foreach ($names as $name) {
+					if (!in_array($name, $validator->except, true)) {
+						foreach ($validator->attributes as $attribute) {
+							$scenarios[$name][$attribute] = true;
+						}
+					}
+				}
+			} else {
+				foreach ($validator->on as $name) {
+					foreach ($validator->attributes as $attribute) {
+						$scenarios[$name][$attribute] = true;
+					}
+				}
+			}
+		}
+
+		foreach ($scenarios as $scenario => $attributes) {
+			if (empty($attributes) && $scenario !== self::DEFAULT_SCENARIO) {
+				unset($scenarios[$scenario]);
+			} else {
+				$scenarios[$scenario] = array_keys($attributes);
+			}
+		}
+
+		return $scenarios;
+	}
+
+	/**
+	 * Returns the form name that this model class should use.
+	 *
+	 * The form name is mainly used by [[\yii\web\ActiveForm]] to determine how to name
+	 * the input fields for the attributes in a model. If the form name is "A" and an attribute
+	 * name is "b", then the corresponding input name would be "A[b]". If the form name is
+	 * an empty string, then the input name would be "b".
+	 *
+	 * By default, this method returns the model class name (without the namespace part)
+	 * as the form name. You may override it when the model is used in different forms.
+	 *
+	 * @return string the form name of this model class.
+	 */
+	public function formName()
+	{
+		$reflector = new ReflectionClass($this);
+		return $reflector->getShortName();
+	}
+
+	/**
+	 * Returns the list of attribute names.
+	 * By default, this method returns all public non-static properties of the class.
+	 * You may override this method to change the default behavior.
+	 * @return array list of attribute names.
+	 */
+	public function attributes()
+	{
+		$class = new ReflectionClass($this);
+		$names = [];
+		foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
+			if (!$property->isStatic()) {
+				$names[] = $property->getName();
+			}
+		}
+		return $names;
+	}
+
+	/**
+	 * Returns the attribute labels.
+	 *
+	 * Attribute labels are mainly used for display purpose. For example, given an attribute
+	 * `firstName`, we can declare a label `First Name` which is more user-friendly and can
+	 * be displayed to end users.
+	 *
+	 * By default an attribute label is generated using [[generateAttributeLabel()]].
+	 * This method allows you to explicitly specify attribute labels.
+	 *
+	 * Note, in order to inherit labels defined in the parent class, a child class needs to
+	 * merge the parent labels with child labels using functions such as `array_merge()`.
+	 *
+	 * @return array attribute labels (name => label)
+	 * @see generateAttributeLabel()
+	 */
+	public function attributeLabels()
+	{
+		return [];
+	}
+
+	/**
+	 * Performs the data validation.
+	 *
+	 * This method executes the validation rules applicable to the current [[scenario]].
+	 * The following criteria are used to determine whether a rule is currently applicable:
+	 *
+	 * - the rule must be associated with the attributes relevant to the current scenario;
+	 * - the rules must be effective for the current scenario.
+	 *
+	 * This method will call [[beforeValidate()]] and [[afterValidate()]] before and
+	 * after the actual validation, respectively. If [[beforeValidate()]] returns false,
+	 * the validation will be cancelled and [[afterValidate()]] will not be called.
+	 *
+	 * Errors found during the validation can be retrieved via [[getErrors()]],
+	 * [[getFirstErrors()]] and [[getFirstError()]].
+	 *
+	 * @param array $attributes list of attributes that should be validated.
+	 * If this parameter is empty, it means any attribute listed in the applicable
+	 * validation rules should be validated.
+	 * @param boolean $clearErrors whether to call [[clearErrors()]] before performing validation
+	 * @return boolean whether the validation is successful without any error.
+	 * @throws InvalidParamException if the current scenario is unknown.
+	 */
+	public function validate($attributes = null, $clearErrors = true)
+	{
+		$scenarios = $this->scenarios();
+		$scenario = $this->getScenario();
+		if (!isset($scenarios[$scenario])) {
+			throw new InvalidParamException("Unknown scenario: $scenario");
+		}
+
+		if ($clearErrors) {
+			$this->clearErrors();
+		}
+		if ($attributes === null) {
+			$attributes = $this->activeAttributes();
+		}
+		if ($this->beforeValidate()) {
+			foreach ($this->getActiveValidators() as $validator) {
+				$validator->validateAttributes($this, $attributes);
+			}
+			$this->afterValidate();
+			return !$this->hasErrors();
+		}
+		return false;
+	}
+
+	/**
+	 * This method is invoked before validation starts.
+	 * The default implementation raises a `beforeValidate` event.
+	 * You may override this method to do preliminary checks before validation.
+	 * Make sure the parent implementation is invoked so that the event can be raised.
+	 * @return boolean whether the validation should be executed. Defaults to true.
+	 * If false is returned, the validation will stop and the model is considered invalid.
+	 */
+	public function beforeValidate()
+	{
+		$event = new ModelEvent;
+		$this->trigger(self::EVENT_BEFORE_VALIDATE, $event);
+		return $event->isValid;
+	}
+
+	/**
+	 * This method is invoked after validation ends.
+	 * The default implementation raises an `afterValidate` event.
+	 * You may override this method to do postprocessing after validation.
+	 * Make sure the parent implementation is invoked so that the event can be raised.
+	 */
+	public function afterValidate()
+	{
+		$this->trigger(self::EVENT_AFTER_VALIDATE);
+	}
+
+	/**
+	 * Returns all the validators declared in [[rules()]].
+	 *
+	 * This method differs from [[getActiveValidators()]] in that the latter
+	 * only returns the validators applicable to the current [[scenario]].
+	 *
+	 * Because this method returns an ArrayObject object, you may
+	 * manipulate it by inserting or removing validators (useful in model behaviors).
+	 * For example,
+	 *
+	 * ~~~
+	 * $model->validators[] = $newValidator;
+	 * ~~~
+	 *
+	 * @return ArrayObject|\yii\validators\Validator[] all the validators declared in the model.
+	 */
+	public function getValidators()
+	{
+		if ($this->_validators === null) {
+			$this->_validators = $this->createValidators();
+		}
+		return $this->_validators;
+	}
+
+	/**
+	 * Returns the validators applicable to the current [[scenario]].
+	 * @param string $attribute the name of the attribute whose applicable validators should be returned.
+	 * If this is null, the validators for ALL attributes in the model will be returned.
+	 * @return \yii\validators\Validator[] the validators applicable to the current [[scenario]].
+	 */
+	public function getActiveValidators($attribute = null)
+	{
+		$validators = [];
+		$scenario = $this->getScenario();
+		foreach ($this->getValidators() as $validator) {
+			if ($validator->isActive($scenario) && ($attribute === null || in_array($attribute, $validator->attributes, true))) {
+				$validators[] = $validator;
+			}
+		}
+		return $validators;
+	}
+
+	/**
+	 * Creates validator objects based on the validation rules specified in [[rules()]].
+	 * Unlike [[getValidators()]], each time this method is called, a new list of validators will be returned.
+	 * @return ArrayObject validators
+	 * @throws InvalidConfigException if any validation rule configuration is invalid
+	 */
+	public function createValidators()
+	{
+		$validators = new ArrayObject;
+		foreach ($this->rules() as $rule) {
+			if ($rule instanceof Validator) {
+				$validators->append($rule);
+			} elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type
+				$validator = Validator::createValidator($rule[1], $this, (array) $rule[0], array_slice($rule, 2));
+				$validators->append($validator);
+			} else {
+				throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.');
+			}
+		}
+		return $validators;
+	}
+
+	/**
+	 * Returns a value indicating whether the attribute is required.
+	 * This is determined by checking if the attribute is associated with a
+	 * [[\yii\validators\RequiredValidator|required]] validation rule in the
+	 * current [[scenario]].
+	 * @param string $attribute attribute name
+	 * @return boolean whether the attribute is required
+	 */
+	public function isAttributeRequired($attribute)
+	{
+		foreach ($this->getActiveValidators($attribute) as $validator) {
+			if ($validator instanceof RequiredValidator) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Returns a value indicating whether the attribute is safe for massive assignments.
+	 * @param string $attribute attribute name
+	 * @return boolean whether the attribute is safe for massive assignments
+	 * @see safeAttributes()
+	 */
+	public function isAttributeSafe($attribute)
+	{
+		return in_array($attribute, $this->safeAttributes(), true);
+	}
+
+	/**
+	 * Returns a value indicating whether the attribute is active in the current scenario.
+	 * @param string $attribute attribute name
+	 * @return boolean whether the attribute is active in the current scenario
+	 * @see activeAttributes()
+	 */
+	public function isAttributeActive($attribute)
+	{
+		return in_array($attribute, $this->activeAttributes(), true);
+	}
+
+	/**
+	 * Returns the text label for the specified attribute.
+	 * @param string $attribute the attribute name
+	 * @return string the attribute label
+	 * @see generateAttributeLabel()
+	 * @see attributeLabels()
+	 */
+	public function getAttributeLabel($attribute)
+	{
+		$labels = $this->attributeLabels();
+		return isset($labels[$attribute]) ? $labels[$attribute] : $this->generateAttributeLabel($attribute);
+	}
+
+	/**
+	 * Returns a value indicating whether there is any validation error.
+	 * @param string|null $attribute attribute name. Use null to check all attributes.
+	 * @return boolean whether there is any error.
+	 */
+	public function hasErrors($attribute = null)
+	{
+		return $attribute === null ? !empty($this->_errors) : isset($this->_errors[$attribute]);
+	}
+
+	/**
+	 * Returns the errors for all attribute or a single attribute.
+	 * @param string $attribute attribute name. Use null to retrieve errors for all attributes.
+	 * @property array An array of errors for all attributes. Empty array is returned if no error.
+	 * The result is a two-dimensional array. See [[getErrors()]] for detailed description.
+	 * @return array errors for all attributes or the specified attribute. Empty array is returned if no error.
+	 * Note that when returning errors for all attributes, the result is a two-dimensional array, like the following:
+	 *
+	 * ~~~
+	 * [
+	 *     'username' => [
+	 *         'Username is required.',
+	 *         'Username must contain only word characters.',
+	 *     ],
+	 *     'email' => [
+	 *         'Email address is invalid.',
+	 *     ]
+	 * ]
+	 * ~~~
+	 *
+	 * @see getFirstErrors()
+	 * @see getFirstError()
+	 */
+	public function getErrors($attribute = null)
+	{
+		if ($attribute === null) {
+			return $this->_errors === null ? [] : $this->_errors;
+		} else {
+			return isset($this->_errors[$attribute]) ? $this->_errors[$attribute] : [];
+		}
+	}
+
+	/**
+	 * Returns the first error of every attribute in the model.
+	 * @return array the first errors. An empty array will be returned if there is no error.
+	 * @see getErrors()
+	 * @see getFirstError()
+	 */
+	public function getFirstErrors()
+	{
+		if (empty($this->_errors)) {
+			return [];
+		} else {
+			$errors = [];
+			foreach ($this->_errors as $attributeErrors) {
+				if (isset($attributeErrors[0])) {
+					$errors[] = $attributeErrors[0];
+				}
+			}
+		}
+		return $errors;
+	}
+
+	/**
+	 * Returns the first error of the specified attribute.
+	 * @param string $attribute attribute name.
+	 * @return string the error message. Null is returned if no error.
+	 * @see getErrors()
+	 * @see getFirstErrors()
+	 */
+	public function getFirstError($attribute)
+	{
+		return isset($this->_errors[$attribute]) ? reset($this->_errors[$attribute]) : null;
+	}
+
+	/**
+	 * Adds a new error to the specified attribute.
+	 * @param string $attribute attribute name
+	 * @param string $error new error message
+	 */
+	public function addError($attribute, $error = '')
+	{
+		$this->_errors[$attribute][] = $error;
+	}
+
+	/**
+	 * Removes errors for all attributes or a single attribute.
+	 * @param string $attribute attribute name. Use null to remove errors for all attribute.
+	 */
+	public function clearErrors($attribute = null)
+	{
+		if ($attribute === null) {
+			$this->_errors = [];
+		} else {
+			unset($this->_errors[$attribute]);
+		}
+	}
+
+	/**
+	 * Generates a user friendly attribute label based on the give attribute name.
+	 * This is done by replacing underscores, dashes and dots with blanks and
+	 * changing the first letter of each word to upper case.
+	 * For example, 'department_name' or 'DepartmentName' will generate 'Department Name'.
+	 * @param string $name the column name
+	 * @return string the attribute label
+	 */
+	public function generateAttributeLabel($name)
+	{
+		return Inflector::camel2words($name, true);
+	}
+
+	/**
+	 * Returns attribute values.
+	 * @param array $names list of attributes whose value needs to be returned.
+	 * Defaults to null, meaning all attributes listed in [[attributes()]] will be returned.
+	 * If it is an array, only the attributes in the array will be returned.
+	 * @param array $except list of attributes whose value should NOT be returned.
+	 * @return array attribute values (name => value).
+	 */
+	public function getAttributes($names = null, $except = [])
+	{
+		$values = [];
+		if ($names === null) {
+			$names = $this->attributes();
+		}
+		foreach ($names as $name) {
+			$values[$name] = $this->$name;
+		}
+		foreach ($except as $name) {
+			unset($values[$name]);
+		}
+
+		return $values;
+	}
+
+	/**
+	 * Sets the attribute values in a massive way.
+	 * @param array $values attribute values (name => value) to be assigned to the model.
+	 * @param boolean $safeOnly whether the assignments should only be done to the safe attributes.
+	 * A safe attribute is one that is associated with a validation rule in the current [[scenario]].
+	 * @see safeAttributes()
+	 * @see attributes()
+	 */
+	public function setAttributes($values, $safeOnly = true)
+	{
+		if (is_array($values)) {
+			$attributes = array_flip($safeOnly ? $this->safeAttributes() : $this->attributes());
+			foreach ($values as $name => $value) {
+				if (isset($attributes[$name])) {
+					$this->$name = $value;
+				} elseif ($safeOnly) {
+					$this->onUnsafeAttribute($name, $value);
+				}
+			}
+		}
+	}
+
+	/**
+	 * This method is invoked when an unsafe attribute is being massively assigned.
+	 * The default implementation will log a warning message if YII_DEBUG is on.
+	 * It does nothing otherwise.
+	 * @param string $name the unsafe attribute name
+	 * @param mixed $value the attribute value
+	 */
+	public function onUnsafeAttribute($name, $value)
+	{
+		if (YII_DEBUG) {
+			Yii::trace("Failed to set unsafe attribute '$name' in '" . get_class($this) . "'.", __METHOD__);
+		}
+	}
+
+	/**
+	 * Returns the scenario that this model is used in.
+	 *
+	 * Scenario affects how validation is performed and which attributes can
+	 * be massively assigned.
+	 *
+	 * @return string the scenario that this model is in. Defaults to [[DEFAULT_SCENARIO]].
+	 */
+	public function getScenario()
+	{
+		return $this->_scenario;
+	}
+
+	/**
+	 * Sets the scenario for the model.
+	 * Note that this method does not check if the scenario exists or not.
+	 * The method [[validate()]] will perform this check.
+	 * @param string $value the scenario that this model is in.
+	 */
+	public function setScenario($value)
+	{
+		$this->_scenario = $value;
+	}
+
+	/**
+	 * Returns the attribute names that are safe to be massively assigned in the current scenario.
+	 * @return string[] safe attribute names
+	 */
+	public function safeAttributes()
+	{
+		$scenario = $this->getScenario();
+		$scenarios = $this->scenarios();
+		if (!isset($scenarios[$scenario])) {
+			return [];
+		}
+		$attributes = [];
+		foreach ($scenarios[$scenario] as $attribute) {
+			if ($attribute[0] !== '!') {
+				$attributes[] = $attribute;
+			}
+		}
+		return $attributes;
+	}
+
+	/**
+	 * Returns the attribute names that are subject to validation in the current scenario.
+	 * @return string[] safe attribute names
+	 */
+	public function activeAttributes()
+	{
+		$scenario = $this->getScenario();
+		$scenarios = $this->scenarios();
+		if (!isset($scenarios[$scenario])) {
+			return [];
+		}
+		$attributes = $scenarios[$scenario];
+		foreach ($attributes as $i => $attribute) {
+			if ($attribute[0] === '!') {
+				$attributes[$i] = substr($attribute, 1);
+			}
+		}
+		return $attributes;
+	}
+
+	/**
+	 * Populates the model with the data from end user.
+	 * The data to be loaded is `$data[formName]`, where `formName` refers to the value of [[formName()]].
+	 * If [[formName()]] is empty, the whole `$data` array will be used to populate the model.
+	 * The data being populated is subject to the safety check by [[setAttributes()]].
+	 * @param array $data the data array. This is usually `$_POST` or `$_GET`, but can also be any valid array
+	 * supplied by end user.
+	 * @param string $formName the form name to be used for loading the data into the model.
+	 * If not set, [[formName()]] will be used.
+	 * @return boolean whether the model is successfully populated with some data.
+	 */
+	public function load($data, $formName = null)
+	{
+		$scope = $formName === null ? $this->formName() : $formName;
+		if ($scope == '') {
+			$this->setAttributes($data);
+			return true;
+		} elseif (isset($data[$scope])) {
+			$this->setAttributes($data[$scope]);
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * Populates a set of models with the data from end user.
+	 * This method is mainly used to collect tabular data input.
+	 * The data to be loaded for each model is `$data[formName][index]`, where `formName`
+	 * refers to the value of [[formName()]], and `index` the index of the model in the `$models` array.
+	 * If [[formName()]] is empty, `$data[index]` will be used to populate each model.
+	 * The data being populated to each model is subject to the safety check by [[setAttributes()]].
+	 * @param array $models the models to be populated. Note that all models should have the same class.
+	 * @param array $data the data array. This is usually `$_POST` or `$_GET`, but can also be any valid array
+	 * supplied by end user.
+	 * @return boolean whether the model is successfully populated with some data.
+	 */
+	public static function loadMultiple($models, $data)
+	{
+		/** @var Model $model */
+		$model = reset($models);
+		if ($model === false) {
+			return false;
+		}
+		$success = false;
+		$scope = $model->formName();
+		foreach ($models as $i => $model) {
+			if ($scope == '') {
+				if (isset($data[$i])) {
+					$model->setAttributes($data[$i]);
+					$success = true;
+				}
+			} elseif (isset($data[$scope][$i])) {
+				$model->setAttributes($data[$scope][$i]);
+				$success = true;
+			}
+		}
+		return $success;
+	}
+
+	/**
+	 * Validates multiple models.
+	 * This method will validate every model. The models being validated may
+	 * be of the same or different types.
+	 * @param array $models the models to be validated
+	 * @param array $attributes list of attributes that should be validated.
+	 * If this parameter is empty, it means any attribute listed in the applicable
+	 * validation rules should be validated.
+	 * @return boolean whether all models are valid. False will be returned if one
+	 * or multiple models have validation error.
+	 */
+	public static function validateMultiple($models, $attributes = null)
+	{
+		$valid = true;
+		/** @var Model $model */
+		foreach ($models as $model) {
+			$valid = $model->validate($attributes) && $valid;
+		}
+		return $valid;
+	}
+
+	/**
+	 * Converts the object into an array.
+	 * The default implementation will return [[attributes]].
+	 * @return array the array representation of the object
+	 */
+	public function toArray()
+	{
+		return $this->getAttributes();
+	}
+
+	/**
+	 * Returns an iterator for traversing the attributes in the model.
+	 * This method is required by the interface IteratorAggregate.
+	 * @return ArrayIterator an iterator for traversing the items in the list.
+	 */
+	public function getIterator()
+	{
+		$attributes = $this->getAttributes();
+		return new ArrayIterator($attributes);
+	}
+
+	/**
+	 * Returns whether there is an element at the specified offset.
+	 * This method is required by the SPL interface `ArrayAccess`.
+	 * It is implicitly called when you use something like `isset($model[$offset])`.
+	 * @param mixed $offset the offset to check on
+	 * @return boolean
+	 */
+	public function offsetExists($offset)
+	{
+		return $this->$offset !== null;
+	}
+
+	/**
+	 * Returns the element at the specified offset.
+	 * This method is required by the SPL interface `ArrayAccess`.
+	 * It is implicitly called when you use something like `$value = $model[$offset];`.
+	 * @param mixed $offset the offset to retrieve element.
+	 * @return mixed the element at the offset, null if no element is found at the offset
+	 */
+	public function offsetGet($offset)
+	{
+		return $this->$offset;
+	}
+
+	/**
+	 * Sets the element at the specified offset.
+	 * This method is required by the SPL interface `ArrayAccess`.
+	 * It is implicitly called when you use something like `$model[$offset] = $item;`.
+	 * @param integer $offset the offset to set element
+	 * @param mixed $item the element value
+	 */
+	public function offsetSet($offset, $item)
+	{
+		$this->$offset = $item;
+	}
+
+	/**
+	 * Sets the element value at the specified offset to null.
+	 * This method is required by the SPL interface `ArrayAccess`.
+	 * It is implicitly called when you use something like `unset($model[$offset])`.
+	 * @param mixed $offset the offset to unset element
+	 */
+	public function offsetUnset($offset)
+	{
+		$this->$offset = null;
+	}
+}

+ 25 - 0
php-yii2/app/vendor/yiisoft/yii2/base/ModelEvent.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * ModelEvent class.
+ *
+ * ModelEvent represents the parameter needed by model events.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class ModelEvent extends Event
+{
+	/**
+	 * @var boolean whether the model is in valid status. Defaults to true.
+	 * A model is in valid status if it passes validations or certain checks.
+	 */
+	public $isValid = true;
+}

+ 667 - 0
php-yii2/app/vendor/yiisoft/yii2/base/Module.php

@@ -0,0 +1,667 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+use Yii;
+
+/**
+ * Module is the base class for module and application classes.
+ *
+ * A module represents a sub-application which contains MVC elements by itself, such as
+ * models, views, controllers, etc.
+ *
+ * A module may consist of [[modules|sub-modules]].
+ *
+ * [[components|Components]] may be registered with the module so that they are globally
+ * accessible within the module.
+ *
+ * @property array $aliases List of path aliases to be defined. The array keys are alias names (must start
+ * with '@') and the array values are the corresponding paths or aliases. See [[setAliases()]] for an example.
+ * This property is write-only.
+ * @property string $basePath The root directory of the module.
+ * @property array $components The components (indexed by their IDs).
+ * @property string $controllerPath The directory that contains the controller classes.
+ * @property string $layoutPath The root directory of layout files. Defaults to "[[viewPath]]/layouts".
+ * @property array $modules The modules (indexed by their IDs).
+ * @property string $uniqueId The unique ID of the module. This property is read-only.
+ * @property string $viewPath The root directory of view files. Defaults to "[[basePath]]/view".
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Module extends Component
+{
+	/**
+	 * @var array custom module parameters (name => value).
+	 */
+	public $params = [];
+	/**
+	 * @var array the IDs of the components or modules that should be preloaded right after initialization.
+	 */
+	public $preload = [];
+	/**
+	 * @var string an ID that uniquely identifies this module among other modules which have the same [[module|parent]].
+	 */
+	public $id;
+	/**
+	 * @var Module the parent module of this module. Null if this module does not have a parent.
+	 */
+	public $module;
+	/**
+	 * @var string|boolean the layout that should be applied for views within this module. This refers to a view name
+	 * relative to [[layoutPath]]. If this is not set, it means the layout value of the [[module|parent module]]
+	 * will be taken. If this is false, layout will be disabled within this module.
+	 */
+	public $layout;
+	/**
+	 * @var array mapping from controller ID to controller configurations.
+	 * Each name-value pair specifies the configuration of a single controller.
+	 * A controller configuration can be either a string or an array.
+	 * If the former, the string should be the fully qualified class name of the controller.
+	 * If the latter, the array must contain a 'class' element which specifies
+	 * the controller's fully qualified class name, and the rest of the name-value pairs
+	 * in the array are used to initialize the corresponding controller properties. For example,
+	 *
+	 * ~~~
+	 * [
+	 *   'account' => 'app\controllers\UserController',
+	 *   'article' => [
+	 *      'class' => 'app\controllers\PostController',
+	 *      'pageTitle' => 'something new',
+	 *   ],
+	 * ]
+	 * ~~~
+	 */
+	public $controllerMap = [];
+	/**
+	 * @var string the namespace that controller classes are in. If not set,
+	 * it will use the "controllers" sub-namespace under the namespace of this module.
+	 * For example, if the namespace of this module is "foo\bar", then the default
+	 * controller namespace would be "foo\bar\controllers".
+	 */
+	public $controllerNamespace;
+	/**
+	 * @return string the default route of this module. Defaults to 'default'.
+	 * The route may consist of child module ID, controller ID, and/or action ID.
+	 * For example, `help`, `post/create`, `admin/post/create`.
+	 * If action ID is not given, it will take the default value as specified in
+	 * [[Controller::defaultAction]].
+	 */
+	public $defaultRoute = 'default';
+	/**
+	 * @var string the root directory of the module.
+	 */
+	private $_basePath;
+	/**
+	 * @var string the root directory that contains view files for this module
+	 */
+	private $_viewPath;
+	/**
+	 * @var string the root directory that contains layout view files for this module.
+	 */
+	private $_layoutPath;
+	/**
+	 * @var string the directory containing controller classes in the module.
+	 */
+	private $_controllerPath;
+	/**
+	 * @var array child modules of this module
+	 */
+	private $_modules = [];
+	/**
+	 * @var array components registered under this module
+	 */
+	private $_components = [];
+
+	/**
+	 * Constructor.
+	 * @param string $id the ID of this module
+	 * @param Module $parent the parent module (if any)
+	 * @param array $config name-value pairs that will be used to initialize the object properties
+	 */
+	public function __construct($id, $parent = null, $config = [])
+	{
+		$this->id = $id;
+		$this->module = $parent;
+		parent::__construct($config);
+	}
+
+	/**
+	 * Getter magic method.
+	 * This method is overridden to support accessing components
+	 * like reading module properties.
+	 * @param string $name component or property name
+	 * @return mixed the named property value
+	 */
+	public function __get($name)
+	{
+		if ($this->hasComponent($name)) {
+			return $this->getComponent($name);
+		} else {
+			return parent::__get($name);
+		}
+	}
+
+	/**
+	 * Checks if a property value is null.
+	 * This method overrides the parent implementation by checking
+	 * if the named component is loaded.
+	 * @param string $name the property name or the event name
+	 * @return boolean whether the property value is null
+	 */
+	public function __isset($name)
+	{
+		if ($this->hasComponent($name)) {
+			return $this->getComponent($name) !== null;
+		} else {
+			return parent::__isset($name);
+		}
+	}
+
+	/**
+	 * Initializes the module.
+	 * This method is called after the module is created and initialized with property values
+	 * given in configuration. The default implementation will call [[preloadComponents()]] to
+	 * load components that are declared in [[preload]].
+	 *
+	 * If you override this method, please make sure you call the parent implementation.
+	 */
+	public function init()
+	{
+		if ($this->controllerNamespace === null) {
+			$class = get_class($this);
+			if (($pos = strrpos($class, '\\')) !== false) {
+				$this->controllerNamespace = substr($class, 0, $pos) . '\\controllers';
+			}
+		}
+		$this->preloadComponents();
+	}
+
+	/**
+	 * Returns an ID that uniquely identifies this module among all modules within the current application.
+	 * Note that if the module is an application, an empty string will be returned.
+	 * @return string the unique ID of the module.
+	 */
+	public function getUniqueId()
+	{
+		return $this->module ? ltrim($this->module->getUniqueId() . '/' . $this->id, '/') : $this->id;
+	}
+
+	/**
+	 * Returns the root directory of the module.
+	 * It defaults to the directory containing the module class file.
+	 * @return string the root directory of the module.
+	 */
+	public function getBasePath()
+	{
+		if ($this->_basePath === null) {
+			$class = new \ReflectionClass($this);
+			$this->_basePath = dirname($class->getFileName());
+		}
+		return $this->_basePath;
+	}
+
+	/**
+	 * Sets the root directory of the module.
+	 * This method can only be invoked at the beginning of the constructor.
+	 * @param string $path the root directory of the module. This can be either a directory name or a path alias.
+	 * @throws InvalidParamException if the directory does not exist.
+	 */
+	public function setBasePath($path)
+	{
+		$path = Yii::getAlias($path);
+		$p = realpath($path);
+		if ($p !== false && is_dir($p)) {
+			$this->_basePath = $p;
+		} else {
+			throw new InvalidParamException("The directory does not exist: $path");
+		}
+	}
+
+	/**
+	 * Returns the directory that contains the controller classes.
+	 * Defaults to "[[basePath]]/controllers".
+	 * @return string the directory that contains the controller classes.
+	 */
+	public function getControllerPath()
+	{
+		if ($this->_controllerPath !== null) {
+			return $this->_controllerPath;
+		} else {
+			return $this->_controllerPath = $this->getBasePath() . DIRECTORY_SEPARATOR . 'controllers';
+		}
+	}
+
+	/**
+	 * Sets the directory that contains the controller classes.
+	 * @param string $path the directory that contains the controller classes.
+	 * This can be either a directory name or a path alias.
+	 * @throws InvalidParamException if the directory is invalid
+	 */
+	public function setControllerPath($path)
+	{
+		$this->_controllerPath = Yii::getAlias($path);
+	}
+
+	/**
+	 * Returns the directory that contains the view files for this module.
+	 * @return string the root directory of view files. Defaults to "[[basePath]]/view".
+	 */
+	public function getViewPath()
+	{
+		if ($this->_viewPath !== null) {
+			return $this->_viewPath;
+		} else {
+			return $this->_viewPath = $this->getBasePath() . DIRECTORY_SEPARATOR . 'views';
+		}
+	}
+
+	/**
+	 * Sets the directory that contains the view files.
+	 * @param string $path the root directory of view files.
+	 * @throws InvalidParamException if the directory is invalid
+	 */
+	public function setViewPath($path)
+	{
+		$this->_viewPath = Yii::getAlias($path);
+	}
+
+	/**
+	 * Returns the directory that contains layout view files for this module.
+	 * @return string the root directory of layout files. Defaults to "[[viewPath]]/layouts".
+	 */
+	public function getLayoutPath()
+	{
+		if ($this->_layoutPath !== null) {
+			return $this->_layoutPath;
+		} else {
+			return $this->_layoutPath = $this->getViewPath() . DIRECTORY_SEPARATOR . 'layouts';
+		}
+	}
+
+	/**
+	 * Sets the directory that contains the layout files.
+	 * @param string $path the root directory of layout files.
+	 * @throws InvalidParamException if the directory is invalid
+	 */
+	public function setLayoutPath($path)
+	{
+		$this->_layoutPath = Yii::getAlias($path);
+	}
+
+	/**
+	 * Defines path aliases.
+	 * This method calls [[Yii::setAlias()]] to register the path aliases.
+	 * This method is provided so that you can define path aliases when configuring a module.
+	 * @property array list of path aliases to be defined. The array keys are alias names
+	 * (must start with '@') and the array values are the corresponding paths or aliases.
+	 * See [[setAliases()]] for an example.
+	 * @param array $aliases list of path aliases to be defined. The array keys are alias names
+	 * (must start with '@') and the array values are the corresponding paths or aliases.
+	 * For example,
+	 *
+	 * ~~~
+	 * [
+	 *	'@models' => '@app/models', // an existing alias
+	 *	'@backend' => __DIR__ . '/../backend',  // a directory
+	 * ]
+	 * ~~~
+	 */
+	public function setAliases($aliases)
+	{
+		foreach ($aliases as $name => $alias) {
+			Yii::setAlias($name, $alias);
+		}
+	}
+
+	/**
+	 * Checks whether the child module of the specified ID exists.
+	 * This method supports checking the existence of both child and grand child modules.
+	 * @param string $id module ID. For grand child modules, use ID path relative to this module (e.g. `admin/content`).
+	 * @return boolean whether the named module exists. Both loaded and unloaded modules
+	 * are considered.
+	 */
+	public function hasModule($id)
+	{
+		if (($pos = strpos($id, '/')) !== false) {
+			// sub-module
+			$module = $this->getModule(substr($id, 0, $pos));
+			return $module === null ? false : $module->hasModule(substr($id, $pos + 1));
+		} else {
+			return isset($this->_modules[$id]);
+		}
+	}
+
+	/**
+	 * Retrieves the child module of the specified ID.
+	 * This method supports retrieving both child modules and grand child modules.
+	 * @param string $id module ID (case-sensitive). To retrieve grand child modules,
+	 * use ID path relative to this module (e.g. `admin/content`).
+	 * @param boolean $load whether to load the module if it is not yet loaded.
+	 * @return Module|null the module instance, null if the module does not exist.
+	 * @see hasModule()
+	 */
+	public function getModule($id, $load = true)
+	{
+		if (($pos = strpos($id, '/')) !== false) {
+			// sub-module
+			$module = $this->getModule(substr($id, 0, $pos));
+			return $module === null ? null : $module->getModule(substr($id, $pos + 1), $load);
+		}
+
+		if (isset($this->_modules[$id])) {
+			if ($this->_modules[$id] instanceof Module) {
+				return $this->_modules[$id];
+			} elseif ($load) {
+				Yii::trace("Loading module: $id", __METHOD__);
+				return $this->_modules[$id] = Yii::createObject($this->_modules[$id], $id, $this);
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Adds a sub-module to this module.
+	 * @param string $id module ID
+	 * @param Module|array|null $module the sub-module to be added to this module. This can
+	 * be one of the followings:
+	 *
+	 * - a [[Module]] object
+	 * - a configuration array: when [[getModule()]] is called initially, the array
+	 *   will be used to instantiate the sub-module
+	 * - null: the named sub-module will be removed from this module
+	 */
+	public function setModule($id, $module)
+	{
+		if ($module === null) {
+			unset($this->_modules[$id]);
+		} else {
+			$this->_modules[$id] = $module;
+		}
+	}
+
+	/**
+	 * Returns the sub-modules in this module.
+	 * @param boolean $loadedOnly whether to return the loaded sub-modules only. If this is set false,
+	 * then all sub-modules registered in this module will be returned, whether they are loaded or not.
+	 * Loaded modules will be returned as objects, while unloaded modules as configuration arrays.
+	 * @return array the modules (indexed by their IDs)
+	 */
+	public function getModules($loadedOnly = false)
+	{
+		if ($loadedOnly) {
+			$modules = [];
+			foreach ($this->_modules as $module) {
+				if ($module instanceof Module) {
+					$modules[] = $module;
+				}
+			}
+			return $modules;
+		} else {
+			return $this->_modules;
+		}
+	}
+
+	/**
+	 * Registers sub-modules in the current module.
+	 *
+	 * Each sub-module should be specified as a name-value pair, where
+	 * name refers to the ID of the module and value the module or a configuration
+	 * array that can be used to create the module. In the latter case, [[Yii::createObject()]]
+	 * will be used to create the module.
+	 *
+	 * If a new sub-module has the same ID as an existing one, the existing one will be overwritten silently.
+	 *
+	 * The following is an example for registering two sub-modules:
+	 *
+	 * ~~~
+	 * [
+	 *     'comment' => [
+	 *         'class' => 'app\modules\comment\CommentModule',
+	 *         'db' => 'db',
+	 *     ],
+	 *     'booking' => ['class' => 'app\modules\booking\BookingModule'],
+	 * ]
+	 * ~~~
+	 *
+	 * @param array $modules modules (id => module configuration or instances)
+	 */
+	public function setModules($modules)
+	{
+		foreach ($modules as $id => $module) {
+			$this->_modules[$id] = $module;
+		}
+	}
+
+	/**
+	 * Checks whether the named component exists.
+	 * @param string $id component ID
+	 * @return boolean whether the named component exists. Both loaded and unloaded components
+	 * are considered.
+	 */
+	public function hasComponent($id)
+	{
+		return isset($this->_components[$id]);
+	}
+
+	/**
+	 * Retrieves the named component.
+	 * @param string $id component ID (case-sensitive)
+	 * @param boolean $load whether to load the component if it is not yet loaded.
+	 * @return Component|null the component instance, null if the component does not exist.
+	 * @see hasComponent()
+	 */
+	public function getComponent($id, $load = true)
+	{
+		if (isset($this->_components[$id])) {
+			if ($this->_components[$id] instanceof Object) {
+				return $this->_components[$id];
+			} elseif ($load) {
+				return $this->_components[$id] = Yii::createObject($this->_components[$id]);
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Registers a component with this module.
+	 * @param string $id component ID
+	 * @param Component|array|null $component the component to be registered with the module. This can
+	 * be one of the followings:
+	 *
+	 * - a [[Component]] object
+	 * - a configuration array: when [[getComponent()]] is called initially for this component, the array
+	 *   will be used to instantiate the component via [[Yii::createObject()]].
+	 * - null: the named component will be removed from the module
+	 */
+	public function setComponent($id, $component)
+	{
+		if ($component === null) {
+			unset($this->_components[$id]);
+		} else {
+			$this->_components[$id] = $component;
+		}
+	}
+
+	/**
+	 * Returns the registered components.
+	 * @param boolean $loadedOnly whether to return the loaded components only. If this is set false,
+	 * then all components specified in the configuration will be returned, whether they are loaded or not.
+	 * Loaded components will be returned as objects, while unloaded components as configuration arrays.
+	 * @return array the components (indexed by their IDs)
+	 */
+	public function getComponents($loadedOnly = false)
+	{
+		if ($loadedOnly) {
+			$components = [];
+			foreach ($this->_components as $component) {
+				if ($component instanceof Component) {
+					$components[] = $component;
+				}
+			}
+			return $components;
+		} else {
+			return $this->_components;
+		}
+	}
+
+	/**
+	 * Registers a set of components in this module.
+	 *
+	 * Each component should be specified as a name-value pair, where
+	 * name refers to the ID of the component and value the component or a configuration
+	 * array that can be used to create the component. In the latter case, [[Yii::createObject()]]
+	 * will be used to create the component.
+	 *
+	 * If a new component has the same ID as an existing one, the existing one will be overwritten silently.
+	 *
+	 * The following is an example for setting two components:
+	 *
+	 * ~~~
+	 * [
+	 *     'db' => [
+	 *         'class' => 'yii\db\Connection',
+	 *         'dsn' => 'sqlite:path/to/file.db',
+	 *     ],
+	 *     'cache' => [
+	 *         'class' => 'yii\caching\DbCache',
+	 *         'db' => 'db',
+	 *     ],
+	 * ]
+	 * ~~~
+	 *
+	 * @param array $components components (id => component configuration or instance)
+	 */
+	public function setComponents($components)
+	{
+		foreach ($components as $id => $component) {
+			if (!is_object($component) && isset($this->_components[$id]['class']) && !isset($component['class'])) {
+				// set default component class
+				$component['class'] = $this->_components[$id]['class'];
+			}
+			$this->_components[$id] = $component;
+		}
+	}
+
+	/**
+	 * Loads components that are declared in [[preload]].
+	 * @throws InvalidConfigException if a component or module to be preloaded is unknown
+	 */
+	public function preloadComponents()
+	{
+		foreach ($this->preload as $id) {
+			if ($this->hasComponent($id)) {
+				$this->getComponent($id);
+			} elseif ($this->hasModule($id)) {
+				$this->getModule($id);
+			} else {
+				throw new InvalidConfigException("Unknown component or module: $id");
+			}
+		}
+	}
+
+	/**
+	 * Runs a controller action specified by a route.
+	 * This method parses the specified route and creates the corresponding child module(s), controller and action
+	 * instances. It then calls [[Controller::runAction()]] to run the action with the given parameters.
+	 * If the route is empty, the method will use [[defaultRoute]].
+	 * @param string $route the route that specifies the action.
+	 * @param array $params the parameters to be passed to the action
+	 * @return mixed the result of the action.
+	 * @throws InvalidRouteException if the requested route cannot be resolved into an action successfully
+	 */
+	public function runAction($route, $params = [])
+	{
+		$parts = $this->createController($route);
+		if (is_array($parts)) {
+			/** @var Controller $controller */
+			list($controller, $actionID) = $parts;
+			$oldController = Yii::$app->controller;
+			Yii::$app->controller = $controller;
+			$result = $controller->runAction($actionID, $params);
+			Yii::$app->controller = $oldController;
+			return $result;
+		} else {
+			$id = $this->getUniqueId();
+			throw new InvalidRouteException('Unable to resolve the request "' . ($id === '' ? $route : $id . '/' . $route) . '".');
+		}
+	}
+
+	/**
+	 * Creates a controller instance based on the controller ID.
+	 *
+	 * The controller is created within this module. The method first attempts to
+	 * create the controller based on the [[controllerMap]] of the module. If not available,
+	 * it will look for the controller class under the [[controllerPath]] and create an
+	 * instance of it.
+	 *
+	 * @param string $route the route consisting of module, controller and action IDs.
+	 * @return array|boolean If the controller is created successfully, it will be returned together
+	 * with the requested action ID. Otherwise false will be returned.
+	 * @throws InvalidConfigException if the controller class and its file do not match.
+	 */
+	public function createController($route)
+	{
+		if ($route === '') {
+			$route = $this->defaultRoute;
+		}
+		if (strpos($route, '/') !== false) {
+			list ($id, $route) = explode('/', $route, 2);
+		} else {
+			$id = $route;
+			$route = '';
+		}
+
+		$module = $this->getModule($id);
+		if ($module !== null) {
+			return $module->createController($route);
+		}
+
+		if (isset($this->controllerMap[$id])) {
+			$controller = Yii::createObject($this->controllerMap[$id], $id, $this);
+		} elseif (preg_match('/^[a-z0-9\\-_]+$/', $id) && strpos($id, '--') === false && trim($id, '-') === $id) {
+			$className = str_replace(' ', '', ucwords(str_replace('-', ' ', $id))) . 'Controller';
+			$classFile = $this->controllerPath . DIRECTORY_SEPARATOR . $className . '.php';
+			if (!is_file($classFile)) {
+				return false;
+			}
+			$className = ltrim($this->controllerNamespace . '\\' . $className, '\\');
+			Yii::$classMap[$className] = $classFile;
+			if (is_subclass_of($className, 'yii\base\Controller')) {
+				$controller = new $className($id, $this);
+			} elseif (YII_DEBUG) {
+				throw new InvalidConfigException("Controller class must extend from \\yii\\base\\Controller.");
+			}
+		}
+
+		return isset($controller) ? [$controller, $route] : false;
+	}
+
+	/**
+	 * This method is invoked right before an action of this module is to be executed (after all possible filters.)
+	 * You may override this method to do last-minute preparation for the action.
+	 * Make sure you call the parent implementation so that the relevant event is triggered.
+	 * @param Action $action the action to be executed.
+	 * @return boolean whether the action should continue to be executed.
+	 */
+	public function beforeAction($action)
+	{
+		return true;
+	}
+
+	/**
+	 * This method is invoked right after an action of this module has been executed.
+	 * You may override this method to do some postprocessing for the action.
+	 * Make sure you call the parent implementation so that the relevant event is triggered.
+	 * @param Action $action the action just executed.
+	 * @param mixed $result the action return result.
+	 */
+	public function afterAction($action, &$result)
+	{
+	}
+}

+ 25 - 0
php-yii2/app/vendor/yiisoft/yii2/base/NotSupportedException.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * NotSupportedException represents an exception caused by accessing features that are not supported.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class NotSupportedException extends Exception
+{
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return 'Not Supported';
+	}
+}

+ 240 - 0
php-yii2/app/vendor/yiisoft/yii2/base/Object.php

@@ -0,0 +1,240 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+use Yii;
+
+/**
+ * Object is the base class that implements the *property* feature.
+ *
+ * @include @yii/base/Object.md
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Object implements Arrayable
+{
+	/**
+	 * @return string the fully qualified name of this class.
+	 */
+	public static function className()
+	{
+		return get_called_class();
+	}
+
+	/**
+	 * Constructor.
+	 * The default implementation does two things:
+	 *
+	 * - Initializes the object with the given configuration `$config`.
+	 * - Call [[init()]].
+	 *
+	 * If this method is overridden in a child class, it is recommended that
+	 *
+	 * - the last parameter of the constructor is a configuration array, like `$config` here.
+	 * - call the parent implementation at the end of the constructor.
+	 *
+	 * @param array $config name-value pairs that will be used to initialize the object properties
+	 */
+	public function __construct($config = [])
+	{
+		if (!empty($config)) {
+			Yii::configure($this, $config);
+		}
+		$this->init();
+	}
+
+	/**
+	 * Initializes the object.
+	 * This method is invoked at the end of the constructor after the object is initialized with the
+	 * given configuration.
+	 */
+	public function init()
+	{
+	}
+
+	/**
+	 * Returns the value of an object property.
+	 *
+	 * Do not call this method directly as it is a PHP magic method that
+	 * will be implicitly called when executing `$value = $object->property;`.
+	 * @param string $name the property name
+	 * @return mixed the property value
+	 * @throws UnknownPropertyException if the property is not defined
+	 * @throws InvalidCallException if the property is write-only
+	 * @see __set()
+	 */
+	public function __get($name)
+	{
+		$getter = 'get' . $name;
+		if (method_exists($this, $getter)) {
+			return $this->$getter();
+		} elseif (method_exists($this, 'set' . $name)) {
+			throw new InvalidCallException('Getting write-only property: ' . get_class($this) . '::' . $name);
+		} else {
+			throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name);
+		}
+	}
+
+	/**
+	 * Sets value of an object property.
+	 *
+	 * Do not call this method directly as it is a PHP magic method that
+	 * will be implicitly called when executing `$object->property = $value;`.
+	 * @param string $name the property name or the event name
+	 * @param mixed $value the property value
+	 * @throws UnknownPropertyException if the property is not defined
+	 * @throws InvalidCallException if the property is read-only
+	 * @see __get()
+	 */
+	public function __set($name, $value)
+	{
+		$setter = 'set' . $name;
+		if (method_exists($this, $setter)) {
+			$this->$setter($value);
+		} elseif (method_exists($this, 'get' . $name)) {
+			throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name);
+		} else {
+			throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name);
+		}
+	}
+
+	/**
+	 * Checks if the named property is set (not null).
+	 *
+	 * Do not call this method directly as it is a PHP magic method that
+	 * will be implicitly called when executing `isset($object->property)`.
+	 *
+	 * Note that if the property is not defined, false will be returned.
+	 * @param string $name the property name or the event name
+	 * @return boolean whether the named property is set (not null).
+	 */
+	public function __isset($name)
+	{
+		$getter = 'get' . $name;
+		if (method_exists($this, $getter)) {
+			return $this->$getter() !== null;
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * Sets an object property to null.
+	 *
+	 * Do not call this method directly as it is a PHP magic method that
+	 * will be implicitly called when executing `unset($object->property)`.
+	 *
+	 * Note that if the property is not defined, this method will do nothing.
+	 * If the property is read-only, it will throw an exception.
+	 * @param string $name the property name
+	 * @throws InvalidCallException if the property is read only.
+	 */
+	public function __unset($name)
+	{
+		$setter = 'set' . $name;
+		if (method_exists($this, $setter)) {
+			$this->$setter(null);
+		} elseif (method_exists($this, 'get' . $name)) {
+			throw new InvalidCallException('Unsetting read-only property: ' . get_class($this) . '::' . $name);
+		}
+	}
+
+	/**
+	 * Calls the named method which is not a class method.
+	 *
+	 * Do not call this method directly as it is a PHP magic method that
+	 * will be implicitly called when an unknown method is being invoked.
+	 * @param string $name the method name
+	 * @param array $params method parameters
+	 * @throws UnknownMethodException when calling unknown method
+	 * @return mixed the method return value
+	 */
+	public function __call($name, $params)
+	{
+		throw new UnknownMethodException('Unknown method: ' . get_class($this) . "::$name()");
+	}
+
+	/**
+	 * Returns a value indicating whether a property is defined.
+	 * A property is defined if:
+	 *
+	 * - the class has a getter or setter method associated with the specified name
+	 *   (in this case, property name is case-insensitive);
+	 * - the class has a member variable with the specified name (when `$checkVars` is true);
+	 *
+	 * @param string $name the property name
+	 * @param boolean $checkVars whether to treat member variables as properties
+	 * @return boolean whether the property is defined
+	 * @see canGetProperty()
+	 * @see canSetProperty()
+	 */
+	public function hasProperty($name, $checkVars = true)
+	{
+		return $this->canGetProperty($name, $checkVars) || $this->canSetProperty($name, false);
+	}
+
+	/**
+	 * Returns a value indicating whether a property can be read.
+	 * A property is readable if:
+	 *
+	 * - the class has a getter method associated with the specified name
+	 *   (in this case, property name is case-insensitive);
+	 * - the class has a member variable with the specified name (when `$checkVars` is true);
+	 *
+	 * @param string $name the property name
+	 * @param boolean $checkVars whether to treat member variables as properties
+	 * @return boolean whether the property can be read
+	 * @see canSetProperty()
+	 */
+	public function canGetProperty($name, $checkVars = true)
+	{
+		return method_exists($this, 'get' . $name) || $checkVars && property_exists($this, $name);
+	}
+
+	/**
+	 * Returns a value indicating whether a property can be set.
+	 * A property is writable if:
+	 *
+	 * - the class has a setter method associated with the specified name
+	 *   (in this case, property name is case-insensitive);
+	 * - the class has a member variable with the specified name (when `$checkVars` is true);
+	 *
+	 * @param string $name the property name
+	 * @param boolean $checkVars whether to treat member variables as properties
+	 * @return boolean whether the property can be written
+	 * @see canGetProperty()
+	 */
+	public function canSetProperty($name, $checkVars = true)
+	{
+		return method_exists($this, 'set' . $name) || $checkVars && property_exists($this, $name);
+	}
+
+	/**
+	 * Returns a value indicating whether a method is defined.
+	 *
+	 * The default implementation is a call to php function `method_exists()`.
+	 * You may override this method when you implemented the php magic method `__call()`.
+	 * @param string $name the property name
+	 * @return boolean whether the property is defined
+	 */
+	public function hasMethod($name)
+	{
+		return method_exists($this, $name);
+	}
+
+	/**
+	 * Converts the object into an array.
+	 * The default implementation will return all public property values as an array.
+	 * @return array the array representation of the object
+	 */
+	public function toArray()
+	{
+		return Yii::getObjectVars($this);
+	}
+}

+ 82 - 0
php-yii2/app/vendor/yiisoft/yii2/base/Request.php

@@ -0,0 +1,82 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * Request represents a request that is handled by an [[Application]].
+ *
+ * @property boolean $isConsoleRequest The value indicating whether the current request is made via console.
+ * @property string $scriptFile Entry script file path (processed w/ realpath()).
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+abstract class Request extends Component
+{
+	private $_scriptFile;
+	private $_isConsoleRequest;
+
+	/**
+	 * Resolves the current request into a route and the associated parameters.
+	 * @return array the first element is the route, and the second is the associated parameters.
+	 */
+	abstract public function resolve();
+
+	/**
+	 * Returns a value indicating whether the current request is made via command line
+	 * @return boolean the value indicating whether the current request is made via console
+	 */
+	public function getIsConsoleRequest()
+	{
+		return $this->_isConsoleRequest !== null ? $this->_isConsoleRequest : PHP_SAPI === 'cli';
+	}
+
+	/**
+	 * Sets the value indicating whether the current request is made via command line
+	 * @param boolean $value the value indicating whether the current request is made via command line
+	 */
+	public function setIsConsoleRequest($value)
+	{
+		$this->_isConsoleRequest = $value;
+	}
+
+	/**
+	 * Returns entry script file path.
+	 * @return string entry script file path (processed w/ realpath())
+	 * @throws InvalidConfigException if the entry script file path cannot be determined automatically.
+	 */
+	public function getScriptFile()
+	{
+		if ($this->_scriptFile === null) {
+			if (isset($_SERVER['SCRIPT_FILENAME'])) {
+				$this->setScriptFile($_SERVER['SCRIPT_FILENAME']);
+			} else {
+				throw new InvalidConfigException('Unable to determine the entry script file path.');
+			}
+		}
+		return $this->_scriptFile;
+	}
+
+	/**
+	 * Sets the entry script file path.
+	 * The entry script file path can normally be determined based on the `SCRIPT_FILENAME` SERVER variable.
+	 * However, for some server configurations, this may not be correct or feasible.
+	 * This setter is provided so that the entry script file path can be manually specified.
+	 * @param string $value the entry script file path. This can be either a file path or a path alias.
+	 * @throws InvalidConfigException if the provided entry script file path is invalid.
+	 */
+	public function setScriptFile($value)
+	{
+		$scriptFile = realpath(\Yii::getAlias($value));
+		if ($scriptFile !== false && is_file($scriptFile)) {
+			$this->_scriptFile = $scriptFile;
+		} else {
+			throw new InvalidConfigException('Unable to determine the entry script file path.');
+		}
+	}
+}

+ 30 - 0
php-yii2/app/vendor/yiisoft/yii2/base/Response.php

@@ -0,0 +1,30 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * Response represents the response of an [[Application]] to a [[Request]].
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Response extends Component
+{
+	/**
+	 * @var integer the exit status. Exit statuses should be in the range 0 to 254.
+	 * The status 0 means the program terminates successfully.
+	 */
+	public $exitStatus = 0;
+
+	/**
+	 * Sends the response to client.
+	 */
+	public function send()
+	{
+	}
+}

+ 152 - 0
php-yii2/app/vendor/yiisoft/yii2/base/Theme.php

@@ -0,0 +1,152 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+use Yii;
+use yii\helpers\FileHelper;
+
+/**
+ * Theme represents an application theme.
+ *
+ * When [[View]] renders a view file, it will check the [[Application::theme|active theme]]
+ * to see if there is a themed version of the view file exists. If so, the themed version will be rendered instead.
+ *
+ * A theme is a directory consisting of view files which are meant to replace their non-themed counterparts.
+ *
+ * Theme uses [[pathMap]] to achieve the view file replacement:
+ *
+ * 1. It first looks for a key in [[pathMap]] that is a substring of the given view file path;
+ * 2. If such a key exists, the corresponding value will be used to replace the corresponding part
+ *    in the view file path;
+ * 3. It will then check if the updated view file exists or not. If so, that file will be used
+ *    to replace the original view file.
+ * 4. If Step 2 or 3 fails, the original view file will be used.
+ *
+ * For example, if [[pathMap]] is `['/web/views' => '/web/themes/basic']`,
+ * then the themed version for a view file `/web/views/site/index.php` will be
+ * `/web/themes/basic/site/index.php`.
+ *
+ * It is possible to map a single path to multiple paths. For example,
+ *
+ * ~~~
+ * 'pathMap' => [
+ *     '/web/views' => [
+ *         '/web/themes/christmas',
+ *         '/web/themes/basic',
+ *     ],
+ * ]
+ * ~~~
+ *
+ * In this case, the themed version could be either `/web/themes/christmas/site/index.php` or
+ * `/web/themes/basic/site/index.php`. The former has precedence over the latter if both files exist.
+ *
+ * To use a theme, you should configure the [[View::theme|theme]] property of the "view" application
+ * component like the following:
+ *
+ * ~~~
+ * 'view' => [
+ *     'theme' => [
+ *         'basePath' => '@webroot/themes/basic',
+ *         'baseUrl' => '@web/themes/basic',
+ *     ],
+ * ],
+ * ~~~
+ *
+ * The above configuration specifies a theme located under the "themes/basic" directory of the Web folder
+ * that contains the entry script of the application. If your theme is designed to handle modules,
+ * you may configure the [[pathMap]] property like described above.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Theme extends Component
+{
+	/**
+	 * @var string the root path or path alias of this theme. All resources of this theme are located
+	 * under this directory. This property must be set if [[pathMap]] is not set.
+	 * @see pathMap
+	 */
+	public $basePath;
+	/**
+	 * @var string the base URL (or path alias) for this theme. All resources of this theme are considered
+	 * to be under this base URL. This property must be set. It is mainly used by [[getUrl()]].
+	 */
+	public $baseUrl;
+	/**
+	 * @var array the mapping between view directories and their corresponding themed versions.
+	 * If not set, it will be initialized as a mapping from [[Application::basePath]] to [[basePath]].
+	 * This property is used by [[applyTo()]] when a view is trying to apply the theme.
+	 * Path aliases can be used when specifying directories.
+	 */
+	public $pathMap;
+
+
+	/**
+	 * Initializes the theme.
+	 * @throws InvalidConfigException if [[basePath]] is not set.
+	 */
+	public function init()
+	{
+		parent::init();
+		if (empty($this->pathMap)) {
+			if ($this->basePath !== null) {
+				$this->basePath = Yii::getAlias($this->basePath);
+				$this->pathMap = [Yii::$app->getBasePath() => [$this->basePath]];
+			} else {
+				throw new InvalidConfigException('The "basePath" property must be set.');
+			}
+		}
+		$paths = [];
+		foreach ($this->pathMap as $from => $tos) {
+			$from = FileHelper::normalizePath(Yii::getAlias($from));
+			foreach ((array)$tos as $to) {
+				$to = FileHelper::normalizePath(Yii::getAlias($to));
+				$paths[$from . DIRECTORY_SEPARATOR][] = $to . DIRECTORY_SEPARATOR;
+			}
+		}
+		$this->pathMap = $paths;
+		if ($this->baseUrl === null) {
+			throw new InvalidConfigException('The "baseUrl" property must be set.');
+		} else {
+			$this->baseUrl = rtrim(Yii::getAlias($this->baseUrl), '/');
+		}
+	}
+
+	/**
+	 * Converts a file to a themed file if possible.
+	 * If there is no corresponding themed file, the original file will be returned.
+	 * @param string $path the file to be themed
+	 * @return string the themed file, or the original file if the themed version is not available.
+	 */
+	public function applyTo($path)
+	{
+		$path = FileHelper::normalizePath($path);
+		foreach ($this->pathMap as $from => $tos) {
+			if (strpos($path, $from) === 0) {
+				$n = strlen($from);
+				foreach ($tos as $to) {
+					$file = $to . substr($path, $n);
+					if (is_file($file)) {
+						return $file;
+					}
+				}
+			}
+		}
+		return $path;
+	}
+
+	/**
+	 * Converts a relative URL into an absolute URL using [[baseUrl]].
+	 * @param string $url the relative URL to be converted.
+	 * @return string the absolute URL
+	 */
+	public function getUrl($url)
+	{
+		return $this->baseUrl . '/' . ltrim($url, '/');
+	}
+}

+ 25 - 0
php-yii2/app/vendor/yiisoft/yii2/base/UnknownClassException.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * UnknownClassException represents an exception caused by using an unknown class.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class UnknownClassException extends Exception
+{
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return 'Unknown Class';
+	}
+}

+ 25 - 0
php-yii2/app/vendor/yiisoft/yii2/base/UnknownMethodException.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * UnknownMethodException represents an exception caused by accessing an unknown object method.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class UnknownMethodException extends Exception
+{
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return 'Unknown Method';
+	}
+}

+ 25 - 0
php-yii2/app/vendor/yiisoft/yii2/base/UnknownPropertyException.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * UnknownPropertyException represents an exception caused by accessing unknown object properties.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class UnknownPropertyException extends Exception
+{
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return 'Unknown Property';
+	}
+}

+ 19 - 0
php-yii2/app/vendor/yiisoft/yii2/base/UserException.php

@@ -0,0 +1,19 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * UserException is the base class for exceptions that are meant to be shown to end users.
+ * Such exceptions are often caused by mistakes of end users.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class UserException extends Exception
+{
+}

+ 461 - 0
php-yii2/app/vendor/yiisoft/yii2/base/View.php

@@ -0,0 +1,461 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+use Yii;
+use yii\helpers\FileHelper;
+use yii\widgets\Block;
+use yii\widgets\ContentDecorator;
+use yii\widgets\FragmentCache;
+
+/**
+ * View represents a view object in the MVC pattern.
+ *
+ * View provides a set of methods (e.g. [[render()]]) for rendering purpose.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class View extends Component
+{
+	/**
+	 * @event Event an event that is triggered by [[beginPage()]].
+	 */
+	const EVENT_BEGIN_PAGE = 'beginPage';
+	/**
+	 * @event Event an event that is triggered by [[endPage()]].
+	 */
+	const EVENT_END_PAGE = 'endPage';
+	/**
+	 * @event ViewEvent an event that is triggered by [[renderFile()]] right before it renders a view file.
+	 */
+	const EVENT_BEFORE_RENDER = 'beforeRender';
+	/**
+	 * @event ViewEvent an event that is triggered by [[renderFile()]] right after it renders a view file.
+	 */
+	const EVENT_AFTER_RENDER = 'afterRender';
+
+	/**
+	 * @var ViewContextInterface the context under which the [[renderFile()]] method is being invoked.
+	 */
+	public $context;
+	/**
+	 * @var mixed custom parameters that are shared among view templates.
+	 */
+	public $params = [];
+	/**
+	 * @var array a list of available renderers indexed by their corresponding supported file extensions.
+	 * Each renderer may be a view renderer object or the configuration for creating the renderer object.
+	 * For example, the following configuration enables both Smarty and Twig view renderers:
+	 *
+	 * ~~~
+	 * [
+	 *     'tpl' => ['class' => 'yii\smarty\ViewRenderer'],
+	 *     'twig' => ['class' => 'yii\twig\ViewRenderer'],
+	 * ]
+	 * ~~~
+	 *
+	 * If no renderer is available for the given view file, the view file will be treated as a normal PHP
+	 * and rendered via [[renderPhpFile()]].
+	 */
+	public $renderers;
+	/**
+	 * @var string the default view file extension. This will be appended to view file names if they don't have file extensions.
+	 */
+	public $defaultExtension = 'php';
+	/**
+	 * @var Theme|array the theme object or the configuration array for creating the theme object.
+	 * If not set, it means theming is not enabled.
+	 */
+	public $theme;
+	/**
+	 * @var array a list of named output blocks. The keys are the block names and the values
+	 * are the corresponding block content. You can call [[beginBlock()]] and [[endBlock()]]
+	 * to capture small fragments of a view. They can be later accessed somewhere else
+	 * through this property.
+	 */
+	public $blocks;
+	/**
+	 * @var array a list of currently active fragment cache widgets. This property
+	 * is used internally to implement the content caching feature. Do not modify it directly.
+	 * @internal
+	 */
+	public $cacheStack = [];
+	/**
+	 * @var array a list of placeholders for embedding dynamic contents. This property
+	 * is used internally to implement the content caching feature. Do not modify it directly.
+	 * @internal
+	 */
+	public $dynamicPlaceholders = [];
+
+
+	/**
+	 * Initializes the view component.
+	 */
+	public function init()
+	{
+		parent::init();
+		if (is_array($this->theme)) {
+			if (!isset($this->theme['class'])) {
+				$this->theme['class'] = 'yii\base\Theme';
+			}
+			$this->theme = Yii::createObject($this->theme);
+		}
+	}
+
+	/**
+	 * Renders a view.
+	 *
+	 * The view to be rendered can be specified in one of the following formats:
+	 *
+	 * - path alias (e.g. "@app/views/site/index");
+	 * - absolute path within application (e.g. "//site/index"): the view name starts with double slashes.
+	 *   The actual view file will be looked for under the [[Application::viewPath|view path]] of the application.
+	 * - absolute path within current module (e.g. "/site/index"): the view name starts with a single slash.
+	 *   The actual view file will be looked for under the [[Module::viewPath|view path]] of [[module]].
+	 * - resolving any other format will be performed via [[ViewContext::findViewFile()]].
+	 *
+	 * @param string $view the view name. Please refer to [[Controller::findViewFile()]]
+	 * and [[Widget::findViewFile()]] on how to specify this parameter.
+	 * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.
+	 * @param object $context the context that the view should use for rendering the view. If null,
+	 * existing [[context]] will be used.
+	 * @return string the rendering result
+	 * @throws InvalidParamException if the view cannot be resolved or the view file does not exist.
+	 * @see renderFile()
+	 */
+	public function render($view, $params = [], $context = null)
+	{
+		$viewFile = $this->findViewFile($view, $context);
+		return $this->renderFile($viewFile, $params, $context);
+	}
+
+	/**
+	 * Finds the view file based on the given view name.
+	 * @param string $view the view name or the path alias of the view file. Please refer to [[render()]]
+	 * on how to specify this parameter.
+	 * @param object $context the context that the view should be used to search the view file. If null,
+	 * existing [[context]] will be used.
+	 * @return string the view file path. Note that the file may not exist.
+	 * @throws InvalidCallException if [[context]] is required and invalid.
+	 */
+	protected function findViewFile($view, $context = null)
+	{
+		if (strncmp($view, '@', 1) === 0) {
+			// e.g. "@app/views/main"
+			$file = Yii::getAlias($view);
+		} elseif (strncmp($view, '//', 2) === 0) {
+			// e.g. "//layouts/main"
+			$file = Yii::$app->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
+		} elseif (strncmp($view, '/', 1) === 0) {
+			// e.g. "/site/index"
+			if (Yii::$app->controller !== null) {
+				$file = Yii::$app->controller->module->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
+			} else {
+				throw new InvalidCallException("Unable to locate view file for view '$view': no active controller.");
+			}
+		} else {
+			// context required
+			if ($context === null) {
+				$context = $this->context;
+			}
+			if ($context instanceof ViewContextInterface) {
+				$file = $context->findViewFile($view);
+			} else {
+				throw new InvalidCallException("Unable to locate view file for view '$view': no active view context.");
+			}
+		}
+
+		if (pathinfo($file, PATHINFO_EXTENSION) !== '') {
+			return $file;
+		}
+		$path = $file . '.' . $this->defaultExtension;
+		if ($this->defaultExtension !== 'php' && !is_file($path)) {
+			$path = $file . '.php';
+		}
+		return $path;
+	}
+
+	/**
+	 * Renders a view file.
+	 *
+	 * If [[theme]] is enabled (not null), it will try to render the themed version of the view file as long
+	 * as it is available.
+	 *
+	 * The method will call [[FileHelper::localize()]] to localize the view file.
+	 *
+	 * If [[renderer]] is enabled (not null), the method will use it to render the view file.
+	 * Otherwise, it will simply include the view file as a normal PHP file, capture its output and
+	 * return it as a string.
+	 *
+	 * @param string $viewFile the view file. This can be either a file path or a path alias.
+	 * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.
+	 * @param object $context the context that the view should use for rendering the view. If null,
+	 * existing [[context]] will be used.
+	 * @return string the rendering result
+	 * @throws InvalidParamException if the view file does not exist
+	 */
+	public function renderFile($viewFile, $params = [], $context = null)
+	{
+		$viewFile = Yii::getAlias($viewFile);
+		if ($this->theme !== null) {
+			$viewFile = $this->theme->applyTo($viewFile);
+		}
+		if (is_file($viewFile)) {
+			$viewFile = FileHelper::localize($viewFile);
+		} else {
+			throw new InvalidParamException("The view file does not exist: $viewFile");
+		}
+
+		$oldContext = $this->context;
+		if ($context !== null) {
+			$this->context = $context;
+		}
+
+		$output = '';
+		if ($this->beforeRender($viewFile)) {
+			Yii::trace("Rendering view file: $viewFile", __METHOD__);
+			$ext = pathinfo($viewFile, PATHINFO_EXTENSION);
+			if (isset($this->renderers[$ext])) {
+				if (is_array($this->renderers[$ext]) || is_string($this->renderers[$ext])) {
+					$this->renderers[$ext] = Yii::createObject($this->renderers[$ext]);
+				}
+				/** @var ViewRenderer $renderer */
+				$renderer = $this->renderers[$ext];
+				$output = $renderer->render($this, $viewFile, $params);
+			} else {
+				$output = $this->renderPhpFile($viewFile, $params);
+			}
+			$this->afterRender($viewFile, $output);
+		}
+
+		$this->context = $oldContext;
+
+		return $output;
+	}
+
+	/**
+	 * This method is invoked right before [[renderFile()]] renders a view file.
+	 * The default implementation will trigger the [[EVENT_BEFORE_RENDER]] event.
+	 * If you override this method, make sure you call the parent implementation first.
+	 * @param string $viewFile the view file to be rendered
+	 * @return boolean whether to continue rendering the view file.
+	 */
+	public function beforeRender($viewFile)
+	{
+		$event = new ViewEvent($viewFile);
+		$this->trigger(self::EVENT_BEFORE_RENDER, $event);
+		return $event->isValid;
+	}
+
+	/**
+	 * This method is invoked right after [[renderFile()]] renders a view file.
+	 * The default implementation will trigger the [[EVENT_AFTER_RENDER]] event.
+	 * If you override this method, make sure you call the parent implementation first.
+	 * @param string $viewFile the view file to be rendered
+	 * @param string $output the rendering result of the view file. Updates to this parameter
+	 * will be passed back and returned by [[renderFile()]].
+	 */
+	public function afterRender($viewFile, &$output)
+	{
+		if ($this->hasEventHandlers(self::EVENT_AFTER_RENDER)) {
+			$event = new ViewEvent($viewFile);
+			$event->output = $output;
+			$this->trigger(self::EVENT_AFTER_RENDER, $event);
+			$output = $event->output;
+		}
+	}
+
+	/**
+	 * Renders a view file as a PHP script.
+	 *
+	 * This method treats the view file as a PHP script and includes the file.
+	 * It extracts the given parameters and makes them available in the view file.
+	 * The method captures the output of the included view file and returns it as a string.
+	 *
+	 * This method should mainly be called by view renderer or [[renderFile()]].
+	 *
+	 * @param string $_file_ the view file.
+	 * @param array $_params_ the parameters (name-value pairs) that will be extracted and made available in the view file.
+	 * @return string the rendering result
+	 */
+	public function renderPhpFile($_file_, $_params_ = [])
+	{
+		ob_start();
+		ob_implicit_flush(false);
+		extract($_params_, EXTR_OVERWRITE);
+		require($_file_);
+		return ob_get_clean();
+	}
+
+	/**
+	 * Renders dynamic content returned by the given PHP statements.
+	 * This method is mainly used together with content caching (fragment caching and page caching)
+	 * when some portions of the content (called *dynamic content*) should not be cached.
+	 * The dynamic content must be returned by some PHP statements.
+	 * @param string $statements the PHP statements for generating the dynamic content.
+	 * @return string the placeholder of the dynamic content, or the dynamic content if there is no
+	 * active content cache currently.
+	 */
+	public function renderDynamic($statements)
+	{
+		if (!empty($this->cacheStack)) {
+			$n = count($this->dynamicPlaceholders);
+			$placeholder = "<![CDATA[YII-DYNAMIC-$n]]>";
+			$this->addDynamicPlaceholder($placeholder, $statements);
+			return $placeholder;
+		} else {
+			return $this->evaluateDynamicContent($statements);
+		}
+	}
+
+	/**
+	 * Adds a placeholder for dynamic content.
+	 * This method is internally used.
+	 * @param string $placeholder the placeholder name
+	 * @param string $statements the PHP statements for generating the dynamic content
+	 */
+	public function addDynamicPlaceholder($placeholder, $statements)
+	{
+		foreach ($this->cacheStack as $cache) {
+			$cache->dynamicPlaceholders[$placeholder] = $statements;
+		}
+		$this->dynamicPlaceholders[$placeholder] = $statements;
+	}
+
+	/**
+	 * Evaluates the given PHP statements.
+	 * This method is mainly used internally to implement dynamic content feature.
+	 * @param string $statements the PHP statements to be evaluated.
+	 * @return mixed the return value of the PHP statements.
+	 */
+	public function evaluateDynamicContent($statements)
+	{
+		return eval($statements);
+	}
+
+	/**
+	 * Begins recording a block.
+	 * This method is a shortcut to beginning [[Block]]
+	 * @param string $id the block ID.
+	 * @param boolean $renderInPlace whether to render the block content in place.
+	 * Defaults to false, meaning the captured block will not be displayed.
+	 * @return Block the Block widget instance
+	 */
+	public function beginBlock($id, $renderInPlace = false)
+	{
+		return Block::begin([
+			'id' => $id,
+			'renderInPlace' => $renderInPlace,
+			'view' => $this,
+		]);
+	}
+
+	/**
+	 * Ends recording a block.
+	 */
+	public function endBlock()
+	{
+		Block::end();
+	}
+
+	/**
+	 * Begins the rendering of content that is to be decorated by the specified view.
+	 * This method can be used to implement nested layout. For example, a layout can be embedded
+	 * in another layout file specified as '@app/views/layouts/base.php' like the following:
+	 *
+	 * ~~~
+	 * <?php $this->beginContent('@app/views/layouts/base.php'); ?>
+	 * ...layout content here...
+	 * <?php $this->endContent(); ?>
+	 * ~~~
+	 *
+	 * @param string $viewFile the view file that will be used to decorate the content enclosed by this widget.
+	 * This can be specified as either the view file path or path alias.
+	 * @param array $params the variables (name => value) to be extracted and made available in the decorative view.
+	 * @return ContentDecorator the ContentDecorator widget instance
+	 * @see ContentDecorator
+	 */
+	public function beginContent($viewFile, $params = [])
+	{
+		return ContentDecorator::begin([
+			'viewFile' => $viewFile,
+			'params' => $params,
+			'view' => $this,
+		]);
+	}
+
+	/**
+	 * Ends the rendering of content.
+	 */
+	public function endContent()
+	{
+		ContentDecorator::end();
+	}
+
+	/**
+	 * Begins fragment caching.
+	 * This method will display cached content if it is available.
+	 * If not, it will start caching and would expect an [[endCache()]]
+	 * call to end the cache and save the content into cache.
+	 * A typical usage of fragment caching is as follows,
+	 *
+	 * ~~~
+	 * if ($this->beginCache($id)) {
+	 *     // ...generate content here
+	 *     $this->endCache();
+	 * }
+	 * ~~~
+	 *
+	 * @param string $id a unique ID identifying the fragment to be cached.
+	 * @param array $properties initial property values for [[FragmentCache]]
+	 * @return boolean whether you should generate the content for caching.
+	 * False if the cached version is available.
+	 */
+	public function beginCache($id, $properties = [])
+	{
+		$properties['id'] = $id;
+		$properties['view'] = $this;
+		/** @var FragmentCache $cache */
+		$cache = FragmentCache::begin($properties);
+		if ($cache->getCachedContent() !== false) {
+			$this->endCache();
+			return false;
+		} else {
+			return true;
+		}
+	}
+
+	/**
+	 * Ends fragment caching.
+	 */
+	public function endCache()
+	{
+		FragmentCache::end();
+	}
+
+	/**
+	 * Marks the beginning of a page.
+	 */
+	public function beginPage()
+	{
+		ob_start();
+		ob_implicit_flush(false);
+
+		$this->trigger(self::EVENT_BEGIN_PAGE);
+	}
+
+	/**
+	 * Marks the ending of a page.
+	 */
+	public function endPage()
+	{
+		$this->trigger(self::EVENT_END_PAGE);
+		ob_end_flush();
+	}
+}

+ 26 - 0
php-yii2/app/vendor/yiisoft/yii2/base/ViewContextInterface.php

@@ -0,0 +1,26 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * ViewContextInterface is the interface that should implemented by classes who want to support relative view names.
+ *
+ * The method [[findViewFile()]] should be implemented to convert a relative view name into a file path.
+ *
+ * @author Paul Klimov <[email protected]>
+ * @since 2.0
+ */
+interface ViewContextInterface
+{
+	/**
+	 * Finds the view file corresponding to the specified relative view name.
+	 * @param string $view a relative view name. The name does NOT start with a slash.
+	 * @return string the view file path. Note that the file may not exist.
+	 */
+	public function findViewFile($view);
+}

+ 46 - 0
php-yii2/app/vendor/yiisoft/yii2/base/ViewEvent.php

@@ -0,0 +1,46 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * ViewEvent represents events triggered by the [[View]] component.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class ViewEvent extends Event
+{
+	/**
+	 * @var string the rendering result of [[View::renderFile()]].
+	 * Event handlers may modify this property and the modified output will be
+	 * returned by [[View::renderFile()]]. This property is only used
+	 * by [[View::EVENT_AFTER_RENDER]] event.
+	 */
+	public $output;
+	/**
+	 * @var string the view file path that is being rendered by [[View::renderFile()]].
+	 */
+	public $viewFile;
+	/**
+	 * @var boolean whether to continue rendering the view file. Event handlers of
+	 * [[View::EVENT_BEFORE_RENDER]] may set this property to decide whether
+	 * to continue rendering the current view file.
+	 */
+	public $isValid = true;
+
+	/**
+	 * Constructor.
+	 * @param string $viewFile the view file path that is being rendered by [[View::renderFile()]].
+	 * @param array $config name-value pairs that will be used to initialize the object properties
+	 */
+	public function __construct($viewFile, $config = [])
+	{
+		$this->viewFile = $viewFile;
+		parent::__construct($config);
+	}
+}

+ 30 - 0
php-yii2/app/vendor/yiisoft/yii2/base/ViewRenderer.php

@@ -0,0 +1,30 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * ViewRenderer is the base class for view renderer classes.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+abstract class ViewRenderer extends Component
+{
+	/**
+	 * Renders a view file.
+	 *
+	 * This method is invoked by [[View]] whenever it tries to render a view.
+	 * Child classes must implement this method to render the given view file.
+	 *
+	 * @param View $view the view object used for rendering the file.
+	 * @param string $file the view file.
+	 * @param array $params the parameters to be passed to the view file.
+	 * @return string the rendering result
+	 */
+	abstract public function render($view, $file, $params);
+}

+ 214 - 0
php-yii2/app/vendor/yiisoft/yii2/base/Widget.php

@@ -0,0 +1,214 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+use Yii;
+use ReflectionClass;
+
+/**
+ * Widget is the base class for widgets.
+ *
+ * @property string $id ID of the widget.
+ * @property \yii\web\View $view The view object that can be used to render views or view files. Note that the
+ * type of this property differs in getter and setter. See [[getView()]] and [[setView()]] for details.
+ * @property string $viewPath The directory containing the view files for this widget. This property is
+ * read-only.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Widget extends Component implements ViewContextInterface
+{
+	/**
+	 * @var integer a counter used to generate [[id]] for widgets.
+	 * @internal
+	 */
+	public static $counter = 0;
+	/**
+	 * @var string the prefix to the automatically generated widget IDs.
+	 * @see [[getId()]]
+	 */
+	public static $autoIdPrefix = 'w';
+
+	/**
+	 * @var Widget[] the widgets that are currently being rendered (not ended). This property
+	 * is maintained by [[begin()]] and [[end()]] methods.
+	 * @internal
+	 */
+	public static $stack = [];
+
+	
+	/**
+	 * Begins a widget.
+	 * This method creates an instance of the calling class. It will apply the configuration
+	 * to the created instance. A matching [[end()]] call should be called later.
+	 * @param array $config name-value pairs that will be used to initialize the object properties
+	 * @return static the newly created widget instance
+	 */
+	public static function begin($config = [])
+	{
+		$config['class'] = get_called_class();
+		/** @var Widget $widget */
+		$widget = Yii::createObject($config);
+		self::$stack[] = $widget;
+		return $widget;
+	}
+
+	/**
+	 * Ends a widget.
+	 * Note that the rendering result of the widget is directly echoed out.
+	 * @return static the widget instance that is ended.
+	 * @throws InvalidCallException if [[begin()]] and [[end()]] calls are not properly nested
+	 */
+	public static function end()
+	{
+		if (!empty(self::$stack)) {
+			$widget = array_pop(self::$stack);
+			if (get_class($widget) === get_called_class()) {
+				$widget->run();
+				return $widget;
+			} else {
+				throw new InvalidCallException("Expecting end() of " . get_class($widget) . ", found " . get_called_class());
+			}
+		} else {
+			throw new InvalidCallException("Unexpected " . get_called_class() . '::end() call. A matching begin() is not found.');
+		}
+	}
+
+	/**
+	 * Creates a widget instance and runs it.
+	 * The widget rendering result is returned by this method.
+	 * @param array $config name-value pairs that will be used to initialize the object properties
+	 * @return string the rendering result of the widget.
+	 */
+	public static function widget($config = [])
+	{
+		ob_start();
+		ob_implicit_flush(false);
+		/** @var Widget $widget */
+		$config['class'] = get_called_class();
+		$widget = Yii::createObject($config);
+		$widget->run();
+		return ob_get_clean();
+	}
+
+	private $_id;
+	
+	/**
+	 * Returns the ID of the widget.
+	 * @param boolean $autoGenerate whether to generate an ID if it is not set previously
+	 * @return string ID of the widget.
+	 */
+	public function getId($autoGenerate = true)
+	{
+		if ($autoGenerate && $this->_id === null) {
+			$this->_id = self::$autoIdPrefix . self::$counter++;
+		}
+		return $this->_id;
+	}
+
+	/**
+	 * Sets the ID of the widget.
+	 * @param string $value id of the widget.
+	 */
+	public function setId($value)
+	{
+		$this->_id = $value;
+	}
+
+	private $_view;
+	
+	/**
+	 * Returns the view object that can be used to render views or view files.
+	 * The [[render()]] and [[renderFile()]] methods will use
+	 * this view object to implement the actual view rendering.
+	 * If not set, it will default to the "view" application component.
+	 * @return \yii\web\View the view object that can be used to render views or view files.
+	 */
+	public function getView()
+	{
+		if ($this->_view === null) {
+			$this->_view = Yii::$app->getView();
+		}
+		return $this->_view;
+	}
+
+	/**
+	 * Sets the view object to be used by this widget.
+	 * @param View $view the view object that can be used to render views or view files.
+	 */
+	public function setView($view)
+	{
+		$this->_view = $view;
+	}
+
+	/**
+	 * Executes the widget.
+	 */
+	public function run()
+	{
+	}
+
+	/**
+	 * Renders a view.
+	 * The view to be rendered can be specified in one of the following formats:
+	 *
+	 * - path alias (e.g. "@app/views/site/index");
+	 * - absolute path within application (e.g. "//site/index"): the view name starts with double slashes.
+	 *   The actual view file will be looked for under the [[Application::viewPath|view path]] of the application.
+	 * - absolute path within module (e.g. "/site/index"): the view name starts with a single slash.
+	 *   The actual view file will be looked for under the [[Module::viewPath|view path]] of the currently
+	 *   active module.
+	 * - relative path (e.g. "index"): the actual view file will be looked for under [[viewPath]].
+	 *
+	 * If the view name does not contain a file extension, it will use the default one `.php`.
+
+	 * @param string $view the view name. Please refer to [[findViewFile()]] on how to specify a view name.
+	 * @param array $params the parameters (name-value pairs) that should be made available in the view.
+	 * @return string the rendering result.
+	 * @throws InvalidParamException if the view file does not exist.
+	 */
+	public function render($view, $params = [])
+	{
+		return $this->getView()->render($view, $params, $this);
+	}
+
+	/**
+	 * Renders a view file.
+	 * @param string $file the view file to be rendered. This can be either a file path or a path alias.
+	 * @param array $params the parameters (name-value pairs) that should be made available in the view.
+	 * @return string the rendering result.
+	 * @throws InvalidParamException if the view file does not exist.
+	 */
+	public function renderFile($file, $params = [])
+	{
+		return $this->getView()->renderFile($file, $params, $this);
+	}
+
+	/**
+	 * Returns the directory containing the view files for this widget.
+	 * The default implementation returns the 'views' subdirectory under the directory containing the widget class file.
+	 * @return string the directory containing the view files for this widget.
+	 */
+	public function getViewPath()
+	{
+		$class = new ReflectionClass($this);
+		return dirname($class->getFileName()) . DIRECTORY_SEPARATOR . 'views';
+	}
+
+	/**
+	 * Finds the view file based on the given view name.
+	 * File will be searched under [[viewPath]] directory.
+	 * @param string $view the view name.
+	 * @return string the view file path. Note that the file may not exist.
+	 */
+	public function findViewFile($view)
+	{
+		return $this->getViewPath() . DIRECTORY_SEPARATOR . $view;
+	}
+}

+ 116 - 0
php-yii2/app/vendor/yiisoft/yii2/behaviors/AutoTimestamp.php

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

+ 133 - 0
php-yii2/app/vendor/yiisoft/yii2/caching/ApcCache.php

@@ -0,0 +1,133 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\caching;
+
+/**
+ * ApcCache provides APC caching in terms of an application component.
+ *
+ * To use this application component, the [APC PHP extension](http://www.php.net/apc) must be loaded.
+ * In order to enable APC for CLI you should add "apc.enable_cli = 1" to your php.ini.
+ *
+ * See [[Cache]] for common cache operations that ApcCache supports.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class ApcCache extends Cache
+{
+	/**
+	 * Checks whether a specified key exists in the cache.
+	 * This can be faster than getting the value from the cache if the data is big.
+	 * Note that this method does not check whether the dependency associated
+	 * with the cached data, if there is any, has changed. So a call to [[get]]
+	 * may return false while exists returns true.
+	 * @param mixed $key a key identifying the cached value. This can be a simple string or
+	 * a complex data structure consisting of factors representing the key.
+	 * @return boolean true if a value exists in cache, false if the value is not in the cache or expired.
+	 */
+	public function exists($key)
+	{
+		$key = $this->buildKey($key);
+		return apc_exists($key);
+	}
+
+	/**
+	 * Retrieves a value from cache with a specified key.
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key a unique key identifying the cached value
+	 * @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
+	 */
+	protected function getValue($key)
+	{
+		return apc_fetch($key);
+	}
+
+	/**
+	 * Retrieves multiple values from cache with the specified keys.
+	 * @param array $keys a list of keys identifying the cached values
+	 * @return array a list of cached values indexed by the keys
+	 */
+	protected function getValues($keys)
+	{
+		return apc_fetch($keys);
+	}
+
+	/**
+	 * Stores a value identified by a key in cache.
+	 * This is the implementation of the method declared in the parent class.
+	 *
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	protected function setValue($key, $value, $expire)
+	{
+		return apc_store($key, $value, $expire);
+	}
+
+	/**
+	 * Stores multiple key-value pairs in cache.
+	 * @param array $data array where key corresponds to cache key while value
+	 * @param integer $expire the number of seconds in which the cached values will expire. 0 means never expire.
+	 * @return array array of failed keys
+	 */
+	protected function setValues($data, $expire)
+	{
+		return array_keys(apc_store($data, null, $expire));
+	}
+
+	/**
+	 * Stores a value identified by a key into cache if the cache does not contain this key.
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	protected function addValue($key, $value, $expire)
+	{
+		return apc_add($key, $value, $expire);
+	}
+
+	/**
+	 * Adds multiple key-value pairs to cache.
+	 * @param array $data array where key corresponds to cache key while value is the value stored
+	 * @param integer $expire the number of seconds in which the cached values will expire. 0 means never expire.
+	 * @return array array of failed keys
+	 */
+	protected function addValues($data, $expire)
+	{
+		return array_keys(apc_add($data, null, $expire));
+	}
+
+	/**
+	 * Deletes a value with the specified key from cache
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key the key of the value to be deleted
+	 * @return boolean if no error happens during deletion
+	 */
+	protected function deleteValue($key)
+	{
+		return apc_delete($key);
+	}
+
+	/**
+	 * Deletes all values from cache.
+	 * This is the implementation of the method declared in the parent class.
+	 * @return boolean whether the flush operation was successful.
+	 */
+	protected function flushValues()
+	{
+		if (extension_loaded('apcu')) {
+			return apc_clear_cache();
+		} else {
+			return apc_clear_cache('user');
+		}
+	}
+}

+ 473 - 0
php-yii2/app/vendor/yiisoft/yii2/caching/Cache.php

@@ -0,0 +1,473 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\caching;
+
+use Yii;
+use yii\base\Component;
+use yii\helpers\StringHelper;
+
+/**
+ * Cache is the base class for cache classes supporting different cache storage implementation.
+ *
+ * A data item can be stored in cache by calling [[set()]] and be retrieved back
+ * later (in the same or different request) by [[get()]]. In both operations,
+ * a key identifying the data item is required. An expiration time and/or a [[Dependency|dependency]]
+ * can also be specified when calling [[set()]]. If the data item expires or the dependency
+ * changes at the time of calling [[get()]], the cache will return no data.
+ *
+ * A typical usage pattern of cache is like the following:
+ *
+ * ~~~
+ * $key = 'demo';
+ * $data = $cache->get($key);
+ * if ($data === false) {
+ *     // ...generate $data here...
+ *     $cache->set($key, $data, $expire, $dependency);
+ * }
+ * ~~~
+ *
+ * Because Cache implements the ArrayAccess interface, it can be used like an array. For example,
+ *
+ * ~~~
+ * $cache['foo'] = 'some data';
+ * echo $cache['foo'];
+ * ~~~
+ *
+ * Derived classes should implement the following methods:
+ *
+ * - [[getValue()]]: retrieve the value with a key (if any) from cache
+ * - [[setValue()]]: store the value with a key into cache
+ * - [[addValue()]]: store the value only if the cache does not have this key before
+ * - [[deleteValue()]]: delete the value with the specified key from cache
+ * - [[flushValues()]]: delete all values from cache
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+abstract class Cache extends Component implements \ArrayAccess
+{
+	/**
+	 * @var string a string prefixed to every cache key so that it is unique. If not set,
+	 * it will use a prefix generated from [[Application::id]]. You may set this property to be an empty string
+	 * if you don't want to use key prefix. It is recommended that you explicitly set this property to some
+	 * static value if the cached data needs to be shared among multiple applications.
+	 *
+	 * To ensure interoperability, only alphanumeric characters should be used.
+	 */
+	public $keyPrefix;
+	/**
+	 * @var array|boolean the functions used to serialize and unserialize cached data. Defaults to null, meaning
+	 * using the default PHP `serialize()` and `unserialize()` functions. If you want to use some more efficient
+	 * serializer (e.g. [igbinary](http://pecl.php.net/package/igbinary)), you may configure this property with
+	 * a two-element array. The first element specifies the serialization function, and the second the deserialization
+	 * function. If this property is set false, data will be directly sent to and retrieved from the underlying
+	 * cache component without any serialization or deserialization. You should not turn off serialization if
+	 * you are using [[Dependency|cache dependency]], because it relies on data serialization.
+	 */
+	public $serializer;
+
+
+	/**
+	 * Initializes the application component.
+	 * This method overrides the parent implementation by setting default cache key prefix.
+	 */
+	public function init()
+	{
+		parent::init();
+		if ($this->keyPrefix === null) {
+			$this->keyPrefix = substr(md5(Yii::$app->id), 0, 5);
+		}
+	}
+
+	/**
+	 * Builds a normalized cache key from a given key.
+	 *
+	 * If the given key is a string containing alphanumeric characters only and no more than 32 characters,
+	 * then the key will be returned back prefixed with [[keyPrefix]]. Otherwise, a normalized key
+	 * is generated by serializing the given key, applying MD5 hashing, and prefixing with [[keyPrefix]].
+	 *
+	 * @param mixed $key the key to be normalized
+	 * @return string the generated cache key
+	 */
+	protected function buildKey($key)
+	{
+		if (is_string($key)) {
+			$key = ctype_alnum($key) && StringHelper::byteLength($key) <= 32 ? $key : md5($key);
+		} else {
+			$key = md5(json_encode($key));
+		}
+		return $this->keyPrefix . $key;
+	}
+
+	/**
+	 * Retrieves a value from cache with a specified key.
+	 * @param mixed $key a key identifying the cached value. This can be a simple string or
+	 * a complex data structure consisting of factors representing the key.
+	 * @return mixed the value stored in cache, false if the value is not in the cache, expired,
+	 * or the dependency associated with the cached data has changed.
+	 */
+	public function get($key)
+	{
+		$key = $this->buildKey($key);
+		$value = $this->getValue($key);
+		if ($value === false || $this->serializer === false) {
+			return $value;
+		} elseif ($this->serializer === null) {
+			$value = unserialize($value);
+		} else {
+			$value = call_user_func($this->serializer[1], $value);
+		}
+		if (is_array($value) && !($value[1] instanceof Dependency && $value[1]->getHasChanged($this))) {
+			return $value[0];
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * Checks whether a specified key exists in the cache.
+	 * This can be faster than getting the value from the cache if the data is big.
+	 * In case a cache does not support this feature natively, this method will try to simulate it
+	 * but has no performance improvement over getting it.
+	 * Note that this method does not check whether the dependency associated
+	 * with the cached data, if there is any, has changed. So a call to [[get]]
+	 * may return false while exists returns true.
+	 * @param mixed $key a key identifying the cached value. This can be a simple string or
+	 * a complex data structure consisting of factors representing the key.
+	 * @return boolean true if a value exists in cache, false if the value is not in the cache or expired.
+	 */
+	public function exists($key)
+	{
+		$key = $this->buildKey($key);
+		$value = $this->getValue($key);
+		return $value !== false;
+	}
+
+	/**
+	 * Retrieves multiple values from cache with the specified keys.
+	 * Some caches (such as memcache, apc) allow retrieving multiple cached values at the same time,
+	 * which may improve the performance. In case a cache does not support this feature natively,
+	 * this method will try to simulate it.
+	 * @param array $keys list of keys identifying the cached values
+	 * @return array list of cached values corresponding to the specified keys. The array
+	 * is returned in terms of (key, value) pairs.
+	 * If a value is not cached or expired, the corresponding array value will be false.
+	 */
+	public function mget($keys)
+	{
+		$keyMap = [];
+		foreach ($keys as $key) {
+			$keyMap[$key] = $this->buildKey($key);
+		}
+		$values = $this->getValues(array_values($keyMap));
+		$results = [];
+		foreach ($keyMap as $key => $newKey) {
+			$results[$key] = false;
+			if (isset($values[$newKey])) {
+				if ($this->serializer === false) {
+					$results[$key] = $values[$newKey];
+				} else {
+					$value = $this->serializer === null ? unserialize($values[$newKey])
+							: call_user_func($this->serializer[1], $values[$newKey]);
+
+					if (is_array($value) && !($value[1] instanceof Dependency && $value[1]->getHasChanged($this))) {
+						$results[$key] = $value[0];
+					}
+				}
+			}
+		}
+		return $results;
+	}
+
+	/**
+	 * Stores a value identified by a key into cache.
+	 * If the cache already contains such a key, the existing value and
+	 * expiration time will be replaced with the new ones, respectively.
+	 *
+	 * @param mixed $key a key identifying the value to be cached. This can be a simple string or
+	 * a complex data structure consisting of factors representing the key.
+	 * @param mixed $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @param Dependency $dependency dependency of the cached item. If the dependency changes,
+	 * the corresponding value in the cache will be invalidated when it is fetched via [[get()]].
+	 * This parameter is ignored if [[serializer]] is false.
+	 * @return boolean whether the value is successfully stored into cache
+	 */
+	public function set($key, $value, $expire = 0, $dependency = null)
+	{
+		if ($dependency !== null && $this->serializer !== false) {
+			$dependency->evaluateDependency($this);
+		}
+		if ($this->serializer === null) {
+			$value = serialize([$value, $dependency]);
+		} elseif ($this->serializer !== false) {
+			$value = call_user_func($this->serializer[0], [$value, $dependency]);
+		}
+		$key = $this->buildKey($key);
+		return $this->setValue($key, $value, $expire);
+	}
+
+	/**
+	 * Stores multiple items in cache. Each item contains a value identified by a key.
+	 * If the cache already contains such a key, the existing value and
+	 * expiration time will be replaced with the new ones, respectively.
+	 *
+	 * @param array $items the items to be cached, as key-value pairs.
+	 * @param integer $expire default number of seconds in which the cached values will expire. 0 means never expire.
+	 * @param Dependency $dependency dependency of the cached items. If the dependency changes,
+	 * the corresponding values in the cache will be invalidated when it is fetched via [[get()]].
+	 * This parameter is ignored if [[serializer]] is false.
+	 * @return boolean whether the items are successfully stored into cache
+	 */
+	public function mset($items, $expire = 0, $dependency = null)
+	{
+		if ($dependency !== null && $this->serializer !== false) {
+			$dependency->evaluateDependency($this);
+		}
+
+		$data = [];
+		foreach ($items as $key => $value) {
+			$itemKey = $this->buildKey($key);
+			if ($this->serializer === null) {
+				$itemValue = serialize([$value, $dependency]);
+			} elseif ($this->serializer !== false) {
+				$itemValue = call_user_func($this->serializer[0], [$value, $dependency]);
+			}
+
+			$data[$itemKey] = $itemValue;
+		}
+		return $this->setValues($data, $expire);
+	}
+
+	/**
+	 * Stores multiple items in cache. Each item contains a value identified by a key.
+	 * If the cache already contains such a key, the existing value and expiration time will be preserved.
+	 *
+	 * @param array $items the items to be cached, as key-value pairs.
+	 * @param integer $expire default number of seconds in which the cached values will expire. 0 means never expire.
+	 * @param Dependency $dependency dependency of the cached items. If the dependency changes,
+	 * the corresponding values in the cache will be invalidated when it is fetched via [[get()]].
+	 * This parameter is ignored if [[serializer]] is false.
+	 * @return boolean whether the items are successfully stored into cache
+	 */
+	public function madd($items, $expire = 0, $dependency = null)
+	{
+		if ($dependency !== null && $this->serializer !== false) {
+			$dependency->evaluateDependency($this);
+		}
+
+		$data = [];
+		foreach ($items as $key => $value) {
+			$itemKey = $this->buildKey($key);
+			if ($this->serializer === null) {
+				$itemValue = serialize([$value, $dependency]);
+			} elseif ($this->serializer !== false) {
+				$itemValue = call_user_func($this->serializer[0], [$value, $dependency]);
+			}
+
+			$data[$itemKey] = $itemValue;
+		}
+		return $this->addValues($data, $expire);
+	}
+
+	/**
+	 * Stores a value identified by a key into cache if the cache does not contain this key.
+	 * Nothing will be done if the cache already contains the key.
+	 * @param mixed $key a key identifying the value to be cached. This can be a simple string or
+	 * a complex data structure consisting of factors representing the key.
+	 * @param mixed $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @param Dependency $dependency dependency of the cached item. If the dependency changes,
+	 * the corresponding value in the cache will be invalidated when it is fetched via [[get()]].
+	 * This parameter is ignored if [[serializer]] is false.
+	 * @return boolean whether the value is successfully stored into cache
+	 */
+	public function add($key, $value, $expire = 0, $dependency = null)
+	{
+		if ($dependency !== null && $this->serializer !== false) {
+			$dependency->evaluateDependency($this);
+		}
+		if ($this->serializer === null) {
+			$value = serialize([$value, $dependency]);
+		} elseif ($this->serializer !== false) {
+			$value = call_user_func($this->serializer[0], [$value, $dependency]);
+		}
+		$key = $this->buildKey($key);
+		return $this->addValue($key, $value, $expire);
+	}
+
+	/**
+	 * Deletes a value with the specified key from cache
+	 * @param mixed $key a key identifying the value to be deleted from cache. This can be a simple string or
+	 * a complex data structure consisting of factors representing the key.
+	 * @return boolean if no error happens during deletion
+	 */
+	public function delete($key)
+	{
+		$key = $this->buildKey($key);
+		return $this->deleteValue($key);
+	}
+
+	/**
+	 * Deletes all values from cache.
+	 * Be careful of performing this operation if the cache is shared among multiple applications.
+	 * @return boolean whether the flush operation was successful.
+	 */
+	public function flush()
+	{
+		return $this->flushValues();
+	}
+
+	/**
+	 * Retrieves a value from cache with a specified key.
+	 * This method should be implemented by child classes to retrieve the data
+	 * from specific cache storage.
+	 * @param string $key a unique key identifying the cached value
+	 * @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
+	 */
+	abstract protected function getValue($key);
+
+	/**
+	 * Stores a value identified by a key in cache.
+	 * This method should be implemented by child classes to store the data
+	 * in specific cache storage.
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	abstract protected function setValue($key, $value, $expire);
+
+	/**
+	 * Stores a value identified by a key into cache if the cache does not contain this key.
+	 * This method should be implemented by child classes to store the data
+	 * in specific cache storage.
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	abstract protected function addValue($key, $value, $expire);
+
+	/**
+	 * Deletes a value with the specified key from cache
+	 * This method should be implemented by child classes to delete the data from actual cache storage.
+	 * @param string $key the key of the value to be deleted
+	 * @return boolean if no error happens during deletion
+	 */
+	abstract protected function deleteValue($key);
+
+	/**
+	 * Deletes all values from cache.
+	 * Child classes may implement this method to realize the flush operation.
+	 * @return boolean whether the flush operation was successful.
+	 */
+	abstract protected function flushValues();
+
+	/**
+	 * Retrieves multiple values from cache with the specified keys.
+	 * The default implementation calls [[getValue()]] multiple times to retrieve
+	 * the cached values one by one. If the underlying cache storage supports multiget,
+	 * this method should be overridden to exploit that feature.
+	 * @param array $keys a list of keys identifying the cached values
+	 * @return array a list of cached values indexed by the keys
+	 */
+	protected function getValues($keys)
+	{
+		$results = [];
+		foreach ($keys as $key) {
+			$results[$key] = $this->getValue($key);
+		}
+		return $results;
+	}
+
+	/**
+	 * Stores multiple key-value pairs in cache.
+	 * The default implementation calls [[setValue()]] multiple times store values one by one. If the underlying cache
+	 * storage supports multiset, this method should be overridden to exploit that feature.
+	 * @param array $data array where key corresponds to cache key while value is the value stored
+	 * @param integer $expire the number of seconds in which the cached values will expire. 0 means never expire.
+	 * @return array array of failed keys
+	 */
+	protected function setValues($data, $expire)
+	{
+		$failedKeys = [];
+		foreach ($data as $key => $value)
+		{
+			if ($this->setValue($key, $value, $expire) === false) {
+				$failedKeys[] = $key;
+			}
+		}
+		return $failedKeys;
+	}
+
+	/**
+	 * Adds multiple key-value pairs to cache.
+	 * The default implementation calls [[addValue()]] multiple times add values one by one. If the underlying cache
+	 * storage supports multiadd, this method should be overridden to exploit that feature.
+	 * @param array $data array where key corresponds to cache key while value is the value stored
+	 * @param integer $expire the number of seconds in which the cached values will expire. 0 means never expire.
+	 * @return array array of failed keys
+	 */
+	protected function addValues($data, $expire)
+	{
+		$failedKeys = [];
+		foreach ($data as $key => $value)
+		{
+			if ($this->addValue($key, $value, $expire) === false) {
+				$failedKeys[] = $key;
+			}
+		}
+		return $failedKeys;
+	}
+
+	/**
+	 * Returns whether there is a cache entry with a specified key.
+	 * This method is required by the interface ArrayAccess.
+	 * @param string $key a key identifying the cached value
+	 * @return boolean
+	 */
+	public function offsetExists($key)
+	{
+		return $this->get($key) !== false;
+	}
+
+	/**
+	 * Retrieves the value from cache with a specified key.
+	 * This method is required by the interface ArrayAccess.
+	 * @param string $key a key identifying the cached value
+	 * @return mixed the value stored in cache, false if the value is not in the cache or expired.
+	 */
+	public function offsetGet($key)
+	{
+		return $this->get($key);
+	}
+
+	/**
+	 * Stores the value identified by a key into cache.
+	 * If the cache already contains such a key, the existing value will be
+	 * replaced with the new ones. To add expiration and dependencies, use the [[set()]] method.
+	 * This method is required by the interface ArrayAccess.
+	 * @param string $key the key identifying the value to be cached
+	 * @param mixed $value the value to be cached
+	 */
+	public function offsetSet($key, $value)
+	{
+		$this->set($key, $value);
+	}
+
+	/**
+	 * Deletes the value with the specified key from cache
+	 * This method is required by the interface ArrayAccess.
+	 * @param string $key the key of the value to be deleted
+	 */
+	public function offsetUnset($key)
+	{
+		$this->delete($key);
+	}
+}

+ 75 - 0
php-yii2/app/vendor/yiisoft/yii2/caching/ChainedDependency.php

@@ -0,0 +1,75 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\caching;
+
+/**
+ * ChainedDependency represents a dependency which is composed of a list of other dependencies.
+ *
+ * When [[dependOnAll]] is true, if any of the dependencies has changed, this dependency is
+ * considered changed; When [[dependOnAll]] is false, if one of the dependencies has NOT changed,
+ * this dependency is considered NOT changed.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class ChainedDependency extends Dependency
+{
+	/**
+	 * @var Dependency[] list of dependencies that this dependency is composed of.
+	 * Each array element must be a dependency object.
+	 */
+	public $dependencies = [];
+	/**
+	 * @var boolean whether this dependency is depending on every dependency in [[dependencies]].
+	 * Defaults to true, meaning if any of the dependencies has changed, this dependency is considered changed.
+	 * When it is set false, it means if one of the dependencies has NOT changed, this dependency
+	 * is considered NOT changed.
+	 */
+	public $dependOnAll = true;
+
+	/**
+	 * Evaluates the dependency by generating and saving the data related with dependency.
+	 * @param Cache $cache the cache component that is currently evaluating this dependency
+	 */
+	public function evaluateDependency($cache)
+	{
+		foreach ($this->dependencies as $dependency) {
+			$dependency->evaluateDependency($cache);
+		}
+	}
+
+	/**
+	 * Generates the data needed to determine if dependency has been changed.
+	 * This method does nothing in this class.
+	 * @param Cache $cache the cache component that is currently evaluating this dependency
+	 * @return mixed the data needed to determine if dependency has been changed.
+	 */
+	protected function generateDependencyData($cache)
+	{
+		return null;
+	}
+
+	/**
+	 * Performs the actual dependency checking.
+	 * This method returns true if any of the dependency objects
+	 * reports a dependency change.
+	 * @param Cache $cache the cache component that is currently evaluating this dependency
+	 * @return boolean whether the dependency is changed or not.
+	 */
+	public function getHasChanged($cache)
+	{
+		foreach ($this->dependencies as $dependency) {
+			if ($this->dependOnAll && $dependency->getHasChanged($cache)) {
+				return true;
+			} elseif (!$this->dependOnAll && !$dependency->getHasChanged($cache)) {
+				return false;
+			}
+		}
+		return !$this->dependOnAll;
+	}
+}

+ 274 - 0
php-yii2/app/vendor/yiisoft/yii2/caching/DbCache.php

@@ -0,0 +1,274 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\caching;
+
+use Yii;
+use yii\base\InvalidConfigException;
+use yii\db\Connection;
+use yii\db\Query;
+
+/**
+ * DbCache implements a cache application component by storing cached data in a database.
+ *
+ * By default, DbCache stores session data in a DB table named 'tbl_cache'. This table
+ * must be pre-created. The table name can be changed by setting [[cacheTable]].
+ *
+ * Please refer to [[Cache]] for common cache operations that are supported by DbCache.
+ *
+ * The following example shows how you can configure the application to use DbCache:
+ *
+ * ~~~
+ * 'cache' => [
+ *     'class' => 'yii\caching\DbCache',
+ *     // 'db' => 'mydb',
+ *     // 'cacheTable' => 'my_cache',
+ * ]
+ * ~~~
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class DbCache extends Cache
+{
+	/**
+	 * @var Connection|string the DB connection object or the application component ID of the DB connection.
+	 * After the DbCache object is created, if you want to change this property, you should only assign it
+	 * with a DB connection object.
+	 */
+	public $db = 'db';
+	/**
+	 * @var string name of the DB table to store cache content.
+	 * The table should be pre-created as follows:
+	 *
+	 * ~~~
+	 * CREATE TABLE tbl_cache (
+	 *     id char(128) NOT NULL PRIMARY KEY,
+	 *     expire int(11),
+	 *     data BLOB
+	 * );
+	 * ~~~
+	 *
+	 * where 'BLOB' refers to the BLOB-type of your preferred DBMS. Below are the BLOB type
+	 * that can be used for some popular DBMS:
+	 *
+	 * - MySQL: LONGBLOB
+	 * - PostgreSQL: BYTEA
+	 * - MSSQL: BLOB
+	 *
+	 * When using DbCache in a production server, we recommend you create a DB index for the 'expire'
+	 * column in the cache table to improve the performance.
+	 */
+	public $cacheTable = '{{%cache}}';
+	/**
+	 * @var integer the probability (parts per million) that garbage collection (GC) should be performed
+	 * when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance.
+	 * This number should be between 0 and 1000000. A value 0 meaning no GC will be performed at all.
+	 **/
+	public $gcProbability = 100;
+
+
+	/**
+	 * Initializes the DbCache component.
+	 * This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
+	 * @throws InvalidConfigException if [[db]] is invalid.
+	 */
+	public function init()
+	{
+		parent::init();
+		if (is_string($this->db)) {
+			$this->db = Yii::$app->getComponent($this->db);
+		}
+		if (!$this->db instanceof Connection) {
+			throw new InvalidConfigException("DbCache::db must be either a DB connection instance or the application component ID of a DB connection.");
+		}
+	}
+
+	/**
+	 * Checks whether a specified key exists in the cache.
+	 * This can be faster than getting the value from the cache if the data is big.
+	 * Note that this method does not check whether the dependency associated
+	 * with the cached data, if there is any, has changed. So a call to [[get]]
+	 * may return false while exists returns true.
+	 * @param mixed $key a key identifying the cached value. This can be a simple string or
+	 * a complex data structure consisting of factors representing the key.
+	 * @return boolean true if a value exists in cache, false if the value is not in the cache or expired.
+	 */
+	public function exists($key)
+	{
+		$key = $this->buildKey($key);
+
+		$query = new Query;
+		$query->select(['COUNT(*)'])
+			->from($this->cacheTable)
+			->where('[[id]] = :id AND ([[expire]] = 0 OR [[expire]] >' . time() . ')', [':id' => $key]);
+		if ($this->db->enableQueryCache) {
+			// temporarily disable and re-enable query caching
+			$this->db->enableQueryCache = false;
+			$result = $query->createCommand($this->db)->queryScalar();
+			$this->db->enableQueryCache = true;
+		} else {
+			$result = $query->createCommand($this->db)->queryScalar();
+		}
+		return $result > 0;
+	}
+
+	/**
+	 * Retrieves a value from cache with a specified key.
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key a unique key identifying the cached value
+	 * @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
+	 */
+	protected function getValue($key)
+	{
+		$query = new Query;
+		$query->select(['data'])
+			->from($this->cacheTable)
+			->where('[[id]] = :id AND ([[expire]] = 0 OR [[expire]] >' . time() . ')', [':id' => $key]);
+		if ($this->db->enableQueryCache) {
+			// temporarily disable and re-enable query caching
+			$this->db->enableQueryCache = false;
+			$result = $query->createCommand($this->db)->queryScalar();
+			$this->db->enableQueryCache = true;
+			return $result;
+		} else {
+			return $query->createCommand($this->db)->queryScalar();
+		}
+	}
+
+	/**
+	 * Retrieves multiple values from cache with the specified keys.
+	 * @param array $keys a list of keys identifying the cached values
+	 * @return array a list of cached values indexed by the keys
+	 */
+	protected function getValues($keys)
+	{
+		if (empty($keys)) {
+			return [];
+		}
+		$query = new Query;
+		$query->select(['id', 'data'])
+			->from($this->cacheTable)
+			->where(['id' => $keys])
+			->andWhere('([[expire]] = 0 OR [[expire]] > ' . time() . ')');
+
+		if ($this->db->enableQueryCache) {
+			$this->db->enableQueryCache = false;
+			$rows = $query->createCommand($this->db)->queryAll();
+			$this->db->enableQueryCache = true;
+		} else {
+			$rows = $query->createCommand($this->db)->queryAll();
+		}
+
+		$results = [];
+		foreach ($keys as $key) {
+			$results[$key] = false;
+		}
+		foreach ($rows as $row) {
+			$results[$row['id']] = $row['data'];
+		}
+		return $results;
+	}
+
+	/**
+	 * Stores a value identified by a key in cache.
+	 * This is the implementation of the method declared in the parent class.
+	 *
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	protected function setValue($key, $value, $expire)
+	{
+		$command = $this->db->createCommand()
+			->update($this->cacheTable, [
+				'expire' => $expire > 0 ? $expire + time() : 0,
+				'data' => [$value, \PDO::PARAM_LOB],
+			], ['id' => $key]);
+
+		if ($command->execute()) {
+			$this->gc();
+			return true;
+		} else {
+			return $this->addValue($key, $value, $expire);
+		}
+	}
+
+	/**
+	 * Stores a value identified by a key into cache if the cache does not contain this key.
+	 * This is the implementation of the method declared in the parent class.
+	 *
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	protected function addValue($key, $value, $expire)
+	{
+		$this->gc();
+
+		if ($expire > 0) {
+			$expire += time();
+		} else {
+			$expire = 0;
+		}
+
+		try {
+			$this->db->createCommand()
+				->insert($this->cacheTable, [
+					'id' => $key,
+					'expire' => $expire,
+					'data' => [$value, \PDO::PARAM_LOB],
+				])->execute();
+			return true;
+		} catch (\Exception $e) {
+			return false;
+		}
+	}
+
+	/**
+	 * Deletes a value with the specified key from cache
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key the key of the value to be deleted
+	 * @return boolean if no error happens during deletion
+	 */
+	protected function deleteValue($key)
+	{
+		$this->db->createCommand()
+			->delete($this->cacheTable, ['id' => $key])
+			->execute();
+		return true;
+	}
+
+	/**
+	 * Removes the expired data values.
+	 * @param boolean $force whether to enforce the garbage collection regardless of [[gcProbability]].
+	 * Defaults to false, meaning the actual deletion happens with the probability as specified by [[gcProbability]].
+	 */
+	public function gc($force = false)
+	{
+		if ($force || mt_rand(0, 1000000) < $this->gcProbability) {
+			$this->db->createCommand()
+				->delete($this->cacheTable, '[[expire]] > 0 AND [[expire]] < ' . time())
+				->execute();
+		}
+	}
+
+	/**
+	 * Deletes all values from cache.
+	 * This is the implementation of the method declared in the parent class.
+	 * @return boolean whether the flush operation was successful.
+	 */
+	protected function flushValues()
+	{
+		$this->db->createCommand()
+			->delete($this->cacheTable)
+			->execute();
+		return true;
+	}
+}

+ 66 - 0
php-yii2/app/vendor/yiisoft/yii2/caching/DbDependency.php

@@ -0,0 +1,66 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\caching;
+
+use Yii;
+use yii\base\InvalidConfigException;
+use yii\db\Connection;
+
+/**
+ * DbDependency represents a dependency based on the query result of a SQL statement.
+ *
+ * If the query result changes, the dependency is considered as changed.
+ * The query is specified via the [[sql]] property.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class DbDependency extends Dependency
+{
+	/**
+	 * @var string the application component ID of the DB connection.
+	 */
+	public $db = 'db';
+	/**
+	 * @var string the SQL query whose result is used to determine if the dependency has been changed.
+	 * Only the first row of the query result will be used.
+	 */
+	public $sql;
+	/**
+	 * @var array the parameters (name => value) to be bound to the SQL statement specified by [[sql]].
+	 */
+	public $params = [];
+
+	/**
+	 * Generates the data needed to determine if dependency has been changed.
+	 * This method returns the value of the global state.
+	 * @param Cache $cache the cache component that is currently evaluating this dependency
+	 * @return mixed the data needed to determine if dependency has been changed.
+	 * @throws InvalidConfigException if [[db]] is not a valid application component ID
+	 */
+	protected function generateDependencyData($cache)
+	{
+		$db = Yii::$app->getComponent($this->db);
+		if (!$db instanceof Connection) {
+			throw new InvalidConfigException("DbDependency::db must be the application component ID of a DB connection.");
+		}
+		if ($this->sql === null) {
+			throw new InvalidConfigException("DbDependency::sql must be set.");
+		}
+
+		if ($db->enableQueryCache) {
+			// temporarily disable and re-enable query caching
+			$db->enableQueryCache = false;
+			$result = $db->createCommand($this->sql, $this->params)->queryOne();
+			$db->enableQueryCache = true;
+		} else {
+			$result = $db->createCommand($this->sql, $this->params)->queryOne();
+		}
+		return $result;
+	}
+}

+ 99 - 0
php-yii2/app/vendor/yiisoft/yii2/caching/Dependency.php

@@ -0,0 +1,99 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\caching;
+
+/**
+ * Dependency is the base class for cache dependency classes.
+ *
+ * Child classes should override its [[generateDependencyData()]] for generating
+ * the actual dependency data.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+abstract class Dependency extends \yii\base\Object
+{
+	/**
+	 * @var mixed the dependency data that is saved in cache and later is compared with the
+	 * latest dependency data.
+	 */
+	public $data;
+	/**
+	 * @var boolean whether this dependency is reusable or not. True value means that dependent
+	 * data for this cache dependency will be generated only once per request. This allows you
+	 * to use the same cache dependency for multiple separate cache calls while generating the same
+	 * page without an overhead of re-evaluating dependency data each time. Defaults to false.
+	 */
+	public $reusable = false;
+
+	/**
+	 * @var array static storage of cached data for reusable dependencies.
+	 */
+	private static $_reusableData = [];
+	/**
+	 * @var string a unique hash value for this cache dependency.
+	 */
+	private $_hash;
+
+
+	/**
+	 * Evaluates the dependency by generating and saving the data related with dependency.
+	 * This method is invoked by cache before writing data into it.
+	 * @param Cache $cache the cache component that is currently evaluating this dependency
+	 */
+	public function evaluateDependency($cache)
+	{
+		if (!$this->reusable) {
+			$this->data = $this->generateDependencyData($cache);
+		} else {
+			if ($this->_hash === null) {
+				$this->_hash = sha1(serialize($this));
+			}
+			if (!array_key_exists($this->_hash, self::$_reusableData)) {
+				self::$_reusableData[$this->_hash] = $this->generateDependencyData($cache);
+			}
+			$this->data = self::$_reusableData[$this->_hash];
+		}
+	}
+
+	/**
+	 * Returns a value indicating whether the dependency has changed.
+	 * @param Cache $cache the cache component that is currently evaluating this dependency
+	 * @return boolean whether the dependency has changed.
+	 */
+	public function getHasChanged($cache)
+	{
+		if (!$this->reusable) {
+			return $this->generateDependencyData($cache) !== $this->data;
+		} else {
+			if ($this->_hash === null) {
+				$this->_hash = sha1(serialize($this));
+			}
+			if (!array_key_exists($this->_hash, self::$_reusableData)) {
+				self::$_reusableData[$this->_hash] = $this->generateDependencyData($cache);
+			}
+			return self::$_reusableData[$this->_hash] !== $this->data;
+		}
+	}
+
+	/**
+	 * Resets all cached data for reusable dependencies.
+	 */
+	public static function resetReusableData()
+	{
+		self::$_reusableData = [];
+	}
+
+	/**
+	 * Generates the data needed to determine if dependency has been changed.
+	 * Derived classes should override this method to generate the actual dependency data.
+	 * @param Cache $cache the cache component that is currently evaluating this dependency
+	 * @return mixed the data needed to determine if dependency has been changed.
+	 */
+	abstract protected function generateDependencyData($cache);
+}

+ 81 - 0
php-yii2/app/vendor/yiisoft/yii2/caching/DummyCache.php

@@ -0,0 +1,81 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\caching;
+
+/**
+ * DummyCache is a placeholder cache component.
+ *
+ * DummyCache does not cache anything. It is provided so that one can always configure
+ * a 'cache' application component and save the check of existence of `\Yii::$app->cache`.
+ * By replacing DummyCache with some other cache component, one can quickly switch from
+ * non-caching mode to caching mode.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class DummyCache extends Cache
+{
+	/**
+	 * Retrieves a value from cache with a specified key.
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key a unique key identifying the cached value
+	 * @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
+	 */
+	protected function getValue($key)
+	{
+		return false;
+	}
+
+	/**
+	 * Stores a value identified by a key in cache.
+	 * This is the implementation of the method declared in the parent class.
+	 *
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	protected function setValue($key, $value, $expire)
+	{
+		return true;
+	}
+
+	/**
+	 * Stores a value identified by a key into cache if the cache does not contain this key.
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	protected function addValue($key, $value, $expire)
+	{
+		return true;
+	}
+
+	/**
+	 * Deletes a value with the specified key from cache
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key the key of the value to be deleted
+	 * @return boolean if no error happens during deletion
+	 */
+	protected function deleteValue($key)
+	{
+		return true;
+	}
+
+	/**
+	 * Deletes all values from cache.
+	 * This is the implementation of the method declared in the parent class.
+	 * @return boolean whether the flush operation was successful.
+	 */
+	protected function flushValues()
+	{
+		return true;
+	}
+}

+ 47 - 0
php-yii2/app/vendor/yiisoft/yii2/caching/ExpressionDependency.php

@@ -0,0 +1,47 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\caching;
+
+/**
+ * ExpressionDependency represents a dependency based on the result of a PHP expression.
+ *
+ * ExpressionDependency will use `eval()` to evaluate the PHP expression.
+ * The dependency is reported as unchanged if and only if the result of the expression is
+ * the same as the one evaluated when storing the data to cache.
+ *
+ * A PHP expression can be any PHP code that has a value. To learn more about what an expression is,
+ * please refer to the [php manual](http://www.php.net/manual/en/language.expressions.php).
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class ExpressionDependency extends Dependency
+{
+	/**
+	 * @var string the string representation of a PHP expression whose result is used to determine the dependency.
+	 * A PHP expression can be any PHP code that evaluates to a value. To learn more about what an expression is,
+	 * please refer to the [php manual](http://www.php.net/manual/en/language.expressions.php).
+	 */
+	public $expression = 'true';
+	/**
+	 * @var mixed custom parameters associated with this dependency. You may get the value
+	 * of this property in [[expression]] using `$this->params`.
+	 */
+	public $params;
+
+	/**
+	 * Generates the data needed to determine if dependency has been changed.
+	 * This method returns the result of the PHP expression.
+	 * @param Cache $cache the cache component that is currently evaluating this dependency
+	 * @return mixed the data needed to determine if dependency has been changed.
+	 */
+	protected function generateDependencyData($cache)
+	{
+		return eval("return {$this->expression};");
+	}
+}

+ 240 - 0
php-yii2/app/vendor/yiisoft/yii2/caching/FileCache.php

@@ -0,0 +1,240 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\caching;
+
+use Yii;
+use yii\helpers\FileHelper;
+
+/**
+ * FileCache implements a cache component using files.
+ *
+ * For each data value being cached, FileCache will store it in a separate file.
+ * The cache files are placed under [[cachePath]]. FileCache will perform garbage collection
+ * automatically to remove expired cache files.
+ *
+ * Please refer to [[Cache]] for common cache operations that are supported by FileCache.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class FileCache extends Cache
+{
+	/**
+	 * @var string the directory to store cache files. You may use path alias here.
+	 * If not set, it will use the "cache" subdirectory under the application runtime path.
+	 */
+	public $cachePath = '@runtime/cache';
+	/**
+	 * @var string cache file suffix. Defaults to '.bin'.
+	 */
+	public $cacheFileSuffix = '.bin';
+	/**
+	 * @var integer the level of sub-directories to store cache files. Defaults to 1.
+	 * If the system has huge number of cache files (e.g. one million), you may use a bigger value
+	 * (usually no bigger than 3). Using sub-directories is mainly to ensure the file system
+	 * is not over burdened with a single directory having too many files.
+	 */
+	public $directoryLevel = 1;
+	/**
+	 * @var integer the probability (parts per million) that garbage collection (GC) should be performed
+	 * when storing a piece of data in the cache. Defaults to 10, meaning 0.001% chance.
+	 * This number should be between 0 and 1000000. A value 0 means no GC will be performed at all.
+	 **/
+	public $gcProbability = 10;
+	/**
+	 * @var integer the permission to be set for newly created cache files.
+	 * This value will be used by PHP chmod() function. No umask will be applied.
+	 * If not set, the permission will be determined by the current environment.
+	 */
+	public $fileMode;
+	/**
+	 * @var integer the permission to be set for newly created directories.
+	 * This value will be used by PHP chmod() function. No umask will be applied.
+	 * Defaults to 0775, meaning the directory is read-writable by owner and group,
+	 * but read-only for other users.
+	 */
+	public $dirMode = 0775;
+
+
+	/**
+	 * Initializes this component by ensuring the existence of the cache path.
+	 */
+	public function init()
+	{
+		parent::init();
+		$this->cachePath = Yii::getAlias($this->cachePath);
+		if (!is_dir($this->cachePath)) {
+			FileHelper::createDirectory($this->cachePath, $this->dirMode, true);
+		}
+	}
+
+	/**
+	 * Checks whether a specified key exists in the cache.
+	 * This can be faster than getting the value from the cache if the data is big.
+	 * Note that this method does not check whether the dependency associated
+	 * with the cached data, if there is any, has changed. So a call to [[get]]
+	 * may return false while exists returns true.
+	 * @param mixed $key a key identifying the cached value. This can be a simple string or
+	 * a complex data structure consisting of factors representing the key.
+	 * @return boolean true if a value exists in cache, false if the value is not in the cache or expired.
+	 */
+	public function exists($key)
+	{
+		$cacheFile = $this->getCacheFile($this->buildKey($key));
+		return @filemtime($cacheFile) > time();
+	}
+
+	/**
+	 * Retrieves a value from cache with a specified key.
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key a unique key identifying the cached value
+	 * @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
+	 */
+	protected function getValue($key)
+	{
+		$cacheFile = $this->getCacheFile($key);
+		if (@filemtime($cacheFile) > time()) {
+			return @file_get_contents($cacheFile);
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * Stores a value identified by a key in cache.
+	 * This is the implementation of the method declared in the parent class.
+	 *
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	protected function setValue($key, $value, $expire)
+	{
+		if ($expire <= 0) {
+			$expire = 31536000; // 1 year
+		}
+		$expire += time();
+
+		$cacheFile = $this->getCacheFile($key);
+		if ($this->directoryLevel > 0) {
+			@FileHelper::createDirectory(dirname($cacheFile), $this->dirMode, true);
+		}
+		if (@file_put_contents($cacheFile, $value, LOCK_EX) !== false) {
+			if ($this->fileMode !== null) {
+				@chmod($cacheFile, $this->fileMode);
+			}
+			return @touch($cacheFile, $expire);
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * Stores a value identified by a key into cache if the cache does not contain this key.
+	 * This is the implementation of the method declared in the parent class.
+	 *
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	protected function addValue($key, $value, $expire)
+	{
+		$cacheFile = $this->getCacheFile($key);
+		if (@filemtime($cacheFile) > time()) {
+			return false;
+		}
+		return $this->setValue($key, $value, $expire);
+	}
+
+	/**
+	 * Deletes a value with the specified key from cache
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key the key of the value to be deleted
+	 * @return boolean if no error happens during deletion
+	 */
+	protected function deleteValue($key)
+	{
+		$cacheFile = $this->getCacheFile($key);
+		return @unlink($cacheFile);
+	}
+
+	/**
+	 * Returns the cache file path given the cache key.
+	 * @param string $key cache key
+	 * @return string the cache file path
+	 */
+	protected function getCacheFile($key)
+	{
+		if ($this->directoryLevel > 0) {
+			$base = $this->cachePath;
+			for ($i = 0; $i < $this->directoryLevel; ++$i) {
+				if (($prefix = substr($key, $i + $i, 2)) !== false) {
+					$base .= DIRECTORY_SEPARATOR . $prefix;
+				}
+			}
+			return $base . DIRECTORY_SEPARATOR . $key . $this->cacheFileSuffix;
+		} else {
+			return $this->cachePath . DIRECTORY_SEPARATOR . $key . $this->cacheFileSuffix;
+		}
+	}
+
+	/**
+	 * Deletes all values from cache.
+	 * This is the implementation of the method declared in the parent class.
+	 * @return boolean whether the flush operation was successful.
+	 */
+	protected function flushValues()
+	{
+		$this->gc(true, false);
+		return true;
+	}
+
+	/**
+	 * Removes expired cache files.
+	 * @param boolean $force whether to enforce the garbage collection regardless of [[gcProbability]].
+	 * Defaults to false, meaning the actual deletion happens with the probability as specified by [[gcProbability]].
+	 * @param boolean $expiredOnly whether to removed expired cache files only.
+	 * If true, all cache files under [[cachePath]] will be removed.
+	 */
+	public function gc($force = false, $expiredOnly = true)
+	{
+		if ($force || mt_rand(0, 1000000) < $this->gcProbability) {
+			$this->gcRecursive($this->cachePath, $expiredOnly);
+		}
+	}
+
+	/**
+	 * Recursively removing expired cache files under a directory.
+	 * This method is mainly used by [[gc()]].
+	 * @param string $path the directory under which expired cache files are removed.
+	 * @param boolean $expiredOnly whether to only remove expired cache files. If false, all files
+	 * under `$path` will be removed.
+	 */
+	protected function gcRecursive($path, $expiredOnly)
+	{
+		if (($handle = opendir($path)) !== false) {
+			while (($file = readdir($handle)) !== false) {
+				if ($file[0] === '.') {
+					continue;
+				}
+				$fullPath = $path . DIRECTORY_SEPARATOR . $file;
+				if (is_dir($fullPath)) {
+					$this->gcRecursive($fullPath, $expiredOnly);
+					if (!$expiredOnly) {
+						@rmdir($fullPath);
+					}
+				} elseif (!$expiredOnly || $expiredOnly && @filemtime($fullPath) < time()) {
+					@unlink($fullPath);
+				}
+			}
+			closedir($handle);
+		}
+	}
+}

+ 42 - 0
php-yii2/app/vendor/yiisoft/yii2/caching/FileDependency.php

@@ -0,0 +1,42 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\caching;
+use yii\base\InvalidConfigException;
+
+/**
+ * FileDependency represents a dependency based on a file's last modification time.
+ *
+ * If th last modification time of the file specified via [[fileName]] is changed,
+ * the dependency is considered as changed.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class FileDependency extends Dependency
+{
+	/**
+	 * @var string the name of the file whose last modification time is used to
+	 * check if the dependency has been changed.
+	 */
+	public $fileName;
+
+	/**
+	 * Generates the data needed to determine if dependency has been changed.
+	 * This method returns the file's last modification time.
+	 * @param Cache $cache the cache component that is currently evaluating this dependency
+	 * @return mixed the data needed to determine if dependency has been changed.
+	 * @throws InvalidConfigException if [[fileName]] is not set
+	 */
+	protected function generateDependencyData($cache)
+	{
+		if ($this->fileName === null) {
+			throw new InvalidConfigException('FileDependency::fileName must be set');
+		}
+		return @filemtime($this->fileName);
+	}
+}

+ 73 - 0
php-yii2/app/vendor/yiisoft/yii2/caching/GroupDependency.php

@@ -0,0 +1,73 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\caching;
+use yii\base\InvalidConfigException;
+
+/**
+ * GroupDependency marks a cached data item with a group name.
+ *
+ * You may invalidate the cached data items with the same group name all at once
+ * by calling [[invalidate()]].
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class GroupDependency extends Dependency
+{
+	/**
+	 * @var string the group name. This property must be set.
+	 */
+	public $group;
+
+	/**
+	 * Generates the data needed to determine if dependency has been changed.
+	 * This method does nothing in this class.
+	 * @param Cache $cache the cache component that is currently evaluating this dependency
+	 * @return mixed the data needed to determine if dependency has been changed.
+	 * @throws InvalidConfigException if [[group]] is not set.
+	 */
+	protected function generateDependencyData($cache)
+	{
+		if ($this->group === null) {
+			throw new InvalidConfigException('GroupDependency::group must be set');
+		}
+		$version = $cache->get([__CLASS__, $this->group]);
+		if ($version === false) {
+			$version = $this->invalidate($cache, $this->group);
+		}
+		return $version;
+	}
+
+	/**
+	 * Performs the actual dependency checking.
+	 * @param Cache $cache the cache component that is currently evaluating this dependency
+	 * @return boolean whether the dependency is changed or not.
+	 * @throws InvalidConfigException if [[group]] is not set.
+	 */
+	public function getHasChanged($cache)
+	{
+		if ($this->group === null) {
+			throw new InvalidConfigException('GroupDependency::group must be set');
+		}
+		$version = $cache->get([__CLASS__, $this->group]);
+		return $version === false || $version !== $this->data;
+	}
+
+	/**
+	 * Invalidates all of the cached data items that have the same [[group]].
+	 * @param Cache $cache the cache component that caches the data items
+	 * @param string $group the group name
+	 * @return string the current version number
+	 */
+	public static function invalidate($cache, $group)
+	{
+		$version = microtime();
+		$cache->set([__CLASS__, $group], $version);
+		return $version;
+	}
+}

+ 265 - 0
php-yii2/app/vendor/yiisoft/yii2/caching/MemCache.php

@@ -0,0 +1,265 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\caching;
+
+use yii\base\InvalidConfigException;
+
+/**
+ * MemCache implements a cache application component based on [memcache](http://pecl.php.net/package/memcache)
+ * and [memcached](http://pecl.php.net/package/memcached).
+ *
+ * MemCache supports both [memcache](http://pecl.php.net/package/memcache) and
+ * [memcached](http://pecl.php.net/package/memcached). By setting [[useMemcached]] to be true or false,
+ * one can let MemCache to use either memcached or memcache, respectively.
+ *
+ * MemCache can be configured with a list of memcache servers by settings its [[servers]] property.
+ * By default, MemCache assumes there is a memcache server running on localhost at port 11211.
+ *
+ * See [[Cache]] for common cache operations that MemCache supports.
+ *
+ * Note, there is no security measure to protected data in memcache.
+ * All data in memcache can be accessed by any process running in the system.
+ *
+ * To use MemCache as the cache application component, configure the application as follows,
+ *
+ * ~~~
+ * [
+ *     'components' => [
+ *         'cache' => [
+ *             'class' => 'yii\caching\MemCache',
+ *             'servers' => [
+ *                 [
+ *                     'host' => 'server1',
+ *                     'port' => 11211,
+ *                     'weight' => 60,
+ *                 ],
+ *                 [
+ *                     'host' => 'server2',
+ *                     'port' => 11211,
+ *                     'weight' => 40,
+ *                 ],
+ *             ],
+ *         ],
+ *     ],
+ * ]
+ * ~~~
+ *
+ * In the above, two memcache servers are used: server1 and server2. You can configure more properties of
+ * each server, such as `persistent`, `weight`, `timeout`. Please see [[MemCacheServer]] for available options.
+ *
+ * @property \Memcache|\Memcached $memcache The memcache (or memcached) object used by this cache component.
+ * This property is read-only.
+ * @property MemCacheServer[] $servers List of memcache server configurations. Note that the type of this
+ * property differs in getter and setter. See [[getServers()]] and [[setServers()]] for details.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class MemCache extends Cache
+{
+	/**
+	 * @var boolean whether to use memcached or memcache as the underlying caching extension.
+	 * If true, [memcached](http://pecl.php.net/package/memcached) will be used.
+	 * If false, [memcache](http://pecl.php.net/package/memcache) will be used.
+	 * Defaults to false.
+	 */
+	public $useMemcached = false;
+	/**
+	 * @var \Memcache|\Memcached the Memcache instance
+	 */
+	private $_cache = null;
+	/**
+	 * @var array list of memcache server configurations
+	 */
+	private $_servers = [];
+
+	/**
+	 * Initializes this application component.
+	 * It creates the memcache instance and adds memcache servers.
+	 */
+	public function init()
+	{
+		parent::init();
+		$servers = $this->getServers();
+		$cache = $this->getMemCache();
+		if (empty($servers)) {
+			$cache->addServer('127.0.0.1', 11211);
+		} else {
+			if (!$this->useMemcached) {
+				// different version of memcache may have different number of parameters for the addServer method.
+				$class = new \ReflectionClass($cache);
+				$paramCount = $class->getMethod('addServer')->getNumberOfParameters();
+			}
+			foreach ($servers as $server) {
+				if ($server->host === null) {
+					throw new InvalidConfigException("The 'host' property must be specified for every memcache server.");
+				}
+				if ($this->useMemcached) {
+					$cache->addServer($server->host, $server->port, $server->weight);
+				} else {
+					// $timeout is used for memcache versions that do not have timeoutms parameter
+					$timeout = (int) ($server->timeout / 1000) + (($server->timeout % 1000 > 0) ? 1 : 0);
+					if ($paramCount === 9) {
+						$cache->addServer(
+							$server->host, $server->port, $server->persistent,
+							$server->weight, $timeout, $server->retryInterval,
+							$server->status, $server->failureCallback, $server->timeout
+						);
+					} else {
+						$cache->addServer(
+							$server->host, $server->port, $server->persistent,
+							$server->weight, $timeout, $server->retryInterval,
+							$server->status, $server->failureCallback
+						);
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Returns the underlying memcache (or memcached) object.
+	 * @return \Memcache|\Memcached the memcache (or memcached) object used by this cache component.
+	 * @throws InvalidConfigException if memcache or memcached extension is not loaded
+	 */
+	public function getMemcache()
+	{
+		if ($this->_cache === null) {
+			$extension = $this->useMemcached ? 'memcached' : 'memcache';
+			if (!extension_loaded($extension)) {
+				throw new InvalidConfigException("MemCache requires PHP $extension extension to be loaded.");
+			}
+			$this->_cache = $this->useMemcached ? new \Memcached : new \Memcache;
+		}
+		return $this->_cache;
+	}
+
+	/**
+	 * Returns the memcache server configurations.
+	 * @return MemCacheServer[] list of memcache server configurations.
+	 */
+	public function getServers()
+	{
+		return $this->_servers;
+	}
+
+	/**
+	 * @param array $config list of memcache server configurations. Each element must be an array
+	 * with the following keys: host, port, persistent, weight, timeout, retryInterval, status.
+	 * @see http://www.php.net/manual/en/function.Memcache-addServer.php
+	 */
+	public function setServers($config)
+	{
+		foreach ($config as $c) {
+			$this->_servers[] = new MemCacheServer($c);
+		}
+	}
+
+	/**
+	 * Retrieves a value from cache with a specified key.
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key a unique key identifying the cached value
+	 * @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
+	 */
+	protected function getValue($key)
+	{
+		return $this->_cache->get($key);
+	}
+
+	/**
+	 * Retrieves multiple values from cache with the specified keys.
+	 * @param array $keys a list of keys identifying the cached values
+	 * @return array a list of cached values indexed by the keys
+	 */
+	protected function getValues($keys)
+	{
+		return $this->useMemcached ? $this->_cache->getMulti($keys) : $this->_cache->get($keys);
+	}
+
+	/**
+	 * Stores a value identified by a key in cache.
+	 * This is the implementation of the method declared in the parent class.
+	 *
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	protected function setValue($key, $value, $expire)
+	{
+		if ($expire > 0) {
+			$expire += time();
+		} else {
+			$expire = 0;
+		}
+
+		return $this->useMemcached ? $this->_cache->set($key, $value, $expire) : $this->_cache->set($key, $value, 0, $expire);
+	}
+
+	/**
+	 * Stores multiple key-value pairs in cache.
+	 * @param array $data array where key corresponds to cache key while value is the value stored
+	 * @param integer $expire the number of seconds in which the cached values will expire. 0 means never expire.
+	 * @return array array of failed keys. Always empty in case of using memcached.
+	 */
+	protected function setValues($data, $expire)
+	{
+		if ($this->useMemcached) {
+			if ($expire > 0) {
+				$expire += time();
+			} else {
+				$expire = 0;
+			}
+			$this->_cache->setMulti($data, $expire);
+			return [];
+		} else {
+			return parent::setValues($data, $expire);
+		}
+	}
+
+	/**
+	 * Stores a value identified by a key into cache if the cache does not contain this key.
+	 * This is the implementation of the method declared in the parent class.
+	 *
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	protected function addValue($key, $value, $expire)
+	{
+		if ($expire > 0) {
+			$expire += time();
+		} else {
+			$expire = 0;
+		}
+
+		return $this->useMemcached ? $this->_cache->add($key, $value, $expire) : $this->_cache->add($key, $value, 0, $expire);
+	}
+
+	/**
+	 * Deletes a value with the specified key from cache
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key the key of the value to be deleted
+	 * @return boolean if no error happens during deletion
+	 */
+	protected function deleteValue($key)
+	{
+		return $this->_cache->delete($key, 0);
+	}
+
+	/**
+	 * Deletes all values from cache.
+	 * This is the implementation of the method declared in the parent class.
+	 * @return boolean whether the flush operation was successful.
+	 */
+	protected function flushValues()
+	{
+		return $this->_cache->flush();
+	}
+}

+ 58 - 0
php-yii2/app/vendor/yiisoft/yii2/caching/MemCacheServer.php

@@ -0,0 +1,58 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\caching;
+
+/**
+ * MemCacheServer represents the configuration data for a single memcache or memcached server.
+ *
+ * See [PHP manual](http://www.php.net/manual/en/function.Memcache-addServer.php) for detailed explanation
+ * of each configuration property.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class MemCacheServer extends \yii\base\Object
+{
+	/**
+	 * @var string memcache server hostname or IP address
+	 */
+	public $host;
+	/**
+	 * @var integer memcache server port
+	 */
+	public $port = 11211;
+	/**
+	 * @var integer probability of using this server among all servers.
+	 */
+	public $weight = 1;
+	/**
+	 * @var boolean whether to use a persistent connection. This is used by memcache only.
+	 */
+	public $persistent = true;
+	/**
+	 * @var integer timeout in milliseconds which will be used for connecting to the server.
+	 * This is used by memcache only. For old versions of memcache that only support specifying
+	 * timeout in seconds this will be rounded up to full seconds.
+	 */
+	public $timeout = 1000;
+	/**
+	 * @var integer how often a failed server will be retried (in seconds). This is used by memcache only.
+	 */
+	public $retryInterval = 15;
+	/**
+	 * @var boolean if the server should be flagged as online upon a failure. This is used by memcache only.
+	 */
+	public $status = true;
+	/**
+	 * @var \Closure this callback function will run upon encountering an error.
+	 * The callback is run before fail over is attempted. The function takes two parameters,
+	 * the [[host]] and the [[port]] of the failed server.
+	 * This is used by memcache only.
+	 */
+	public $failureCallback;
+}

+ 132 - 0
php-yii2/app/vendor/yiisoft/yii2/caching/WinCache.php

@@ -0,0 +1,132 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\caching;
+
+/**
+ * WinCache provides Windows Cache caching in terms of an application component.
+ *
+ * To use this application component, the [WinCache PHP extension](http://www.iis.net/expand/wincacheforphp)
+ * must be loaded. Also note that "wincache.ucenabled" should be set to "On" in your php.ini file.
+ *
+ * See [[Cache]] manual for common cache operations that are supported by WinCache.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class WinCache extends Cache
+{
+	/**
+	 * Checks whether a specified key exists in the cache.
+	 * This can be faster than getting the value from the cache if the data is big.
+	 * Note that this method does not check whether the dependency associated
+	 * with the cached data, if there is any, has changed. So a call to [[get]]
+	 * may return false while exists returns true.
+	 * @param mixed $key a key identifying the cached value. This can be a simple string or
+	 * a complex data structure consisting of factors representing the key.
+	 * @return boolean true if a value exists in cache, false if the value is not in the cache or expired.
+	 */
+	public function exists($key)
+	{
+		$key = $this->buildKey($key);
+		return wincache_ucache_exists($key);
+	}
+
+	/**
+	 * Retrieves a value from cache with a specified key.
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key a unique key identifying the cached value
+	 * @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
+	 */
+	protected function getValue($key)
+	{
+		return wincache_ucache_get($key);
+	}
+
+	/**
+	 * Retrieves multiple values from cache with the specified keys.
+	 * @param array $keys a list of keys identifying the cached values
+	 * @return array a list of cached values indexed by the keys
+	 */
+	protected function getValues($keys)
+	{
+		return wincache_ucache_get($keys);
+	}
+
+	/**
+	 * Stores a value identified by a key in cache.
+	 * This is the implementation of the method declared in the parent class.
+	 *
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	protected function setValue($key, $value, $expire)
+	{
+		return wincache_ucache_set($key, $value, $expire);
+	}
+
+	/**
+	 * Stores multiple key-value pairs in cache.
+	 * @param array $data array where key corresponds to cache key while value is the value stored
+	 * @param integer $expire the number of seconds in which the cached values will expire. 0 means never expire.
+	 * @return array array of failed keys
+	 */
+	protected function setValues($data, $expire)
+	{
+		return wincache_ucache_set($data, null, $expire);
+	}
+
+	/**
+	 * Stores a value identified by a key into cache if the cache does not contain this key.
+	 * This is the implementation of the method declared in the parent class.
+	 *
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	protected function addValue($key, $value, $expire)
+	{
+		return wincache_ucache_add($key, $value, $expire);
+	}
+
+	/**
+	 * Adds multiple key-value pairs to cache.
+	 * The default implementation calls [[addValue()]] multiple times add values one by one. If the underlying cache
+	 * storage supports multiadd, this method should be overridden to exploit that feature.
+	 * @param array $data array where key corresponds to cache key while value is the value stored
+	 * @param integer $expire the number of seconds in which the cached values will expire. 0 means never expire.
+	 * @return array array of failed keys
+	 */
+	protected function addValues($data, $expire)
+	{
+		return wincache_ucache_add($data, null, $expire);
+	}
+
+	/**
+	 * Deletes a value with the specified key from cache
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key the key of the value to be deleted
+	 * @return boolean if no error happens during deletion
+	 */
+	protected function deleteValue($key)
+	{
+		return wincache_ucache_delete($key);
+	}
+
+	/**
+	 * Deletes all values from cache.
+	 * This is the implementation of the method declared in the parent class.
+	 * @return boolean whether the flush operation was successful.
+	 */
+	protected function flushValues()
+	{
+		return wincache_ucache_clear();
+	}
+}

+ 104 - 0
php-yii2/app/vendor/yiisoft/yii2/caching/XCache.php

@@ -0,0 +1,104 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\caching;
+
+/**
+ * XCache provides XCache caching in terms of an application component.
+ *
+ * To use this application component, the [XCache PHP extension](http://xcache.lighttpd.net/) must be loaded.
+ * Also note that the [[flush()]] functionality will work correctly only if "xcache.admin.enable_auth"
+ * is set to "Off" in php.ini.
+ *
+ * See [[Cache]] for common cache operations that XCache supports.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class XCache extends Cache
+{
+	/**
+	 * Checks whether a specified key exists in the cache.
+	 * This can be faster than getting the value from the cache if the data is big.
+	 * Note that this method does not check whether the dependency associated
+	 * with the cached data, if there is any, has changed. So a call to [[get]]
+	 * may return false while exists returns true.
+	 * @param mixed $key a key identifying the cached value. This can be a simple string or
+	 * a complex data structure consisting of factors representing the key.
+	 * @return boolean true if a value exists in cache, false if the value is not in the cache or expired.
+	 */
+	public function exists($key)
+	{
+		$key = $this->buildKey($key);
+		return xcache_isset($key);
+	}
+
+	/**
+	 * Retrieves a value from cache with a specified key.
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key a unique key identifying the cached value
+	 * @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
+	 */
+	protected function getValue($key)
+	{
+		return xcache_isset($key) ? xcache_get($key) : false;
+	}
+
+	/**
+	 * Stores a value identified by a key in cache.
+	 * This is the implementation of the method declared in the parent class.
+	 *
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	protected function setValue($key, $value, $expire)
+	{
+		return xcache_set($key, $value, $expire);
+	}
+
+	/**
+	 * Stores a value identified by a key into cache if the cache does not contain this key.
+	 * This is the implementation of the method declared in the parent class.
+	 *
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	protected function addValue($key, $value, $expire)
+	{
+		return !xcache_isset($key) ? $this->setValue($key, $value, $expire) : false;
+	}
+
+	/**
+	 * Deletes a value with the specified key from cache
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key the key of the value to be deleted
+	 * @return boolean if no error happens during deletion
+	 */
+	protected function deleteValue($key)
+	{
+		return xcache_unset($key);
+	}
+
+	/**
+	 * Deletes all values from cache.
+	 * This is the implementation of the method declared in the parent class.
+	 * @return boolean whether the flush operation was successful.
+	 */
+	protected function flushValues()
+	{
+		for ($i = 0, $max = xcache_count(XC_TYPE_VAR); $i < $max; $i++) {
+			if (xcache_clear_cache(XC_TYPE_VAR, $i) === false) {
+				return false;
+			}
+		}
+		return true;
+	}
+}

+ 83 - 0
php-yii2/app/vendor/yiisoft/yii2/caching/ZendDataCache.php

@@ -0,0 +1,83 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\caching;
+
+/**
+ * ZendDataCache provides Zend data caching in terms of an application component.
+ *
+ * To use this application component, the [Zend Data Cache PHP extension](http://www.zend.com/en/products/server/)
+ * must be loaded.
+ *
+ * See [[Cache]] for common cache operations that ZendDataCache supports.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class ZendDataCache extends Cache
+{
+	/**
+	 * Retrieves a value from cache with a specified key.
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key a unique key identifying the cached value
+	 * @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
+	 */
+	protected function getValue($key)
+	{
+		$result = zend_shm_cache_fetch($key);
+		return $result === null ? false : $result;
+	}
+
+	/**
+	 * Stores a value identified by a key in cache.
+	 * This is the implementation of the method declared in the parent class.
+	 *
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	protected function setValue($key, $value, $expire)
+	{
+		return zend_shm_cache_store($key, $value, $expire);
+	}
+
+	/**
+	 * Stores a value identified by a key into cache if the cache does not contain this key.
+	 * This is the implementation of the method declared in the parent class.
+	 *
+	 * @param string $key the key identifying the value to be cached
+	 * @param string $value the value to be cached
+	 * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
+	 * @return boolean true if the value is successfully stored into cache, false otherwise
+	 */
+	protected function addValue($key, $value, $expire)
+	{
+		return zend_shm_cache_fetch($key) === null ? $this->setValue($key, $value, $expire) : false;
+	}
+
+	/**
+	 * Deletes a value with the specified key from cache
+	 * This is the implementation of the method declared in the parent class.
+	 * @param string $key the key of the value to be deleted
+	 * @return boolean if no error happens during deletion
+	 */
+	protected function deleteValue($key)
+	{
+		return zend_shm_cache_delete($key);
+	}
+
+	/**
+	 * Deletes all values from cache.
+	 * This is the implementation of the method declared in the parent class.
+	 * @return boolean whether the flush operation was successful.
+	 */
+	protected function flushValues()
+	{
+		return zend_shm_cache_clear();
+	}
+}

+ 138 - 0
php-yii2/app/vendor/yiisoft/yii2/captcha/Captcha.php

@@ -0,0 +1,138 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\captcha;
+
+use Yii;
+use yii\base\InvalidConfigException;
+use yii\helpers\Html;
+use yii\helpers\Json;
+use yii\widgets\InputWidget;
+
+/**
+ * Captcha renders a CAPTCHA image and an input field that takes user-entered verification code.
+ *
+ * Captcha is used together with [[CaptchaAction]] provide [CAPTCHA](http://en.wikipedia.org/wiki/Captcha)
+ * - a way of preventing Website spamming.
+ *
+ * The image element rendered by Captcha will display a CAPTCHA image generated by
+ * an action whose route is specified by [[captchaAction]]. This action must be an instance of [[CaptchaAction]].
+ *
+ * When the user clicks on the CAPTCHA image, it will cause the CAPTCHA image
+ * to be refreshed with a new CAPTCHA.
+ *
+ * You may use [[\yii\validators\CaptchaValidator]] to validate the user input matches
+ * the current CAPTCHA verification code.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Captcha extends InputWidget
+{
+	/**
+	 * @var string the route of the action that generates the CAPTCHA images.
+	 * The action represented by this route must be an action of [[CaptchaAction]].
+	 */
+	public $captchaAction = 'site/captcha';
+	/**
+	 * @var array HTML attributes to be applied to the CAPTCHA image tag.
+	 */
+	public $imageOptions = [];
+	/**
+	 * @var string the template for arranging the CAPTCHA image tag and the text input tag.
+	 * In this template, the token `{image}` will be replaced with the actual image tag,
+	 * while `{input}` will be replaced with the text input tag.
+	 */
+	public $template = '{image} {input}';
+	/**
+	 * @var array the HTML attributes for the input tag.
+	 */
+	public $options = ['class' => 'form-control'];
+
+	/**
+	 * Initializes the widget.
+	 */
+	public function init()
+	{
+		parent::init();
+
+		$this->checkRequirements();
+
+		if (!isset($this->imageOptions['id'])) {
+			$this->imageOptions['id'] = $this->options['id'] . '-image';
+		}
+	}
+
+	/**
+	 * Renders the widget.
+	 */
+	public function run()
+	{
+		$this->registerClientScript();
+		if ($this->hasModel()) {
+			$input = Html::activeTextInput($this->model, $this->attribute, $this->options);
+		} else {
+			$input = Html::textInput($this->name, $this->value, $this->options);
+		}
+		$url = Yii::$app->getUrlManager()->createUrl($this->captchaAction, ['v' => uniqid()]);
+		$image = Html::img($url, $this->imageOptions);
+		echo strtr($this->template, [
+			'{input}' => $input,
+			'{image}' => $image,
+		]);
+	}
+
+	/**
+	 * Registers the needed JavaScript.
+	 */
+	public function registerClientScript()
+	{
+		$options = $this->getClientOptions();
+		$options = empty($options) ? '' : Json::encode($options);
+		$id = $this->imageOptions['id'];
+		$view = $this->getView();
+		CaptchaAsset::register($view);
+		$view->registerJs("jQuery('#$id').yiiCaptcha($options);");
+	}
+
+	/**
+	 * Returns the options for the captcha JS widget.
+	 * @return array the options
+	 */
+	protected function getClientOptions()
+	{
+		$options = [
+			'refreshUrl' => Html::url(['/' . $this->captchaAction, CaptchaAction::REFRESH_GET_VAR => 1]),
+			'hashKey' => "yiiCaptcha/{$this->captchaAction}",
+		];
+		return $options;
+	}
+
+	/**
+	 * Checks if there is graphic extension available to generate CAPTCHA images.
+	 * This method will check the existence of ImageMagick and GD extensions.
+	 * @return string the name of the graphic extension, either "imagick" or "gd".
+	 * @throws InvalidConfigException if neither ImageMagick nor GD is installed.
+	 */
+	public static function checkRequirements()
+	{
+		if (extension_loaded('imagick')) {
+			$imagick = new \Imagick();
+			$imagickFormats = $imagick->queryFormats('PNG');
+			if (in_array('PNG', $imagickFormats)) {
+				return 'imagick';
+			}
+		}
+		if (extension_loaded('gd')) {
+			$gdInfo = gd_info();
+			if (!empty($gdInfo['FreeType Support'])) {
+				return 'gd';
+			}
+		}
+		throw new InvalidConfigException('GD with FreeType or ImageMagick PHP extensions are required.');
+	}
+}

+ 341 - 0
php-yii2/app/vendor/yiisoft/yii2/captcha/CaptchaAction.php

@@ -0,0 +1,341 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\captcha;
+
+use Yii;
+use yii\base\Action;
+use yii\base\InvalidConfigException;
+
+/**
+ * CaptchaAction renders a CAPTCHA image.
+ *
+ * CaptchaAction is used together with [[Captcha]] and [[\yii\validators\CaptchaValidator]]
+ * to provide the [CAPTCHA](http://en.wikipedia.org/wiki/Captcha) feature.
+ *
+ * By configuring the properties of CaptchaAction, you may customize the appearance of
+ * the generated CAPTCHA images, such as the font color, the background color, etc.
+ *
+ * Note that CaptchaAction requires either GD2 extension or ImageMagick PHP extension.
+ *
+ * Using CAPTCHA involves the following steps:
+ *
+ * 1. Override [[\yii\web\Controller::actions()]] and register an action of class CaptchaAction with ID 'captcha'
+ * 2. In the form model, declare an attribute to store user-entered verification code, and declare the attribute
+ *    to be validated by the 'captcha' validator.
+ * 3. In the controller view, insert a [[Captcha]] widget in the form.
+ *
+ * @property string $verifyCode The verification code. This property is read-only.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class CaptchaAction extends Action
+{
+	/**
+	 * The name of the GET parameter indicating whether the CAPTCHA image should be regenerated.
+	 */
+	const REFRESH_GET_VAR = 'refresh';
+	/**
+	 * @var integer how many times should the same CAPTCHA be displayed. Defaults to 3.
+	 * A value less than or equal to 0 means the test is unlimited (available since version 1.1.2).
+	 */
+	public $testLimit = 3;
+	/**
+	 * @var integer the width of the generated CAPTCHA image. Defaults to 120.
+	 */
+	public $width = 120;
+	/**
+	 * @var integer the height of the generated CAPTCHA image. Defaults to 50.
+	 */
+	public $height = 50;
+	/**
+	 * @var integer padding around the text. Defaults to 2.
+	 */
+	public $padding = 2;
+	/**
+	 * @var integer the background color. For example, 0x55FF00.
+	 * Defaults to 0xFFFFFF, meaning white color.
+	 */
+	public $backColor = 0xFFFFFF;
+	/**
+	 * @var integer the font color. For example, 0x55FF00. Defaults to 0x2040A0 (blue color).
+	 */
+	public $foreColor = 0x2040A0;
+	/**
+	 * @var boolean whether to use transparent background. Defaults to false.
+	 */
+	public $transparent = false;
+	/**
+	 * @var integer the minimum length for randomly generated word. Defaults to 6.
+	 */
+	public $minLength = 6;
+	/**
+	 * @var integer the maximum length for randomly generated word. Defaults to 7.
+	 */
+	public $maxLength = 7;
+	/**
+	 * @var integer the offset between characters. Defaults to -2. You can adjust this property
+	 * in order to decrease or increase the readability of the captcha.
+	 **/
+	public $offset = -2;
+	/**
+	 * @var string the TrueType font file. This can be either a file path or path alias.
+	 */
+	public $fontFile = '@yii/captcha/SpicyRice.ttf';
+	/**
+	 * @var string the fixed verification code. When this property is set,
+	 * [[getVerifyCode()]] will always return the value of this property.
+	 * This is mainly used in automated tests where we want to be able to reproduce
+	 * the same verification code each time we run the tests.
+	 * If not set, it means the verification code will be randomly generated.
+	 */
+	public $fixedVerifyCode;
+
+
+	/**
+	 * Initializes the action.
+	 * @throws InvalidConfigException if the font file does not exist.
+	 */
+	public function init()
+	{
+		$this->fontFile = Yii::getAlias($this->fontFile);
+		if (!is_file($this->fontFile)) {
+			throw new InvalidConfigException("The font file does not exist: {$this->fontFile}");
+		}
+	}
+
+	/**
+	 * Runs the action.
+	 */
+	public function run()
+	{
+		if (isset($_GET[self::REFRESH_GET_VAR])) {
+			// AJAX request for regenerating code
+			$code = $this->getVerifyCode(true);
+			/** @var \yii\web\Controller $controller */
+			$controller = $this->controller;
+			return json_encode([
+				'hash1' => $this->generateValidationHash($code),
+				'hash2' => $this->generateValidationHash(strtolower($code)),
+				// we add a random 'v' parameter so that FireFox can refresh the image
+				// when src attribute of image tag is changed
+				'url' => $controller->createUrl($this->id, ['v' => uniqid()]),
+			]);
+		} else {
+			$this->setHttpHeaders();
+			return $this->renderImage($this->getVerifyCode());
+		}
+	}
+
+	/**
+	 * Generates a hash code that can be used for client side validation.
+	 * @param string $code the CAPTCHA code
+	 * @return string a hash code generated from the CAPTCHA code
+	 */
+	public function generateValidationHash($code)
+	{
+		for ($h = 0, $i = strlen($code) - 1; $i >= 0; --$i) {
+			$h += ord($code[$i]);
+		}
+		return $h;
+	}
+
+	/**
+	 * Gets the verification code.
+	 * @param boolean $regenerate whether the verification code should be regenerated.
+	 * @return string the verification code.
+	 */
+	public function getVerifyCode($regenerate = false)
+	{
+		if ($this->fixedVerifyCode !== null) {
+			return $this->fixedVerifyCode;
+		}
+
+		$session = Yii::$app->getSession();
+		$session->open();
+		$name = $this->getSessionKey();
+		if ($session[$name] === null || $regenerate) {
+			$session[$name] = $this->generateVerifyCode();
+			$session[$name . 'count'] = 1;
+		}
+		return $session[$name];
+	}
+
+	/**
+	 * Validates the input to see if it matches the generated code.
+	 * @param string $input user input
+	 * @param boolean $caseSensitive whether the comparison should be case-sensitive
+	 * @return boolean whether the input is valid
+	 */
+	public function validate($input, $caseSensitive)
+	{
+		$code = $this->getVerifyCode();
+		$valid = $caseSensitive ? ($input === $code) : strcasecmp($input, $code) === 0;
+		$session = Yii::$app->getSession();
+		$session->open();
+		$name = $this->getSessionKey() . 'count';
+		$session[$name] = $session[$name] + 1;
+		if ($valid || $session[$name] > $this->testLimit && $this->testLimit > 0) {
+			$this->getVerifyCode(true);
+		}
+		return $valid;
+	}
+
+	/**
+	 * Generates a new verification code.
+	 * @return string the generated verification code
+	 */
+	protected function generateVerifyCode()
+	{
+		if ($this->minLength > $this->maxLength) {
+			$this->maxLength = $this->minLength;
+		}
+		if ($this->minLength < 3) {
+			$this->minLength = 3;
+		}
+		if ($this->maxLength > 20) {
+			$this->maxLength = 20;
+		}
+		$length = mt_rand($this->minLength, $this->maxLength);
+
+		$letters = 'bcdfghjklmnpqrstvwxyz';
+		$vowels = 'aeiou';
+		$code = '';
+		for ($i = 0; $i < $length; ++$i) {
+			if ($i % 2 && mt_rand(0, 10) > 2 || !($i % 2) && mt_rand(0, 10) > 9) {
+				$code .= $vowels[mt_rand(0, 4)];
+			} else {
+				$code .= $letters[mt_rand(0, 20)];
+			}
+		}
+
+		return $code;
+	}
+
+	/**
+	 * Returns the session variable name used to store verification code.
+	 * @return string the session variable name
+	 */
+	protected function getSessionKey()
+	{
+		return '__captcha/' . $this->getUniqueId();
+	}
+
+	/**
+	 * Renders the CAPTCHA image.
+	 * @param string $code the verification code
+	 * @return string image contents
+	 */
+	protected function renderImage($code)
+	{
+		if (Captcha::checkRequirements() === 'gd') {
+			return $this->renderImageByGD($code);
+		} else {
+			return $this->renderImageByImagick($code);
+		}
+	}
+
+	/**
+	 * Renders the CAPTCHA image based on the code using GD library.
+	 * @param string $code the verification code
+	 * @return string image contents
+	 */
+	protected function renderImageByGD($code)
+	{
+		$image = imagecreatetruecolor($this->width, $this->height);
+
+		$backColor = imagecolorallocate($image,
+			(int)($this->backColor % 0x1000000 / 0x10000),
+			(int)($this->backColor % 0x10000 / 0x100),
+			$this->backColor % 0x100);
+		imagefilledrectangle($image, 0, 0, $this->width, $this->height, $backColor);
+		imagecolordeallocate($image, $backColor);
+
+		if ($this->transparent) {
+			imagecolortransparent($image, $backColor);
+		}
+
+		$foreColor = imagecolorallocate($image,
+			(int)($this->foreColor % 0x1000000 / 0x10000),
+			(int)($this->foreColor % 0x10000 / 0x100),
+			$this->foreColor % 0x100);
+
+		$length = strlen($code);
+		$box = imagettfbbox(30, 0, $this->fontFile, $code);
+		$w = $box[4] - $box[0] + $this->offset * ($length - 1);
+		$h = $box[1] - $box[5];
+		$scale = min(($this->width - $this->padding * 2) / $w, ($this->height - $this->padding * 2) / $h);
+		$x = 10;
+		$y = round($this->height * 27 / 40);
+		for ($i = 0; $i < $length; ++$i) {
+			$fontSize = (int)(rand(26, 32) * $scale * 0.8);
+			$angle = rand(-10, 10);
+			$letter = $code[$i];
+			$box = imagettftext($image, $fontSize, $angle, $x, $y, $foreColor, $this->fontFile, $letter);
+			$x = $box[2] + $this->offset;
+		}
+
+		imagecolordeallocate($image, $foreColor);
+
+		ob_start();
+		imagepng($image);
+		imagedestroy($image);
+		return ob_get_clean();
+	}
+
+	/**
+	 * Renders the CAPTCHA image based on the code using ImageMagick library.
+	 * @param string $code the verification code
+	 * @return \Imagick image instance. Can be used as string. In this case it will contain image contents.
+	 */
+	protected function renderImageByImagick($code)
+	{
+		$backColor = $this->transparent ? new \ImagickPixel('transparent') : new \ImagickPixel('#' . dechex($this->backColor));
+		$foreColor = new \ImagickPixel('#' . dechex($this->foreColor));
+
+		$image = new \Imagick();
+		$image->newImage($this->width, $this->height, $backColor);
+
+		$draw = new \ImagickDraw();
+		$draw->setFont($this->fontFile);
+		$draw->setFontSize(30);
+		$fontMetrics = $image->queryFontMetrics($draw, $code);
+
+		$length = strlen($code);
+		$w = (int)($fontMetrics['textWidth']) - 8 + $this->offset * ($length - 1);
+		$h = (int)($fontMetrics['textHeight']) - 8;
+		$scale = min(($this->width - $this->padding * 2) / $w, ($this->height - $this->padding * 2) / $h);
+		$x = 10;
+		$y = round($this->height * 27 / 40);
+		for ($i = 0; $i < $length; ++$i) {
+			$draw = new \ImagickDraw();
+			$draw->setFont($this->fontFile);
+			$draw->setFontSize((int)(rand(26, 32) * $scale * 0.8));
+			$draw->setFillColor($foreColor);
+			$image->annotateImage($draw, $x, $y, rand(-10, 10), $code[$i]);
+			$fontMetrics = $image->queryFontMetrics($draw, $code[$i]);
+			$x += (int)($fontMetrics['textWidth']) + $this->offset;
+		}
+
+		$image->setImageFormat('png');
+		return $image;
+	}
+
+	/**
+	 * Sets the HTTP headers needed by image response.
+	 */
+	protected function setHttpHeaders()
+	{
+		Yii::$app->getResponse()->getHeaders()
+			->set('Pragma', 'public')
+			->set('Expires', '0')
+			->set('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
+			->set('Content-Transfer-Encoding', 'binary')
+			->set('Content-type', 'image/png');
+	}
+}

+ 27 - 0
php-yii2/app/vendor/yiisoft/yii2/captcha/CaptchaAsset.php

@@ -0,0 +1,27 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\captcha;
+
+use yii\web\AssetBundle;
+
+/**
+ * This asset bundle provides the javascript files needed for the [[Captcha]] widget.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class CaptchaAsset extends AssetBundle
+{
+	public $sourcePath = '@yii/assets';
+	public $js = [
+		'yii.captcha.js',
+	];
+	public $depends = [
+		'yii\web\YiiAsset',
+	];
+}

+ 106 - 0
php-yii2/app/vendor/yiisoft/yii2/captcha/CaptchaValidator.php

@@ -0,0 +1,106 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\captcha;
+
+use Yii;
+use yii\base\InvalidConfigException;
+use yii\validators\ValidationAsset;
+use yii\validators\Validator;
+
+/**
+ * CaptchaValidator validates that the attribute value is the same as the verification code displayed in the CAPTCHA.
+ *
+ * CaptchaValidator should be used together with [[CaptchaAction]].
+ *
+ * Note that once CAPTCHA validation succeeds, a new CAPTCHA will be generated automatically. As a result,
+ * CAPTCHA validation should not be used in AJAX validation mode because it may fail the validation
+ * even if a user enters the same code as shown in the CAPTCHA image which is actually different from the latest CAPTCHA code.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class CaptchaValidator extends Validator
+{
+	/**
+	 * @var boolean whether to skip this validator if the input is empty.
+	 */
+	public $skipOnEmpty = false;
+	/**
+	 * @var boolean whether the comparison is case sensitive. Defaults to false.
+	 */
+	public $caseSensitive = false;
+	/**
+	 * @var string the route of the controller action that renders the CAPTCHA image.
+	 */
+	public $captchaAction = 'site/captcha';
+
+
+	/**
+	 * @inheritdoc
+	 */
+	public function init()
+	{
+		parent::init();
+		if ($this->message === null) {
+			$this->message = Yii::t('yii', 'The verification code is incorrect.');
+		}
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	protected function validateValue($value)
+	{
+		$captcha = $this->createCaptchaAction();
+		$valid = !is_array($value) && $captcha->validate($value, $this->caseSensitive);
+		return $valid ? null : [$this->message, []];
+	}
+
+	/**
+	 * Creates the CAPTCHA action object from the route specified by [[captchaAction]].
+	 * @return \yii\captcha\CaptchaAction the action object
+	 * @throws InvalidConfigException
+	 */
+	public function createCaptchaAction()
+	{
+		$ca = Yii::$app->createController($this->captchaAction);
+		if ($ca !== false) {
+			/** @var \yii\base\Controller $controller */
+			list($controller, $actionID) = $ca;
+			$action = $controller->createAction($actionID);
+			if ($action !== null) {
+				return $action;
+			}
+		}
+		throw new InvalidConfigException('Invalid CAPTCHA action ID: ' . $this->captchaAction);
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function clientValidateAttribute($object, $attribute, $view)
+	{
+		$captcha = $this->createCaptchaAction();
+		$code = $captcha->getVerifyCode(false);
+		$hash = $captcha->generateValidationHash($this->caseSensitive ? $code : strtolower($code));
+		$options = [
+			'hash' => $hash,
+			'hashKey' => 'yiiCaptcha/' . $this->captchaAction,
+			'caseSensitive' => $this->caseSensitive,
+			'message' => strtr($this->message, [
+				'{attribute}' => $object->getAttributeLabel($attribute),
+			]),
+		];
+		if ($this->skipOnEmpty) {
+			$options['skipOnEmpty'] = 1;
+		}
+
+		ValidationAsset::register($view);
+		return 'yii.validation.captcha(value, messages, ' . json_encode($options) . ');';
+	}
+}

+ 11 - 0
php-yii2/app/vendor/yiisoft/yii2/captcha/SpicyRice.md

@@ -0,0 +1,11 @@
+## Spicy Rice font
+
+* **Author:** Brian J. Bonislawsky, Astigmatic (AOETI, Astigmatic One Eye Typographic Institute)
+* **License:** SIL Open Font License (OFL), version 1.1, [notes and FAQ](http://scripts.sil.org/OFL)
+
+## Links
+
+* [Astigmatic](http://www.astigmatic.com/)
+* [Google WebFonts](http://www.google.com/webfonts/specimen/Spicy+Rice)
+* [fontsquirrel.com](http://www.fontsquirrel.com/fonts/spicy-rice)
+* [fontspace.com](http://www.fontspace.com/astigmatic-one-eye-typographic-institute/spicy-rice)

BIN
php-yii2/app/vendor/yiisoft/yii2/captcha/SpicyRice.ttf


+ 253 - 0
php-yii2/app/vendor/yiisoft/yii2/classes.php

@@ -0,0 +1,253 @@
+<?php
+/**
+ * Yii core class map.
+ *
+ * This file is automatically generated by the "build classmap" command under the "build" folder.
+ * Do not modify it directly.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+return [
+	'yii\base\Action' => YII_PATH . '/base/Action.php',
+	'yii\base\ActionEvent' => YII_PATH . '/base/ActionEvent.php',
+	'yii\base\ActionFilter' => YII_PATH . '/base/ActionFilter.php',
+	'yii\base\Application' => YII_PATH . '/base/Application.php',
+	'yii\base\Arrayable' => YII_PATH . '/base/Arrayable.php',
+	'yii\base\Behavior' => YII_PATH . '/base/Behavior.php',
+	'yii\base\Component' => YII_PATH . '/base/Component.php',
+	'yii\base\Controller' => YII_PATH . '/base/Controller.php',
+	'yii\base\ErrorException' => YII_PATH . '/base/ErrorException.php',
+	'yii\base\ErrorHandler' => YII_PATH . '/base/ErrorHandler.php',
+	'yii\base\Event' => YII_PATH . '/base/Event.php',
+	'yii\base\Exception' => YII_PATH . '/base/Exception.php',
+	'yii\base\Extension' => YII_PATH . '/base/Extension.php',
+	'yii\base\Formatter' => YII_PATH . '/base/Formatter.php',
+	'yii\base\InlineAction' => YII_PATH . '/base/InlineAction.php',
+	'yii\base\InvalidCallException' => YII_PATH . '/base/InvalidCallException.php',
+	'yii\base\InvalidConfigException' => YII_PATH . '/base/InvalidConfigException.php',
+	'yii\base\InvalidParamException' => YII_PATH . '/base/InvalidParamException.php',
+	'yii\base\InvalidRouteException' => YII_PATH . '/base/InvalidRouteException.php',
+	'yii\base\MailEvent' => YII_PATH . '/base/MailEvent.php',
+	'yii\base\Model' => YII_PATH . '/base/Model.php',
+	'yii\base\ModelEvent' => YII_PATH . '/base/ModelEvent.php',
+	'yii\base\Module' => YII_PATH . '/base/Module.php',
+	'yii\base\NotSupportedException' => YII_PATH . '/base/NotSupportedException.php',
+	'yii\base\Object' => YII_PATH . '/base/Object.php',
+	'yii\base\Request' => YII_PATH . '/base/Request.php',
+	'yii\base\Response' => YII_PATH . '/base/Response.php',
+	'yii\base\Theme' => YII_PATH . '/base/Theme.php',
+	'yii\base\UnknownClassException' => YII_PATH . '/base/UnknownClassException.php',
+	'yii\base\UnknownMethodException' => YII_PATH . '/base/UnknownMethodException.php',
+	'yii\base\UnknownPropertyException' => YII_PATH . '/base/UnknownPropertyException.php',
+	'yii\base\UserException' => YII_PATH . '/base/UserException.php',
+	'yii\base\View' => YII_PATH . '/base/View.php',
+	'yii\base\ViewContextInterface' => YII_PATH . '/base/ViewContextInterface.php',
+	'yii\base\ViewEvent' => YII_PATH . '/base/ViewEvent.php',
+	'yii\base\ViewRenderer' => YII_PATH . '/base/ViewRenderer.php',
+	'yii\base\Widget' => YII_PATH . '/base/Widget.php',
+	'yii\behaviors\AutoTimestamp' => YII_PATH . '/behaviors/AutoTimestamp.php',
+	'yii\caching\ApcCache' => YII_PATH . '/caching/ApcCache.php',
+	'yii\caching\Cache' => YII_PATH . '/caching/Cache.php',
+	'yii\caching\ChainedDependency' => YII_PATH . '/caching/ChainedDependency.php',
+	'yii\caching\DbCache' => YII_PATH . '/caching/DbCache.php',
+	'yii\caching\DbDependency' => YII_PATH . '/caching/DbDependency.php',
+	'yii\caching\Dependency' => YII_PATH . '/caching/Dependency.php',
+	'yii\caching\DummyCache' => YII_PATH . '/caching/DummyCache.php',
+	'yii\caching\ExpressionDependency' => YII_PATH . '/caching/ExpressionDependency.php',
+	'yii\caching\FileCache' => YII_PATH . '/caching/FileCache.php',
+	'yii\caching\FileDependency' => YII_PATH . '/caching/FileDependency.php',
+	'yii\caching\GroupDependency' => YII_PATH . '/caching/GroupDependency.php',
+	'yii\caching\MemCache' => YII_PATH . '/caching/MemCache.php',
+	'yii\caching\MemCacheServer' => YII_PATH . '/caching/MemCacheServer.php',
+	'yii\caching\WinCache' => YII_PATH . '/caching/WinCache.php',
+	'yii\caching\XCache' => YII_PATH . '/caching/XCache.php',
+	'yii\caching\ZendDataCache' => YII_PATH . '/caching/ZendDataCache.php',
+	'yii\captcha\Captcha' => YII_PATH . '/captcha/Captcha.php',
+	'yii\captcha\CaptchaAction' => YII_PATH . '/captcha/CaptchaAction.php',
+	'yii\captcha\CaptchaAsset' => YII_PATH . '/captcha/CaptchaAsset.php',
+	'yii\captcha\CaptchaValidator' => YII_PATH . '/captcha/CaptchaValidator.php',
+	'yii\data\ActiveDataProvider' => YII_PATH . '/data/ActiveDataProvider.php',
+	'yii\data\ArrayDataProvider' => YII_PATH . '/data/ArrayDataProvider.php',
+	'yii\data\BaseDataProvider' => YII_PATH . '/data/BaseDataProvider.php',
+	'yii\data\DataProviderInterface' => YII_PATH . '/data/DataProviderInterface.php',
+	'yii\data\Pagination' => YII_PATH . '/data/Pagination.php',
+	'yii\data\Sort' => YII_PATH . '/data/Sort.php',
+	'yii\data\SqlDataProvider' => YII_PATH . '/data/SqlDataProvider.php',
+	'yii\db\ActiveQuery' => YII_PATH . '/db/ActiveQuery.php',
+	'yii\db\ActiveQueryInterface' => YII_PATH . '/db/ActiveQueryInterface.php',
+	'yii\db\ActiveQueryTrait' => YII_PATH . '/db/ActiveQueryTrait.php',
+	'yii\db\ActiveRecord' => YII_PATH . '/db/ActiveRecord.php',
+	'yii\db\ActiveRecordInterface' => YII_PATH . '/db/ActiveRecordInterface.php',
+	'yii\db\ActiveRelation' => YII_PATH . '/db/ActiveRelation.php',
+	'yii\db\ActiveRelationInterface' => YII_PATH . '/db/ActiveRelationInterface.php',
+	'yii\db\ActiveRelationTrait' => YII_PATH . '/db/ActiveRelationTrait.php',
+	'yii\db\BaseActiveRecord' => YII_PATH . '/db/BaseActiveRecord.php',
+	'yii\db\ColumnSchema' => YII_PATH . '/db/ColumnSchema.php',
+	'yii\db\Command' => YII_PATH . '/db/Command.php',
+	'yii\db\Connection' => YII_PATH . '/db/Connection.php',
+	'yii\db\DataReader' => YII_PATH . '/db/DataReader.php',
+	'yii\db\Exception' => YII_PATH . '/db/Exception.php',
+	'yii\db\Expression' => YII_PATH . '/db/Expression.php',
+	'yii\db\Migration' => YII_PATH . '/db/Migration.php',
+	'yii\db\Query' => YII_PATH . '/db/Query.php',
+	'yii\db\QueryBuilder' => YII_PATH . '/db/QueryBuilder.php',
+	'yii\db\QueryInterface' => YII_PATH . '/db/QueryInterface.php',
+	'yii\db\QueryTrait' => YII_PATH . '/db/QueryTrait.php',
+	'yii\db\Schema' => YII_PATH . '/db/Schema.php',
+	'yii\db\StaleObjectException' => YII_PATH . '/db/StaleObjectException.php',
+	'yii\db\TableSchema' => YII_PATH . '/db/TableSchema.php',
+	'yii\db\Transaction' => YII_PATH . '/db/Transaction.php',
+	'yii\db\cubrid\QueryBuilder' => YII_PATH . '/db/cubrid/QueryBuilder.php',
+	'yii\db\cubrid\Schema' => YII_PATH . '/db/cubrid/Schema.php',
+	'yii\db\mssql\PDO' => YII_PATH . '/db/mssql/PDO.php',
+	'yii\db\mssql\QueryBuilder' => YII_PATH . '/db/mssql/QueryBuilder.php',
+	'yii\db\mssql\Schema' => YII_PATH . '/db/mssql/Schema.php',
+	'yii\db\mssql\SqlsrvPDO' => YII_PATH . '/db/mssql/SqlsrvPDO.php',
+	'yii\db\mssql\TableSchema' => YII_PATH . '/db/mssql/TableSchema.php',
+	'yii\db\mysql\QueryBuilder' => YII_PATH . '/db/mysql/QueryBuilder.php',
+	'yii\db\mysql\Schema' => YII_PATH . '/db/mysql/Schema.php',
+	'yii\db\oci\QueryBuilder' => YII_PATH . '/db/oci/QueryBuilder.php',
+	'yii\db\oci\Schema' => YII_PATH . '/db/oci/Schema.php',
+	'yii\db\pgsql\QueryBuilder' => YII_PATH . '/db/pgsql/QueryBuilder.php',
+	'yii\db\pgsql\Schema' => YII_PATH . '/db/pgsql/Schema.php',
+	'yii\db\sqlite\QueryBuilder' => YII_PATH . '/db/sqlite/QueryBuilder.php',
+	'yii\db\sqlite\Schema' => YII_PATH . '/db/sqlite/Schema.php',
+	'yii\grid\ActionColumn' => YII_PATH . '/grid/ActionColumn.php',
+	'yii\grid\CheckboxColumn' => YII_PATH . '/grid/CheckboxColumn.php',
+	'yii\grid\Column' => YII_PATH . '/grid/Column.php',
+	'yii\grid\DataColumn' => YII_PATH . '/grid/DataColumn.php',
+	'yii\grid\GridView' => YII_PATH . '/grid/GridView.php',
+	'yii\grid\GridViewAsset' => YII_PATH . '/grid/GridViewAsset.php',
+	'yii\grid\SerialColumn' => YII_PATH . '/grid/SerialColumn.php',
+	'yii\helpers\ArrayHelper' => YII_PATH . '/helpers/ArrayHelper.php',
+	'yii\helpers\BaseArrayHelper' => YII_PATH . '/helpers/BaseArrayHelper.php',
+	'yii\helpers\BaseConsole' => YII_PATH . '/helpers/BaseConsole.php',
+	'yii\helpers\BaseFileHelper' => YII_PATH . '/helpers/BaseFileHelper.php',
+	'yii\helpers\BaseHtml' => YII_PATH . '/helpers/BaseHtml.php',
+	'yii\helpers\BaseHtmlPurifier' => YII_PATH . '/helpers/BaseHtmlPurifier.php',
+	'yii\helpers\BaseInflector' => YII_PATH . '/helpers/BaseInflector.php',
+	'yii\helpers\BaseJson' => YII_PATH . '/helpers/BaseJson.php',
+	'yii\helpers\BaseMarkdown' => YII_PATH . '/helpers/BaseMarkdown.php',
+	'yii\helpers\BaseSecurity' => YII_PATH . '/helpers/BaseSecurity.php',
+	'yii\helpers\BaseStringHelper' => YII_PATH . '/helpers/BaseStringHelper.php',
+	'yii\helpers\BaseVarDumper' => YII_PATH . '/helpers/BaseVarDumper.php',
+	'yii\helpers\Console' => YII_PATH . '/helpers/Console.php',
+	'yii\helpers\FileHelper' => YII_PATH . '/helpers/FileHelper.php',
+	'yii\helpers\Html' => YII_PATH . '/helpers/Html.php',
+	'yii\helpers\HtmlPurifier' => YII_PATH . '/helpers/HtmlPurifier.php',
+	'yii\helpers\Inflector' => YII_PATH . '/helpers/Inflector.php',
+	'yii\helpers\Json' => YII_PATH . '/helpers/Json.php',
+	'yii\helpers\Markdown' => YII_PATH . '/helpers/Markdown.php',
+	'yii\helpers\Security' => YII_PATH . '/helpers/Security.php',
+	'yii\helpers\StringHelper' => YII_PATH . '/helpers/StringHelper.php',
+	'yii\helpers\VarDumper' => YII_PATH . '/helpers/VarDumper.php',
+	'yii\i18n\DbMessageSource' => YII_PATH . '/i18n/DbMessageSource.php',
+	'yii\i18n\Formatter' => YII_PATH . '/i18n/Formatter.php',
+	'yii\i18n\GettextFile' => YII_PATH . '/i18n/GettextFile.php',
+	'yii\i18n\GettextMessageSource' => YII_PATH . '/i18n/GettextMessageSource.php',
+	'yii\i18n\GettextMoFile' => YII_PATH . '/i18n/GettextMoFile.php',
+	'yii\i18n\GettextPoFile' => YII_PATH . '/i18n/GettextPoFile.php',
+	'yii\i18n\I18N' => YII_PATH . '/i18n/I18N.php',
+	'yii\i18n\MessageFormatter' => YII_PATH . '/i18n/MessageFormatter.php',
+	'yii\i18n\MessageSource' => YII_PATH . '/i18n/MessageSource.php',
+	'yii\i18n\MissingTranslationEvent' => YII_PATH . '/i18n/MissingTranslationEvent.php',
+	'yii\i18n\PhpMessageSource' => YII_PATH . '/i18n/PhpMessageSource.php',
+	'yii\log\DbTarget' => YII_PATH . '/log/DbTarget.php',
+	'yii\log\EmailTarget' => YII_PATH . '/log/EmailTarget.php',
+	'yii\log\FileTarget' => YII_PATH . '/log/FileTarget.php',
+	'yii\log\Logger' => YII_PATH . '/log/Logger.php',
+	'yii\log\Target' => YII_PATH . '/log/Target.php',
+	'yii\mail\BaseMailer' => YII_PATH . '/mail/BaseMailer.php',
+	'yii\mail\BaseMessage' => YII_PATH . '/mail/BaseMessage.php',
+	'yii\mail\MailerInterface' => YII_PATH . '/mail/MailerInterface.php',
+	'yii\mail\MessageInterface' => YII_PATH . '/mail/MessageInterface.php',
+	'yii\mutex\DbMutex' => YII_PATH . '/mutex/DbMutex.php',
+	'yii\mutex\FileMutex' => YII_PATH . '/mutex/FileMutex.php',
+	'yii\mutex\Mutex' => YII_PATH . '/mutex/Mutex.php',
+	'yii\mutex\MysqlMutex' => YII_PATH . '/mutex/MysqlMutex.php',
+	'yii\rbac\Assignment' => YII_PATH . '/rbac/Assignment.php',
+	'yii\rbac\DbManager' => YII_PATH . '/rbac/DbManager.php',
+	'yii\rbac\Item' => YII_PATH . '/rbac/Item.php',
+	'yii\rbac\Manager' => YII_PATH . '/rbac/Manager.php',
+	'yii\rbac\PhpManager' => YII_PATH . '/rbac/PhpManager.php',
+	'yii\requirements\YiiRequirementChecker' => YII_PATH . '/requirements/YiiRequirementChecker.php',
+	'yii\validators\BooleanValidator' => YII_PATH . '/validators/BooleanValidator.php',
+	'yii\validators\CompareValidator' => YII_PATH . '/validators/CompareValidator.php',
+	'yii\validators\DateValidator' => YII_PATH . '/validators/DateValidator.php',
+	'yii\validators\DefaultValueValidator' => YII_PATH . '/validators/DefaultValueValidator.php',
+	'yii\validators\EmailValidator' => YII_PATH . '/validators/EmailValidator.php',
+	'yii\validators\ExistValidator' => YII_PATH . '/validators/ExistValidator.php',
+	'yii\validators\FileValidator' => YII_PATH . '/validators/FileValidator.php',
+	'yii\validators\FilterValidator' => YII_PATH . '/validators/FilterValidator.php',
+	'yii\validators\ImageValidator' => YII_PATH . '/validators/ImageValidator.php',
+	'yii\validators\InlineValidator' => YII_PATH . '/validators/InlineValidator.php',
+	'yii\validators\NumberValidator' => YII_PATH . '/validators/NumberValidator.php',
+	'yii\validators\PunycodeAsset' => YII_PATH . '/validators/PunycodeAsset.php',
+	'yii\validators\RangeValidator' => YII_PATH . '/validators/RangeValidator.php',
+	'yii\validators\RegularExpressionValidator' => YII_PATH . '/validators/RegularExpressionValidator.php',
+	'yii\validators\RequiredValidator' => YII_PATH . '/validators/RequiredValidator.php',
+	'yii\validators\SafeValidator' => YII_PATH . '/validators/SafeValidator.php',
+	'yii\validators\StringValidator' => YII_PATH . '/validators/StringValidator.php',
+	'yii\validators\UniqueValidator' => YII_PATH . '/validators/UniqueValidator.php',
+	'yii\validators\UrlValidator' => YII_PATH . '/validators/UrlValidator.php',
+	'yii\validators\ValidationAsset' => YII_PATH . '/validators/ValidationAsset.php',
+	'yii\validators\Validator' => YII_PATH . '/validators/Validator.php',
+	'yii\web\AccessControl' => YII_PATH . '/web/AccessControl.php',
+	'yii\web\AccessDeniedHttpException' => YII_PATH . '/web/AccessDeniedHttpException.php',
+	'yii\web\AccessRule' => YII_PATH . '/web/AccessRule.php',
+	'yii\web\Application' => YII_PATH . '/web/Application.php',
+	'yii\web\AssetBundle' => YII_PATH . '/web/AssetBundle.php',
+	'yii\web\AssetConverter' => YII_PATH . '/web/AssetConverter.php',
+	'yii\web\AssetConverterInterface' => YII_PATH . '/web/AssetConverterInterface.php',
+	'yii\web\AssetManager' => YII_PATH . '/web/AssetManager.php',
+	'yii\web\BadRequestHttpException' => YII_PATH . '/web/BadRequestHttpException.php',
+	'yii\web\CacheSession' => YII_PATH . '/web/CacheSession.php',
+	'yii\web\Controller' => YII_PATH . '/web/Controller.php',
+	'yii\web\Cookie' => YII_PATH . '/web/Cookie.php',
+	'yii\web\CookieCollection' => YII_PATH . '/web/CookieCollection.php',
+	'yii\web\DbSession' => YII_PATH . '/web/DbSession.php',
+	'yii\web\ErrorAction' => YII_PATH . '/web/ErrorAction.php',
+	'yii\web\HeaderCollection' => YII_PATH . '/web/HeaderCollection.php',
+	'yii\web\HttpCache' => YII_PATH . '/web/HttpCache.php',
+	'yii\web\HttpException' => YII_PATH . '/web/HttpException.php',
+	'yii\web\IdentityInterface' => YII_PATH . '/web/IdentityInterface.php',
+	'yii\web\JqueryAsset' => YII_PATH . '/web/JqueryAsset.php',
+	'yii\web\JsExpression' => YII_PATH . '/web/JsExpression.php',
+	'yii\web\MethodNotAllowedHttpException' => YII_PATH . '/web/MethodNotAllowedHttpException.php',
+	'yii\web\NotFoundHttpException' => YII_PATH . '/web/NotFoundHttpException.php',
+	'yii\web\PageCache' => YII_PATH . '/web/PageCache.php',
+	'yii\web\Request' => YII_PATH . '/web/Request.php',
+	'yii\web\Response' => YII_PATH . '/web/Response.php',
+	'yii\web\ResponseFormatterInterface' => YII_PATH . '/web/ResponseFormatterInterface.php',
+	'yii\web\Session' => YII_PATH . '/web/Session.php',
+	'yii\web\SessionIterator' => YII_PATH . '/web/SessionIterator.php',
+	'yii\web\UploadedFile' => YII_PATH . '/web/UploadedFile.php',
+	'yii\web\UrlManager' => YII_PATH . '/web/UrlManager.php',
+	'yii\web\UrlRule' => YII_PATH . '/web/UrlRule.php',
+	'yii\web\User' => YII_PATH . '/web/User.php',
+	'yii\web\UserEvent' => YII_PATH . '/web/UserEvent.php',
+	'yii\web\VerbFilter' => YII_PATH . '/web/VerbFilter.php',
+	'yii\web\View' => YII_PATH . '/web/View.php',
+	'yii\web\XmlResponseFormatter' => YII_PATH . '/web/XmlResponseFormatter.php',
+	'yii\web\YiiAsset' => YII_PATH . '/web/YiiAsset.php',
+	'yii\widgets\ActiveField' => YII_PATH . '/widgets/ActiveField.php',
+	'yii\widgets\ActiveForm' => YII_PATH . '/widgets/ActiveForm.php',
+	'yii\widgets\ActiveFormAsset' => YII_PATH . '/widgets/ActiveFormAsset.php',
+	'yii\widgets\BaseListView' => YII_PATH . '/widgets/BaseListView.php',
+	'yii\widgets\Block' => YII_PATH . '/widgets/Block.php',
+	'yii\widgets\Breadcrumbs' => YII_PATH . '/widgets/Breadcrumbs.php',
+	'yii\widgets\ContentDecorator' => YII_PATH . '/widgets/ContentDecorator.php',
+	'yii\widgets\DetailView' => YII_PATH . '/widgets/DetailView.php',
+	'yii\widgets\FragmentCache' => YII_PATH . '/widgets/FragmentCache.php',
+	'yii\widgets\InputWidget' => YII_PATH . '/widgets/InputWidget.php',
+	'yii\widgets\LinkPager' => YII_PATH . '/widgets/LinkPager.php',
+	'yii\widgets\LinkSorter' => YII_PATH . '/widgets/LinkSorter.php',
+	'yii\widgets\ListView' => YII_PATH . '/widgets/ListView.php',
+	'yii\widgets\MaskedInput' => YII_PATH . '/widgets/MaskedInput.php',
+	'yii\widgets\MaskedInputAsset' => YII_PATH . '/widgets/MaskedInputAsset.php',
+	'yii\widgets\Menu' => YII_PATH . '/widgets/Menu.php',
+	'yii\widgets\Spaceless' => YII_PATH . '/widgets/Spaceless.php',
+];

+ 64 - 0
php-yii2/app/vendor/yiisoft/yii2/composer.json

@@ -0,0 +1,64 @@
+{
+	"name": "yiisoft/yii2",
+	"description": "Yii PHP Framework Version 2",
+	"keywords": ["yii", "framework"],
+	"homepage": "http://www.yiiframework.com/",
+	"type": "library",
+	"license": "BSD-3-Clause",
+	"authors": [
+		{
+			"name": "Qiang Xue",
+			"email": "[email protected]",
+			"homepage": "http://www.yiiframework.com/",
+			"role": "Founder and project lead"
+		},
+		{
+			"name": "Alexander Makarov",
+			"email": "[email protected]",
+			"homepage": "http://rmcreative.ru/",
+			"role": "Core framework development"
+		},
+		{
+			"name": "Maurizio Domba",
+			"homepage": "http://mdomba.info/",
+			"role": "Core framework development"
+		},
+		{
+			"name": "Carsten Brandt",
+			"email": "[email protected]",
+			"homepage": "http://cebe.cc/",
+			"role": "Core framework development"
+		},
+		{
+			"name": "Timur Ruziev",
+			"email": "[email protected]",
+			"homepage": "http://resurtm.com/",
+			"role": "Core framework development"
+		},
+		{
+			"name": "Paul Klimov",
+			"email": "[email protected]",
+			"role": "Core framework development"
+		}
+	],
+	"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"
+	},
+	"require": {
+		"php": ">=5.4.0",
+		"ext-mbstring": "*",
+		"lib-pcre": "*",
+		"yiisoft/yii2-composer": "*",
+		"yiisoft/jquery": "2.0.*",
+		"phpspec/php-diff": ">=1.0.2",
+		"ezyang/htmlpurifier": "4.6.*",
+		"michelf/php-markdown": "1.3.*"
+	},
+	"autoload": {
+		"psr-4": { "yii\\": "" }
+	}
+}

+ 165 - 0
php-yii2/app/vendor/yiisoft/yii2/console/Application.php

@@ -0,0 +1,165 @@
+<?php
+/**
+ * Console Application class file.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\console;
+
+use Yii;
+use yii\base\InvalidRouteException;
+
+/**
+ * Application represents a console application.
+ *
+ * Application extends from [[yii\base\Application]] by providing functionalities that are
+ * specific to console requests. In particular, it deals with console requests
+ * through a command-based approach:
+ *
+ * - A console application consists of one or several possible user commands;
+ * - Each user command is implemented as a class extending [[\yii\console\Controller]];
+ * - User specifies which command to run on the command line;
+ * - The command processes the user request with the specified parameters.
+ *
+ * The command classes reside in the directory specified by [[controllerPath]].
+ * Their naming should follow the same naming convention as controllers. For example, the `help` command
+ * is implemented using the `HelpController` class.
+ *
+ * To run the console application, enter the following on the command line:
+ *
+ * ~~~
+ * yii <route> [--param1=value1 --param2 ...]
+ * ~~~
+ *
+ * where `<route>` refers to a controller route in the form of `ModuleID/ControllerID/ActionID`
+ * (e.g. `sitemap/create`), and `param1`, `param2` refers to a set of named parameters that
+ * will be used to initialize the controller action (e.g. `--since=0` specifies a `since` parameter
+ * whose value is 0 and a corresponding `$since` parameter is passed to the action method).
+ *
+ * A `help` command is provided by default, which lists available commands and shows their usage.
+ * To use this command, simply type:
+ *
+ * ~~~
+ * yii help
+ * ~~~
+ *
+ * @property Response $response The response component. This property is read-only.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Application extends \yii\base\Application
+{
+	/**
+	 * @var string the default route of this application. Defaults to 'help',
+	 * meaning the `help` command.
+	 */
+	public $defaultRoute = 'help';
+	/**
+	 * @var boolean whether to enable the commands provided by the core framework.
+	 * Defaults to true.
+	 */
+	public $enableCoreCommands = true;
+	/**
+	 * @var Controller the currently active controller instance
+	 */
+	public $controller;
+
+	/**
+	 * Initialize the application.
+	 */
+	public function init()
+	{
+		parent::init();
+		if ($this->enableCoreCommands) {
+			foreach ($this->coreCommands() as $id => $command) {
+				if (!isset($this->controllerMap[$id])) {
+					$this->controllerMap[$id] = $command;
+				}
+			}
+		}
+		// ensure we have the 'help' command so that we can list the available commands
+		if (!isset($this->controllerMap['help'])) {
+			$this->controllerMap['help'] = 'yii\console\controllers\HelpController';
+		}
+	}
+
+	/**
+	 * Handles the specified request.
+	 * @param Request $request the request to be handled
+	 * @return Response the resulting response
+	 */
+	public function handleRequest($request)
+	{
+		list ($route, $params) = $request->resolve();
+		$this->requestedRoute = $route;
+		$result = $this->runAction($route, $params);
+		if ($result instanceof Response) {
+			return $result;
+		} else {
+			$response = $this->getResponse();
+			$response->exitStatus = (int)$result;
+			return $response;
+		}
+	}
+
+	/**
+	 * Returns the response component.
+	 * @return Response the response component
+	 */
+	public function getResponse()
+	{
+		return $this->getComponent('response');
+	}
+
+	/**
+	 * Runs a controller action specified by a route.
+	 * This method parses the specified route and creates the corresponding child module(s), controller and action
+	 * instances. It then calls [[Controller::runAction()]] to run the action with the given parameters.
+	 * If the route is empty, the method will use [[defaultRoute]].
+	 * @param string $route the route that specifies the action.
+	 * @param array $params the parameters to be passed to the action
+	 * @return integer the status code returned by the action execution. 0 means normal, and other values mean abnormal.
+	 * @throws Exception if the route is invalid
+	 */
+	public function runAction($route, $params = [])
+	{
+		try {
+			return parent::runAction($route, $params);
+		} catch (InvalidRouteException $e) {
+			throw new Exception(Yii::t('yii', 'Unknown command "{command}".', ['command' => $route]), 0, $e);
+		}
+	}
+
+	/**
+	 * Returns the configuration of the built-in commands.
+	 * @return array the configuration of the built-in commands.
+	 */
+	public function coreCommands()
+	{
+		return [
+			'message' => 'yii\console\controllers\MessageController',
+			'help' => 'yii\console\controllers\HelpController',
+			'migrate' => 'yii\console\controllers\MigrateController',
+			'cache' => 'yii\console\controllers\CacheController',
+			'asset' => 'yii\console\controllers\AssetController',
+			'fixture' => 'yii\console\controllers\FixtureController',
+		];
+	}
+
+	/**
+	 * Registers the core application components.
+	 * @see setComponents
+	 */
+	public function registerCoreComponents()
+	{
+		parent::registerCoreComponents();
+		$this->setComponents([
+			'request' => ['class' => 'yii\console\Request'],
+			'response' => ['class' => 'yii\console\Response'],
+		]);
+	}
+}

+ 276 - 0
php-yii2/app/vendor/yiisoft/yii2/console/Controller.php

@@ -0,0 +1,276 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\console;
+
+use Yii;
+use yii\base\Action;
+use yii\base\InlineAction;
+use yii\base\InvalidRouteException;
+use yii\helpers\Console;
+
+/**
+ * Controller is the base class of console command classes.
+ *
+ * A controller consists of one or several actions known as sub-commands.
+ * Users call a console command by specifying the corresponding route which identifies a controller action.
+ * The `yii` program is used when calling a console command, like the following:
+ *
+ * ~~~
+ * yii <route> [--param1=value1 --param2 ...]
+ * ~~~
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Controller extends \yii\base\Controller
+{
+	/**
+	 * @var boolean whether to run the command interactively.
+	 */
+	public $interactive = true;
+
+	/**
+	 * @var boolean whether to enable ANSI color in the output.
+	 * If not set, ANSI color will only be enabled for terminals that support it.
+	 */
+	public $color;
+
+	/**
+	 * Returns a value indicating whether ANSI color is enabled.
+	 *
+	 * ANSI color is enabled only if [[color]] is set true or is not set
+	 * and the terminal supports ANSI color.
+	 *
+	 * @param resource $stream the stream to check.
+	 * @return boolean Whether to enable ANSI style in output.
+	 */
+	public function isColorEnabled($stream = STDOUT)
+	{
+		return $this->color ===  null ? Console::streamSupportsAnsiColors($stream) : $this->color;
+	}
+
+	/**
+	 * Runs an action with the specified action ID and parameters.
+	 * If the action ID is empty, the method will use [[defaultAction]].
+	 * @param string $id the ID of the action to be executed.
+	 * @param array $params the parameters (name-value pairs) to be passed to the action.
+	 * @return integer the status of the action execution. 0 means normal, other values mean abnormal.
+	 * @throws InvalidRouteException if the requested action ID cannot be resolved into an action successfully.
+	 * @see createAction
+	 */
+	public function runAction($id, $params = [])
+	{
+		if (!empty($params)) {
+			$options = $this->globalOptions();
+			foreach ($params as $name => $value) {
+				if (in_array($name, $options, true)) {
+					$this->$name = $value;
+					unset($params[$name]);
+				}
+			}
+		}
+		return parent::runAction($id, $params);
+	}
+
+	/**
+	 * Binds the parameters to the action.
+	 * This method is invoked by [[Action]] when it begins to run with the given parameters.
+	 * This method will first bind the parameters with the [[globalOptions()|global options]]
+	 * available to the action. It then validates the given arguments.
+	 * @param Action $action the action to be bound with parameters
+	 * @param array $params the parameters to be bound to the action
+	 * @return array the valid parameters that the action can run with.
+	 * @throws Exception if there are unknown options or missing arguments
+	 */
+	public function bindActionParams($action, $params)
+	{
+		$args = [];
+		if (!empty($params)) {
+			$options = $this->globalOptions();
+			foreach ($params as $name => $value) {
+				if (in_array($name, $options, true)) {
+					$this->$name = $value;
+				} elseif (is_int($name)) {
+					$args[] = $value;
+				} else {
+					throw new Exception(Yii::t('yii', 'Unknown option: --{name}', ['name' => $name]));
+				}
+			}
+		}
+
+		if ($action instanceof InlineAction) {
+			$method = new \ReflectionMethod($this, $action->actionMethod);
+		} else {
+			$method = new \ReflectionMethod($action, 'run');
+		}
+
+		$missing = [];
+		foreach ($method->getParameters() as $i => $param) {
+			if ($param->isArray() && isset($args[$i])) {
+				$args[$i] = preg_split('/\s*,\s*/', $args[$i]);
+			}
+			if (!isset($args[$i])) {
+				if ($param->isDefaultValueAvailable()) {
+					$args[$i] = $param->getDefaultValue();
+				} else {
+					$missing[] = $param->getName();
+				}
+			}
+		}
+
+		if (!empty($missing)) {
+			throw new Exception(Yii::t('yii', 'Missing required arguments: {params}', ['params' => implode(', ', $missing)]));
+		}
+
+		return $args;
+	}
+
+	/**
+	 * Formats a string with ANSI codes
+	 *
+	 * You may pass additional parameters using the constants defined in [[yii\helpers\Console]].
+	 *
+	 * Example:
+	 *
+	 * ~~~
+	 * echo $this->ansiFormat('This will be red and underlined.', Console::FG_RED, Console::UNDERLINE);
+	 * ~~~
+	 *
+	 * @param string $string the string to be formatted
+	 * @return string
+	 */
+	public function ansiFormat($string)
+	{
+		if ($this->isColorEnabled()) {
+			$args = func_get_args();
+			array_shift($args);
+			$string = Console::ansiFormat($string, $args);
+		}
+		return $string;
+	}
+
+	/**
+	 * Prints a string to STDOUT
+	 *
+	 * You may optionally format the string with ANSI codes by
+	 * passing additional parameters using the constants defined in [[yii\helpers\Console]].
+	 *
+	 * Example:
+	 *
+	 * ~~~
+	 * $this->stdout('This will be red and underlined.', Console::FG_RED, Console::UNDERLINE);
+	 * ~~~
+	 *
+	 * @param string $string the string to print
+	 * @return int|boolean Number of bytes printed or false on error
+	 */
+	public function stdout($string)
+	{
+		if ($this->isColorEnabled()) {
+			$args = func_get_args();
+			array_shift($args);
+			$string = Console::ansiFormat($string, $args);
+		}
+		return Console::stdout($string);
+	}
+
+	/**
+	 * Prints a string to STDERR
+	 *
+	 * You may optionally format the string with ANSI codes by
+	 * passing additional parameters using the constants defined in [[yii\helpers\Console]].
+	 *
+	 * Example:
+	 *
+	 * ~~~
+	 * $this->stderr('This will be red and underlined.', Console::FG_RED, Console::UNDERLINE);
+	 * ~~~
+	 *
+	 * @param string $string the string to print
+	 * @return int|boolean Number of bytes printed or false on error
+	 */
+	public function stderr($string)
+	{
+		if ($this->isColorEnabled(STDERR)) {
+			$args = func_get_args();
+			array_shift($args);
+			$string = Console::ansiFormat($string, $args);
+		}
+		return fwrite(STDERR, $string);
+	}
+
+	/**
+	 * Prompts the user for input and validates it
+	 *
+	 * @param string $text prompt string
+	 * @param array $options the options to validate the input:
+	 *
+	 *  - required: whether it is required or not
+	 *  - default: default value if no input is inserted by the user
+	 *  - pattern: regular expression pattern to validate user input
+	 *  - validator: a callable function to validate input. The function must accept two parameters:
+	 *      - $input: the user input to validate
+	 *      - $error: the error value passed by reference if validation failed.
+	 * @return string the user input
+	 */
+	public function prompt($text, $options = [])
+	{
+		if ($this->interactive) {
+			return Console::prompt($text, $options);
+		} else {
+			return isset($options['default']) ? $options['default'] : '';
+		}
+	}
+
+	/**
+	 * Asks user to confirm by typing y or n.
+	 *
+	 * @param string $message to echo out before waiting for user input
+	 * @param boolean $default this value is returned if no selection is made.
+	 * @return boolean whether user confirmed.
+	 * Will return true if [[interactive]] is false.
+	 */
+	public function confirm($message, $default = false)
+	{
+		if ($this->interactive) {
+			return Console::confirm($message, $default);
+		} else {
+			return true;
+		}
+	}
+
+	/**
+	 * Gives the user an option to choose from. Giving '?' as an input will show
+	 * a list of options to choose from and their explanations.
+	 *
+	 * @param string $prompt the prompt message
+	 * @param array  $options Key-value array of options to choose from
+	 *
+	 * @return string An option character the user chose
+	 */
+	public function select($prompt, $options = [])
+	{
+		return Console::select($prompt, $options);
+	}
+
+	/**
+	 * Returns the names of the global options for this command.
+	 * A global option requires the existence of a public member variable whose
+	 * name is the option name.
+	 * Child classes may override this method to specify possible global options.
+	 *
+	 * Note that the values setting via global options are not available
+	 * until [[beforeAction()]] is being called.
+	 *
+	 * @return array the names of the global options for this command.
+	 */
+	public function globalOptions()
+	{
+		return ['color', 'interactive'];
+	}
+}

+ 27 - 0
php-yii2/app/vendor/yiisoft/yii2/console/Exception.php

@@ -0,0 +1,27 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\console;
+
+use yii\base\UserException;
+
+/**
+ * Exception represents an exception caused by incorrect usage of a console command.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Exception extends UserException
+{
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return 'Error';
+	}
+}

+ 77 - 0
php-yii2/app/vendor/yiisoft/yii2/console/Request.php

@@ -0,0 +1,77 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\console;
+
+/**
+ * The console Request represents the environment information for a console application.
+ *
+ * It is a wrapper for the PHP `$_SERVER` variable which holds information about the
+ * currently running PHP script and the command line arguments given to it.
+ *
+ * @property array $params The command line arguments. It does not include the entry script name.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Request extends \yii\base\Request
+{
+	private $_params;
+
+	/**
+	 * Returns the command line arguments.
+	 * @return array the command line arguments. It does not include the entry script name.
+	 */
+	public function getParams()
+	{
+		if (!isset($this->_params)) {
+			if (isset($_SERVER['argv'])) {
+				$this->_params = $_SERVER['argv'];
+				array_shift($this->_params);
+			} else {
+				$this->_params = [];
+			}
+		}
+		return $this->_params;
+	}
+
+	/**
+	 * Sets the command line arguments.
+	 * @param array $params the command line arguments
+	 */
+	public function setParams($params)
+	{
+		$this->_params = $params;
+	}
+
+	/**
+	 * Resolves the current request into a route and the associated parameters.
+	 * @return array the first element is the route, and the second is the associated parameters.
+	 */
+	public function resolve()
+	{
+		$rawParams = $this->getParams();
+		if (isset($rawParams[0])) {
+			$route = $rawParams[0];
+			array_shift($rawParams);
+		} else {
+			$route = '';
+		}
+
+		$params = [];
+		foreach ($rawParams as $param) {
+			if (preg_match('/^--(\w+)(=(.*))?$/', $param, $matches)) {
+				$name = $matches[1];
+				$params[$name] = isset($matches[3]) ? $matches[3] : true;
+			} else {
+				$params[] = $param;
+			}
+		}
+
+		return [$route, $params];
+	}
+}

+ 18 - 0
php-yii2/app/vendor/yiisoft/yii2/console/Response.php

@@ -0,0 +1,18 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\console;
+
+/**
+ * The console Response represents the result of a console application.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class Response extends \yii\base\Response
+{
+}

+ 613 - 0
php-yii2/app/vendor/yiisoft/yii2/console/controllers/AssetController.php

@@ -0,0 +1,613 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\console\controllers;
+
+use Yii;
+use yii\console\Exception;
+use yii\console\Controller;
+
+/**
+ * This command allows you to combine and compress your JavaScript and CSS files.
+ *
+ * Usage:
+ * 1. Create a configuration file using 'template' action:
+ *    yii asset/template /path/to/myapp/config.php
+ * 2. Edit the created config file, adjusting it for your web application needs.
+ * 3. Run the 'compress' action, using created config:
+ *    yii asset /path/to/myapp/config.php /path/to/myapp/config/assets_compressed.php
+ * 4. Adjust your web application config to use compressed assets.
+ *
+ * Note: in the console environment some path aliases like '@webroot' and '@web' may not exist,
+ * so corresponding paths inside the configuration should be specified directly.
+ *
+ * Note: by default this command relies on an external tools to perform actual files compression,
+ * check [[jsCompressor]] and [[cssCompressor]] for more details.
+ *
+ * @property \yii\web\AssetManager $assetManager Asset manager instance. Note that the type of this property
+ * differs in getter and setter. See [[getAssetManager()]] and [[setAssetManager()]] for details.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class AssetController extends Controller
+{
+	/**
+	 * @var string controller default action ID.
+	 */
+	public $defaultAction = 'compress';
+	/**
+	 * @var array list of asset bundles to be compressed.
+	 */
+	public $bundles = [];
+	/**
+	 * @var array list of asset bundles, which represents output compressed files.
+	 * You can specify the name of the output compressed file using 'css' and 'js' keys:
+	 * For example:
+	 *
+	 * ~~~
+	 * 'app\config\AllAsset' => [
+	 *     'js' => 'js/all-{ts}.js',
+	 *     'css' => 'css/all-{ts}.css',
+	 *     'depends' => [ ... ],
+	 * ]
+	 * ~~~
+	 *
+	 * File names can contain placeholder "{ts}", which will be filled by current timestamp, while
+	 * file creation.
+	 */
+	public $targets = [];
+	/**
+	 * @var string|callback JavaScript file compressor.
+	 * If a string, it is treated as shell command template, which should contain
+	 * placeholders {from} - source file name - and {to} - output file name.
+	 * Otherwise, it is treated as PHP callback, which should perform the compression.
+	 *
+	 * Default value relies on usage of "Closure Compiler"
+	 * @see https://developers.google.com/closure/compiler/
+	 */
+	public $jsCompressor = 'java -jar compiler.jar --js {from} --js_output_file {to}';
+	/**
+	 * @var string|callback CSS file compressor.
+	 * If a string, it is treated as shell command template, which should contain
+	 * placeholders {from} - source file name - and {to} - output file name.
+	 * Otherwise, it is treated as PHP callback, which should perform the compression.
+	 *
+	 * Default value relies on usage of "YUI Compressor"
+	 * @see https://github.com/yui/yuicompressor/
+	 */
+	public $cssCompressor = 'java -jar yuicompressor.jar {from} -o {to}';
+
+	/**
+	 * @var array|\yii\web\AssetManager [[yii\web\AssetManager]] instance or its array configuration, which will be used
+	 * for assets processing.
+	 */
+	private $_assetManager = [];
+
+	/**
+	 * Returns the asset manager instance.
+	 * @throws \yii\console\Exception on invalid configuration.
+	 * @return \yii\web\AssetManager asset manager instance.
+	 */
+	public function getAssetManager()
+	{
+		if (!is_object($this->_assetManager)) {
+			$options = $this->_assetManager;
+			if (!isset($options['class'])) {
+				$options['class'] = 'yii\\web\\AssetManager';
+			}
+			if (!isset($options['basePath'])) {
+				throw new Exception("Please specify 'basePath' for the 'assetManager' option.");
+			}
+			if (!isset($options['baseUrl'])) {
+				throw new Exception("Please specify 'baseUrl' for the 'assetManager' option.");
+			}
+			$this->_assetManager = Yii::createObject($options);
+		}
+		return $this->_assetManager;
+	}
+
+	/**
+	 * Sets asset manager instance or configuration.
+	 * @param \yii\web\AssetManager|array $assetManager asset manager instance or its array configuration.
+	 * @throws \yii\console\Exception on invalid argument type.
+	 */
+	public function setAssetManager($assetManager)
+	{
+		if (is_scalar($assetManager)) {
+			throw new Exception('"' . get_class($this) . '::assetManager" should be either object or array - "' . gettype($assetManager) . '" given.');
+		}
+		$this->_assetManager = $assetManager;
+	}
+
+	/**
+	 * Combines and compresses the asset files according to the given configuration.
+	 * During the process new asset bundle configuration file will be created.
+	 * You should replace your original asset bundle configuration with this file in order to use compressed files.
+	 * @param string $configFile configuration file name.
+	 * @param string $bundleFile output asset bundles configuration file name.
+	 */
+	public function actionCompress($configFile, $bundleFile)
+	{
+		$this->loadConfiguration($configFile);
+		$bundles = $this->loadBundles($this->bundles);
+		$targets = $this->loadTargets($this->targets, $bundles);
+		$timestamp = time();
+		foreach ($targets as $name => $target) {
+			echo "Creating output bundle '{$name}':\n";
+			if (!empty($target->js)) {
+				$this->buildTarget($target, 'js', $bundles, $timestamp);
+			}
+			if (!empty($target->css)) {
+				$this->buildTarget($target, 'css', $bundles, $timestamp);
+			}
+			echo "\n";
+		}
+
+		$targets = $this->adjustDependency($targets, $bundles);
+		$this->saveTargets($targets, $bundleFile);
+	}
+
+	/**
+	 * Applies configuration from the given file to self instance.
+	 * @param string $configFile configuration file name.
+	 * @throws \yii\console\Exception on failure.
+	 */
+	protected function loadConfiguration($configFile)
+	{
+		echo "Loading configuration from '{$configFile}'...\n";
+		foreach (require($configFile) as $name => $value) {
+			if (property_exists($this, $name) || $this->canSetProperty($name)) {
+				$this->$name = $value;
+			} else {
+				throw new Exception("Unknown configuration option: $name");
+			}
+		}
+
+		$this->getAssetManager(); // check if asset manager configuration is correct
+	}
+
+	/**
+	 * Creates full list of source asset bundles.
+	 * @param string[] $bundles list of asset bundle names
+	 * @return \yii\web\AssetBundle[] list of source asset bundles.
+	 */
+	protected function loadBundles($bundles)
+	{
+		echo "Collecting source bundles information...\n";
+
+		$am = $this->getAssetManager();
+		$result = [];
+		foreach ($bundles as $name) {
+			$result[$name] = $am->getBundle($name);
+		}
+		foreach ($result as $bundle) {
+			$this->loadDependency($bundle, $result);
+		}
+
+		return $result;
+	}
+
+	/**
+	 * Loads asset bundle dependencies recursively.
+	 * @param \yii\web\AssetBundle $bundle bundle instance
+	 * @param array $result already loaded bundles list.
+	 * @throws Exception on failure.
+	 */
+	protected function loadDependency($bundle, &$result)
+	{
+		$am = $this->getAssetManager();
+		foreach ($bundle->depends as $name) {
+			if (!isset($result[$name])) {
+				$dependencyBundle = $am->getBundle($name);
+				$result[$name] = false;
+				$this->loadDependency($dependencyBundle, $result);
+				$result[$name] = $dependencyBundle;
+			} elseif ($result[$name] === false) {
+				throw new Exception("A circular dependency is detected for bundle '$name'.");
+			}
+		}
+	}
+
+	/**
+	 * Creates full list of output asset bundles.
+	 * @param array $targets output asset bundles configuration.
+	 * @param \yii\web\AssetBundle[] $bundles list of source asset bundles.
+	 * @return \yii\web\AssetBundle[] list of output asset bundles.
+	 * @throws Exception on failure.
+	 */
+	protected function loadTargets($targets, $bundles)
+	{
+		// build the dependency order of bundles
+		$registered = [];
+		foreach ($bundles as $name => $bundle) {
+			$this->registerBundle($bundles, $name, $registered);
+		}
+		$bundleOrders = array_combine(array_keys($registered), range(0, count($bundles) - 1));
+
+		// fill up the target which has empty 'depends'.
+		$referenced = [];
+		foreach ($targets as $name => $target) {
+			if (empty($target['depends'])) {
+				if (!isset($all)) {
+					$all = $name;
+				} else {
+					throw new Exception("Only one target can have empty 'depends' option. Found two now: $all, $name");
+				}
+			} else {
+				foreach ($target['depends'] as $bundle) {
+					if (!isset($referenced[$bundle])) {
+						$referenced[$bundle] = $name;
+					} else {
+						throw new Exception("Target '{$referenced[$bundle]}' and '$name' cannot contain the bundle '$bundle' at the same time.");
+					}
+				}
+			}
+		}
+		if (isset($all)) {
+			$targets[$all]['depends'] = array_diff(array_keys($registered), array_keys($referenced));
+		}
+
+		// adjust the 'depends' order for each target according to the dependency order of bundles
+		// create an AssetBundle object for each target
+		foreach ($targets as $name => $target) {
+			if (!isset($target['basePath'])) {
+				throw new Exception("Please specify 'basePath' for the '$name' target.");
+			}
+			if (!isset($target['baseUrl'])) {
+				throw new Exception("Please specify 'baseUrl' for the '$name' target.");
+			}
+			usort($target['depends'], function ($a, $b) use ($bundleOrders) {
+				if ($bundleOrders[$a] == $bundleOrders[$b]) {
+					return 0;
+				} else {
+					return $bundleOrders[$a] > $bundleOrders[$b] ? 1 : -1;
+				}
+			});
+			$target['class'] = $name;
+			$targets[$name] = Yii::createObject($target);
+		}
+		return $targets;
+	}
+
+	/**
+	 * Builds output asset bundle.
+	 * @param \yii\web\AssetBundle $target output asset bundle
+	 * @param string $type either 'js' or 'css'.
+	 * @param \yii\web\AssetBundle[] $bundles source asset bundles.
+	 * @param integer $timestamp current timestamp.
+	 * @throws Exception on failure.
+	 */
+	protected function buildTarget($target, $type, $bundles, $timestamp)
+	{
+		$outputFile = strtr($target->$type, [
+			'{ts}' => $timestamp,
+		]);
+		$inputFiles = [];
+
+		foreach ($target->depends as $name) {
+			if (isset($bundles[$name])) {
+				foreach ($bundles[$name]->$type as $file) {
+					$inputFiles[] = $bundles[$name]->basePath . '/' . $file;
+				}
+			} else {
+				throw new Exception("Unknown bundle: '{$name}'");
+			}
+		}
+		if ($type === 'js') {
+			$this->compressJsFiles($inputFiles, $target->basePath . '/' . $outputFile);
+		} else {
+			$this->compressCssFiles($inputFiles, $target->basePath . '/' . $outputFile);
+		}
+		$target->$type = [$outputFile];
+	}
+
+	/**
+	 * Adjust dependencies between asset bundles in the way source bundles begin to depend on output ones.
+	 * @param \yii\web\AssetBundle[] $targets output asset bundles.
+	 * @param \yii\web\AssetBundle[] $bundles source asset bundles.
+	 * @return \yii\web\AssetBundle[] output asset bundles.
+	 */
+	protected function adjustDependency($targets, $bundles)
+	{
+		echo "Creating new bundle configuration...\n";
+
+		$map = [];
+		foreach ($targets as $name => $target) {
+			foreach ($target->depends as $bundle) {
+				$map[$bundle] = $name;
+			}
+		}
+
+		foreach ($targets as $name => $target) {
+			$depends = [];
+			foreach ($target->depends as $bn) {
+				foreach ($bundles[$bn]->depends as $bundle) {
+					$depends[$map[$bundle]] = true;
+				}
+			}
+			unset($depends[$name]);
+			$target->depends = array_keys($depends);
+		}
+
+		// detect possible circular dependencies
+		foreach ($targets as $name => $target) {
+			$registered = [];
+			$this->registerBundle($targets, $name, $registered);
+		}
+
+		foreach ($map as $bundle => $target) {
+			$targets[$bundle] = Yii::createObject([
+				'class' => 'yii\\web\\AssetBundle',
+				'depends' => [$target],
+			]);
+		}
+		return $targets;
+	}
+
+	/**
+	 * Registers asset bundles including their dependencies.
+	 * @param \yii\web\AssetBundle[] $bundles asset bundles list.
+	 * @param string $name bundle name.
+	 * @param array $registered stores already registered names.
+	 * @throws Exception if circular dependency is detected.
+	 */
+	protected function registerBundle($bundles, $name, &$registered)
+	{
+		if (!isset($registered[$name])) {
+			$registered[$name] = false;
+			$bundle = $bundles[$name];
+			foreach ($bundle->depends as $depend) {
+				$this->registerBundle($bundles, $depend, $registered);
+			}
+			unset($registered[$name]);
+			$registered[$name] = true;
+		} elseif ($registered[$name] === false) {
+			throw new Exception("A circular dependency is detected for target '$name'.");
+		}
+	}
+
+	/**
+	 * Saves new asset bundles configuration.
+	 * @param \yii\web\AssetBundle[] $targets list of asset bundles to be saved.
+	 * @param string $bundleFile output file name.
+	 * @throws \yii\console\Exception on failure.
+	 */
+	protected function saveTargets($targets, $bundleFile)
+	{
+		$array = [];
+		foreach ($targets as $name => $target) {
+			foreach (['basePath', 'baseUrl', 'js', 'css', 'depends'] as $prop) {
+				if (!empty($target->$prop)) {
+					$array[$name][$prop] = $target->$prop;
+				} elseif (in_array($prop, ['js', 'css'])) {
+					$array[$name][$prop] = [];
+				}
+			}
+		}
+		$array = var_export($array, true);
+		$version = date('Y-m-d H:i:s', time());
+		$bundleFileContent = <<<EOD
+<?php
+/**
+ * This file is generated by the "yii {$this->id}" command.
+ * DO NOT MODIFY THIS FILE DIRECTLY.
+ * @version {$version}
+ */
+return {$array};
+EOD;
+		if (!file_put_contents($bundleFile, $bundleFileContent)) {
+			throw new Exception("Unable to write output bundle configuration at '{$bundleFile}'.");
+		}
+		echo "Output bundle configuration created at '{$bundleFile}'.\n";
+	}
+
+	/**
+	 * Compresses given JavaScript files and combines them into the single one.
+	 * @param array $inputFiles list of source file names.
+	 * @param string $outputFile output file name.
+	 * @throws \yii\console\Exception on failure
+	 */
+	protected function compressJsFiles($inputFiles, $outputFile)
+	{
+		if (empty($inputFiles)) {
+			return;
+		}
+		echo "  Compressing JavaScript files...\n";
+		if (is_string($this->jsCompressor)) {
+			$tmpFile = $outputFile . '.tmp';
+			$this->combineJsFiles($inputFiles, $tmpFile);
+			echo shell_exec(strtr($this->jsCompressor, [
+				'{from}' => escapeshellarg($tmpFile),
+				'{to}' => escapeshellarg($outputFile),
+			]));
+			@unlink($tmpFile);
+		} else {
+			call_user_func($this->jsCompressor, $this, $inputFiles, $outputFile);
+		}
+		if (!file_exists($outputFile)) {
+			throw new Exception("Unable to compress JavaScript files into '{$outputFile}'.");
+		}
+		echo "  JavaScript files compressed into '{$outputFile}'.\n";
+	}
+
+	/**
+	 * Compresses given CSS files and combines them into the single one.
+	 * @param array $inputFiles list of source file names.
+	 * @param string $outputFile output file name.
+	 * @throws \yii\console\Exception on failure
+	 */
+	protected function compressCssFiles($inputFiles, $outputFile)
+	{
+		if (empty($inputFiles)) {
+			return;
+		}
+		echo "  Compressing CSS files...\n";
+		if (is_string($this->cssCompressor)) {
+			$tmpFile = $outputFile . '.tmp';
+			$this->combineCssFiles($inputFiles, $tmpFile);
+			echo shell_exec(strtr($this->cssCompressor, [
+				'{from}' => escapeshellarg($tmpFile),
+				'{to}' => escapeshellarg($outputFile),
+			]));
+			@unlink($tmpFile);
+		} else {
+			call_user_func($this->cssCompressor, $this, $inputFiles, $outputFile);
+		}
+		if (!file_exists($outputFile)) {
+			throw new Exception("Unable to compress CSS files into '{$outputFile}'.");
+		}
+		echo "  CSS files compressed into '{$outputFile}'.\n";
+	}
+
+	/**
+	 * Combines JavaScript files into a single one.
+	 * @param array $inputFiles source file names.
+	 * @param string $outputFile output file name.
+	 * @throws \yii\console\Exception on failure.
+	 */
+	public function combineJsFiles($inputFiles, $outputFile)
+	{
+		$content = '';
+		foreach ($inputFiles as $file) {
+			$content .= "/*** BEGIN FILE: $file ***/\n"
+				. file_get_contents($file)
+				. "/*** END FILE: $file ***/\n";
+		}
+		if (!file_put_contents($outputFile, $content)) {
+			throw new Exception("Unable to write output JavaScript file '{$outputFile}'.");
+		}
+	}
+
+	/**
+	 * Combines CSS files into a single one.
+	 * @param array $inputFiles source file names.
+	 * @param string $outputFile output file name.
+	 * @throws \yii\console\Exception on failure.
+	 */
+	public function combineCssFiles($inputFiles, $outputFile)
+	{
+		$content = '';
+		foreach ($inputFiles as $file) {
+			$content .= "/*** BEGIN FILE: $file ***/\n"
+				. $this->adjustCssUrl(file_get_contents($file), dirname($file), dirname($outputFile))
+				. "/*** END FILE: $file ***/\n";
+		}
+		if (!file_put_contents($outputFile, $content)) {
+			throw new Exception("Unable to write output CSS file '{$outputFile}'.");
+		}
+	}
+
+	/**
+	 * Adjusts CSS content allowing URL references pointing to the original resources.
+	 * @param string $cssContent source CSS content.
+	 * @param string $inputFilePath input CSS file name.
+	 * @param string $outputFilePath output CSS file name.
+	 * @return string adjusted CSS content.
+	 */
+	protected function adjustCssUrl($cssContent, $inputFilePath, $outputFilePath)
+	{
+		$sharedPathParts = [];
+		$inputFilePathParts = explode('/', $inputFilePath);
+		$inputFilePathPartsCount = count($inputFilePathParts);
+		$outputFilePathParts = explode('/', $outputFilePath);
+		$outputFilePathPartsCount = count($outputFilePathParts);
+		for ($i =0; $i < $inputFilePathPartsCount && $i < $outputFilePathPartsCount; $i++) {
+			if ($inputFilePathParts[$i] == $outputFilePathParts[$i]) {
+				$sharedPathParts[] = $inputFilePathParts[$i];
+			} else {
+				break;
+			}
+		}
+		$sharedPath = implode('/', $sharedPathParts);
+
+		$inputFileRelativePath = trim(str_replace($sharedPath, '', $inputFilePath), '/');
+		$outputFileRelativePath = trim(str_replace($sharedPath, '', $outputFilePath), '/');
+		$inputFileRelativePathParts = explode('/', $inputFileRelativePath);
+		$outputFileRelativePathParts = explode('/', $outputFileRelativePath);
+
+		$callback = function ($matches) use ($inputFileRelativePathParts, $outputFileRelativePathParts) {
+			$fullMatch = $matches[0];
+			$inputUrl = $matches[1];
+
+			if (preg_match('/https?:\/\//is', $inputUrl)) {
+				return $fullMatch;
+			}
+
+			$outputUrlParts = array_fill(0, count($outputFileRelativePathParts), '..');
+			$outputUrlParts = array_merge($outputUrlParts, $inputFileRelativePathParts);
+
+			if (strpos($inputUrl, '/') !== false) {
+				$inputUrlParts = explode('/', $inputUrl);
+				foreach ($inputUrlParts as $key => $inputUrlPart) {
+					if ($inputUrlPart == '..') {
+						array_pop($outputUrlParts);
+						unset($inputUrlParts[$key]);
+					}
+				}
+				$outputUrlParts[] = implode('/', $inputUrlParts);
+			} else {
+				$outputUrlParts[] = $inputUrl;
+			}
+			$outputUrl = implode('/', $outputUrlParts);
+
+			return str_replace($inputUrl, $outputUrl, $fullMatch);
+		};
+
+		$cssContent = preg_replace_callback('/url\(["\']?([^"]*)["\']?\)/is', $callback, $cssContent);
+
+		return $cssContent;
+	}
+
+	/**
+	 * Creates template of configuration file for [[actionCompress]].
+	 * @param string $configFile output file name.
+	 * @throws \yii\console\Exception on failure.
+	 */
+	public function actionTemplate($configFile)
+	{
+		$template = <<<EOD
+<?php
+/**
+ * Configuration file for the "yii asset" console command.
+ * Note that in the console environment, some path aliases like '@webroot' and '@web' may not exist.
+ * Please define these missing path aliases.
+ */
+return [
+	// The list of asset bundles to compress:
+	'bundles' => [
+		// 'yii\web\YiiAsset',
+		// 'yii\web\JqueryAsset',
+	],
+	// Asset bundle for compression output:
+	'targets' => [
+		'app\config\AllAsset' => [
+			'basePath' => 'path/to/web',
+			'baseUrl' => '',
+			'js' => 'js/all-{ts}.js',
+			'css' => 'css/all-{ts}.css',
+		],
+	],
+	// Asset manager configuration:
+	'assetManager' => [
+		'basePath' => __DIR__,
+		'baseUrl' => '',
+	],
+];
+EOD;
+		if (file_exists($configFile)) {
+			if (!$this->confirm("File '{$configFile}' already exists. Do you wish to overwrite it?")) {
+				return;
+			}
+		}
+		if (!file_put_contents($configFile, $template)) {
+			throw new Exception("Unable to write template file '{$configFile}'.");
+		} else {
+			echo "Configuration file template created at '{$configFile}'.\n\n";
+		}
+	}
+}

+ 67 - 0
php-yii2/app/vendor/yiisoft/yii2/console/controllers/CacheController.php

@@ -0,0 +1,67 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\console\controllers;
+
+use Yii;
+use yii\console\Controller;
+use yii\console\Exception;
+use yii\caching\Cache;
+
+/**
+ * This command allows you to flush cache.
+ *
+ * @author Alexander Makarov <[email protected]>
+ * @since 2.0
+ */
+class CacheController extends Controller
+{
+	/**
+	 * Lists the caches that can be flushed.
+	 */
+	public function actionIndex()
+	{
+		$caches = [];
+		$components = Yii::$app->getComponents();
+		foreach ($components as $name => $component) {
+			if ($component instanceof Cache) {
+				$caches[$name] = get_class($component);
+			} elseif (is_array($component) && isset($component['class']) && strpos($component['class'], 'Cache') !== false) {
+				$caches[$name] = $component['class'];
+			}
+		}
+		if (!empty($caches)) {
+			echo "The following caches can be flushed:\n\n";
+			foreach ($caches as $name => $class) {
+				echo " * $name: $class\n";
+			}
+		} else {
+			echo "No cache is used.\n";
+		}
+	}
+
+	/**
+	 * Flushes cache.
+	 * @param string $component Name of the cache application component to use.
+	 *
+	 * @throws \yii\console\Exception
+	 */
+	public function actionFlush($component = 'cache')
+	{
+		/** @var Cache $cache */
+		$cache = Yii::$app->getComponent($component);
+		if (!$cache || !$cache instanceof Cache) {
+			throw new Exception('Application component "'.$component.'" is not defined or not a cache.');
+		}
+
+		if (!$cache->flush()) {
+			throw new Exception('Unable to flush cache.');
+		}
+
+		echo "\nDone.\n";
+	}
+}

+ 335 - 0
php-yii2/app/vendor/yiisoft/yii2/console/controllers/FixtureController.php

@@ -0,0 +1,335 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\console\controllers;
+
+use Yii;
+use yii\console\Controller;
+use yii\console\Exception;
+use yii\helpers\FileHelper;
+use yii\helpers\Console;
+
+/**
+ * This command manages fixtures load to the database tables.
+ * You can specify different options of this command to point fixture manager
+ * to the specific tables of the different database connections.
+ *
+ * To use this command simply configure your console.php config like this:
+ *
+ * ~~~
+ * 'db' => [
+ *     'class' => 'yii\db\Connection',
+ *     'dsn' => 'mysql:host=localhost;dbname={your_database}',
+ *     'username' => '{your_db_user}',
+ *     'password' => '',
+ *     'charset' => 'utf8',
+ * ],
+ * 'fixture' => [
+ *     'class' => 'yii\test\DbFixtureManager',
+ * ],
+ * ~~~
+ *
+ * ~~~
+ * #load fixtures under $fixturePath to the "users" table
+ * yii fixture/apply users
+ *
+ * #also a short version of this command (generate action is default)
+ * yii fixture users
+ *
+ * #load fixtures under $fixturePath to the "users" table to the different connection
+ * yii fixture/apply users --db=someOtherDbConneciton
+ *
+ * #load fixtures under different $fixturePath to the "users" table.
+ * yii fixture/apply users --fixturePath=@app/some/other/path/to/fixtures
+ * ~~~
+ *
+ * @author Mark Jebri <[email protected]>
+ * @since 2.0
+ */
+class FixtureController extends Controller
+{
+	use DbTestTrait;
+	
+	/**
+	 * type of fixture apply to database
+	 */
+	const APPLY_ALL = 'all';
+
+	/**
+	 * @var string controller default action ID.
+	 */
+	public $defaultAction = 'apply';
+	/**
+	 * Alias to the path, where all fixtures are stored.
+	 * @var string
+	 */
+	public $fixturePath = '@tests/unit/fixtures';
+	/**
+	 * Id of the database connection component of the application.
+	 * @var string
+	 */
+	public $db = 'db';
+
+
+	/**
+	 * Returns the names of the global options for this command.
+	 * @return array the names of the global options for this command.
+	 */
+	public function globalOptions()
+	{
+		return array_merge(parent::globalOptions(), [
+			'db', 'fixturePath'
+		]);
+	}
+
+	/**
+	 * This method is invoked right before an action is to be executed (after all possible filters.)
+	 * It checks that fixtures path and database connection are available.
+	 * @param \yii\base\Action $action
+	 * @return boolean
+	 */
+	public function beforeAction($action)
+	{
+		if (parent::beforeAction($action)) {
+			$this->checkRequirements();
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * Apply given fixture to the table. You can load several fixtures specifying
+	 * their names separated with commas, like: tbl_user,tbl_profile. Be sure there is no
+	 * whitespace between tables names.
+	 * @param array $fixtures
+	 * @throws \yii\console\Exception
+	 */
+	public function actionApply(array $fixtures, array $except = [])
+	{
+		if ($this->getFixtureManager() === null) {
+			throw new Exception('Fixture manager is not configured properly. Please refer to official documentation for this purposes.');
+		}
+
+		$foundFixtures = $this->findFixtures($fixtures);
+
+		if (!$this->needToApplyAll($fixtures[0])) {
+			$notFoundFixtures = array_diff($fixtures, $foundFixtures);
+
+			if ($notFoundFixtures) {
+				$this->notifyNotFound($notFoundFixtures);
+			}
+		}
+
+		if (!$foundFixtures) {
+			throw new Exception("No files were found by name: \"" . implode(', ', $fixtures) . "\".\n"
+				. "Check that fixtures with these name exists, under fixtures path: \n\"" . Yii::getAlias($this->fixturePath) . "\"."
+			);			
+		}
+
+		if (!$this->confirmApply($foundFixtures, $except)) {
+			return;
+		}
+
+		$fixtures = array_diff($foundFixtures, $except);
+
+		$this->getFixtureManager()->basePath = $this->fixturePath;
+		$this->getFixtureManager()->db = $this->db;
+
+		$transaction = Yii::$app->db->beginTransaction();
+
+		try {
+			$this->loadFixtures($foundFixtures);
+			$transaction->commit();
+
+		} catch (\Exception $e) {
+			$transaction->rollback();
+			$this->stdout("Exception occured, transaction rollback. Tables will be in same state.\n", Console::BG_RED);
+			throw $e;
+		}
+		$this->notifySuccess($foundFixtures);
+	}
+
+	/**
+	 * Truncate given table and clear all fixtures from it. You can clear several tables specifying
+	 * their names separated with commas, like: tbl_user,tbl_profile. Be sure there is no
+	 * whitespace between tables names.
+	 * @param array|string $tables
+	 */
+	public function actionClear(array $tables, array $except = ['tbl_migration'])
+	{		
+		if ($this->needToApplyAll($tables[0])) {
+			$tables = $this->getDbConnection()->schema->getTableNames();
+		}
+
+		if (!$this->confirmClear($tables, $except)) {
+			return;
+		}
+
+		$tables = array_diff($tables, $except);
+
+		$transaction = Yii::$app->db->beginTransaction();
+
+		try {
+			$this->getDbConnection()->createCommand()->checkIntegrity(false)->execute();
+
+			foreach($tables as $table) {
+				$this->getDbConnection()->createCommand()->delete($table)->execute();
+				$this->getDbConnection()->createCommand()->resetSequence($table)->execute();
+				$this->stdout("    Table \"{$table}\" was successfully cleared. \n", Console::FG_GREEN);
+			}
+
+			$this->getDbConnection()->createCommand()->checkIntegrity(true)->execute();
+			$transaction->commit();
+
+		} catch (\Exception $e) {
+			$transaction->rollback();
+			$this->stdout("Exception occured, transaction rollback. Tables will be in same state.\n", Console::BG_RED);
+			throw $e;
+		}
+	}
+
+	/**
+	 * Checks if the database and fixtures path are available.
+	 * @throws Exception
+	 */
+	public function checkRequirements()
+	{
+		$path = Yii::getAlias($this->fixturePath, false);
+
+		if (!is_dir($path) || !is_writable($path)) {
+			throw new Exception("The fixtures path \"{$this->fixturePath}\" not exist or is not writable.");
+		}
+
+	}
+
+	/**
+	 * Returns database connection component
+	 * @return \yii\db\Connection
+	 * @throws Exception if [[db]] is invalid.
+	 */
+	public function getDbConnection()
+	{
+		$db = Yii::$app->getComponent($this->db);
+
+		if ($db === null) {
+			throw new Exception("There is no database connection component with id \"{$this->db}\".");
+		}
+
+		return $db;
+	}
+
+	/**
+	 * Notifies user that fixtures were successfully loaded.
+	 * @param array $fixtures
+	 */
+	private function notifySuccess($fixtures)
+	{
+		$this->stdout("Fixtures were successfully loaded from path:\n", Console::FG_YELLOW);
+		$this->stdout(Yii::getAlias($this->fixturePath) . "\n\n", Console::FG_GREEN);
+		$this->outputList($fixtures);
+	}
+
+	/**
+	 * Notifies user that fixtures were not found under fixtures path.
+	 * @param array $fixtures
+	 */
+	private function notifyNotFound($fixtures)
+	{
+		$this->stdout("Some fixtures were not found under path:\n", Console::BG_RED);
+		$this->stdout(Yii::getAlias($this->fixturePath) . "\n\n", Console::FG_GREEN);
+		$this->outputList($fixtures);
+		$this->stdout("\n");
+	}
+
+	/**
+	 * Prompts user with confirmation if fixtures should be loaded.
+	 * @param array $fixtures
+	 * @param array $except
+	 * @return boolean
+	 */
+	private function confirmApply($fixtures, $except)
+	{
+		$this->stdout("Fixtures will be loaded from path: \n", Console::FG_YELLOW);
+		$this->stdout(Yii::getAlias($this->fixturePath) . "\n\n", Console::FG_GREEN);
+		$this->outputList($fixtures);
+
+		if (count($except)) {
+			$this->stdout("\nFixtures that will NOT be loaded: \n\n", Console::FG_YELLOW);
+			$this->outputList($except);
+		}
+
+		return $this->confirm("\nLoad to database above fixtures?");
+	}
+
+	/**
+	 * Prompts user with confirmation for tables that should be cleared.
+	 * @param array $tables
+	 * @param array $except
+	 * @return boolean
+	 */
+	private function confirmClear($tables, $except)
+	{
+		$this->stdout("Tables below will be cleared:\n\n", Console::FG_YELLOW);
+		$this->outputList($tables);
+
+		if (count($except)) {
+			$this->stdout("\nTables that will NOT be cleared:\n\n", Console::FG_YELLOW);
+			$this->outputList($except);
+		}
+
+		return $this->confirm("\nClear tables?");
+	}
+
+	/**
+	 * Outputs data to the console as a list.
+	 * @param array $data
+	 */
+	private function outputList($data)
+	{
+		foreach($data as $index => $item) {
+			$this->stdout("    " . ($index + 1) . ". {$item}\n", Console::FG_GREEN);
+		}
+	}
+
+	/**
+	 * Checks if needed to apply all fixtures.
+	 * @param string $fixture
+	 * @return bool
+	 */
+	public function needToApplyAll($fixture)
+	{
+		return $fixture == self::APPLY_ALL;
+	}
+
+	/**
+	 * @param array $fixtures
+	 * @return array Array of found fixtures. These may differ from input parameter as not all fixtures may exists.
+	 */
+	private function findFixtures(array $fixtures)
+	{
+		$fixturesPath = Yii::getAlias($this->fixturePath);
+
+		$filesToSearch = ['.php'];
+		if (!$this->needToApplyAll($fixtures[0])) {
+			$filesToSearch = [];
+			foreach ($fixtures as $fileName) {
+				$filesToSearch[] = $fileName . '.php';
+			}
+		}
+
+		$files = FileHelper::findFiles($fixturesPath, ['only' => $filesToSearch]);
+		$foundFixtures = [];
+
+		foreach ($files as $fixture) {
+			$foundFixtures[] = basename($fixture , '.php');
+		}
+
+		return $foundFixtures;
+	}
+
+}

+ 437 - 0
php-yii2/app/vendor/yiisoft/yii2/console/controllers/HelpController.php

@@ -0,0 +1,437 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\console\controllers;
+
+use Yii;
+use yii\base\Application;
+use yii\base\InlineAction;
+use yii\console\Controller;
+use yii\console\Exception;
+use yii\helpers\Console;
+use yii\helpers\Inflector;
+
+/**
+ * This command provides help information about console commands.
+ *
+ * This command displays the available command list in
+ * the application or the detailed instructions about using
+ * a specific command.
+ *
+ * This command can be used as follows on command line:
+ *
+ * ~~~
+ * yii help [command name]
+ * ~~~
+ *
+ * In the above, if the command name is not provided, all
+ * available commands will be displayed.
+ *
+ * @property array $commands All available command names. This property is read-only.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class HelpController extends Controller
+{
+	/**
+	 * Displays available commands or the detailed information
+	 * about a particular command. For example,
+	 *
+	 * @param string $command The name of the command to show help about.
+	 * If not provided, all available commands will be displayed.
+	 * @return integer the exit status
+	 * @throws Exception if the command for help is unknown
+	 */
+	public function actionIndex($command = null)
+	{
+		if ($command !== null) {
+			$result = Yii::$app->createController($command);
+			if ($result === false) {
+				throw new Exception(Yii::t('yii', 'No help for unknown command "{command}".', [
+					'command' => $this->ansiFormat($command, Console::FG_YELLOW),
+				]));
+			}
+
+			list($controller, $actionID) = $result;
+
+			$actions = $this->getActions($controller);
+			if ($actionID !== '' || count($actions) === 1 && $actions[0] === $controller->defaultAction) {
+				$this->getActionHelp($controller, $actionID);
+			} else {
+				$this->getControllerHelp($controller);
+			}
+		} else {
+			$this->getHelp();
+		}
+	}
+
+	/**
+	 * Returns all available command names.
+	 * @return array all available command names
+	 */
+	public function getCommands()
+	{
+		$commands = $this->getModuleCommands(Yii::$app);
+		sort($commands);
+		return array_unique($commands);
+	}
+
+	/**
+	 * Returns all available actions of the specified controller.
+	 * @param Controller $controller the controller instance
+	 * @return array all available action IDs.
+	 */
+	public function getActions($controller)
+	{
+		$actions = array_keys($controller->actions());
+		$class = new \ReflectionClass($controller);
+		foreach ($class->getMethods() as $method) {
+			$name = $method->getName();
+			if ($method->isPublic() && !$method->isStatic() && strpos($name, 'action') === 0 && $name !== 'actions') {
+				$actions[] = Inflector::camel2id(substr($name, 6));
+			}
+		}
+		sort($actions);
+		return array_unique($actions);
+	}
+
+	/**
+	 * Returns available commands of a specified module.
+	 * @param \yii\base\Module $module the module instance
+	 * @return array the available command names
+	 */
+	protected function getModuleCommands($module)
+	{
+		$prefix = $module instanceof Application ? '' : $module->getUniqueID() . '/';
+
+		$commands = [];
+		foreach (array_keys($module->controllerMap) as $id) {
+			$commands[] = $prefix . $id;
+		}
+
+		foreach ($module->getModules() as $id => $child) {
+			if (($child = $module->getModule($id)) === null) {
+				continue;
+			}
+			foreach ($this->getModuleCommands($child) as $command) {
+				$commands[] = $command;
+			}
+		}
+
+		$controllerPath = $module->getControllerPath();
+		if (is_dir($controllerPath)) {
+			$files = scandir($controllerPath);
+			foreach ($files as $file) {
+				if (strcmp(substr($file, -14), 'Controller.php') === 0) {
+					$commands[] = $prefix . Inflector::camel2id(substr(basename($file), 0, -14));
+				}
+			}
+		}
+
+		return $commands;
+	}
+
+	/**
+	 * Displays all available commands.
+	 */
+	protected function getHelp()
+	{
+		$commands = $this->getCommands();
+		if (!empty($commands)) {
+			$this->stdout("\nThe following commands are available:\n\n", Console::BOLD);
+			foreach ($commands as $command) {
+				echo "- " . $this->ansiFormat($command, Console::FG_YELLOW) . "\n";
+			}
+			$scriptName = $this->getScriptName();
+			$this->stdout("\nTo see the help of each command, enter:\n", Console::BOLD);
+			echo "\n  $scriptName " . $this->ansiFormat('help', Console::FG_YELLOW) . ' '
+							. $this->ansiFormat('<command-name>', Console::FG_CYAN) . "\n\n";
+		} else {
+			$this->stdout("\nNo commands are found.\n\n", Console::BOLD);
+		}
+	}
+
+	/**
+	 * Displays the overall information of the command.
+	 * @param Controller $controller the controller instance
+	 */
+	protected function getControllerHelp($controller)
+	{
+		$class = new \ReflectionClass($controller);
+		$comment = strtr(trim(preg_replace('/^\s*\**( |\t)?/m', '', trim($class->getDocComment(), '/'))), "\r", '');
+		if (preg_match('/^\s*@\w+/m', $comment, $matches, PREG_OFFSET_CAPTURE)) {
+			$comment = trim(substr($comment, 0, $matches[0][1]));
+		}
+
+		if ($comment !== '') {
+			$this->stdout("\nDESCRIPTION\n", Console::BOLD);
+			echo "\n" . Console::renderColoredString($comment) . "\n\n";
+		}
+
+		$actions = $this->getActions($controller);
+		if (!empty($actions)) {
+			$this->stdout("\nSUB-COMMANDS\n\n", Console::BOLD);
+			$prefix = $controller->getUniqueId();
+			foreach ($actions as $action) {
+				echo '- ' . $this->ansiFormat($prefix.'/'.$action, Console::FG_YELLOW);
+				if ($action === $controller->defaultAction) {
+					$this->stdout(' (default)', Console::FG_GREEN);
+				}
+				$summary = $this->getActionSummary($controller, $action);
+				if ($summary !== '') {
+					echo ': ' . $summary;
+				}
+				echo "\n";
+			}
+			$scriptName = $this->getScriptName();
+			echo "\nTo see the detailed information about individual sub-commands, enter:\n";
+			echo "\n  $scriptName " . $this->ansiFormat('help', Console::FG_YELLOW) . ' '
+							. $this->ansiFormat('<sub-command>', Console::FG_CYAN) . "\n\n";
+		}
+	}
+
+	/**
+	 * Returns the short summary of the action.
+	 * @param Controller $controller the controller instance
+	 * @param string $actionID action ID
+	 * @return string the summary about the action
+	 */
+	protected function getActionSummary($controller, $actionID)
+	{
+		$action = $controller->createAction($actionID);
+		if ($action === null) {
+			return '';
+		}
+		if ($action instanceof InlineAction) {
+			$reflection = new \ReflectionMethod($controller, $action->actionMethod);
+		} else {
+			$reflection = new \ReflectionClass($action);
+		}
+		$tags = $this->parseComment($reflection->getDocComment());
+		if ($tags['description'] !== '') {
+			$limit = 73 - strlen($action->getUniqueId());
+			if ($actionID === $controller->defaultAction) {
+				$limit -= 10;
+			}
+			if ($limit < 0) {
+				$limit = 50;
+			}
+			$description = $tags['description'];
+			if (($pos = strpos($tags['description'], "\n")) !== false) {
+				$description = substr($description, 0, $pos);
+			}
+			$text = substr($description, 0, $limit);
+			return strlen($description) > $limit ? $text . '...' : $text;
+		} else {
+			return '';
+		}
+	}
+
+	/**
+	 * Displays the detailed information of a command action.
+	 * @param Controller $controller the controller instance
+	 * @param string $actionID action ID
+	 * @throws Exception if the action does not exist
+	 */
+	protected function getActionHelp($controller, $actionID)
+	{
+		$action = $controller->createAction($actionID);
+		if ($action === null) {
+			throw new Exception(Yii::t('yii', 'No help for unknown sub-command "{command}".', [
+				'command' => rtrim($controller->getUniqueId() . '/' . $actionID, '/'),
+			]));
+		}
+		if ($action instanceof InlineAction) {
+			$method = new \ReflectionMethod($controller, $action->actionMethod);
+		} else {
+			$method = new \ReflectionMethod($action, 'run');
+		}
+
+		$tags = $this->parseComment($method->getDocComment());
+		$options = $this->getOptionHelps($controller);
+
+		if ($tags['description'] !== '') {
+			$this->stdout("\nDESCRIPTION\n", Console::BOLD);
+			echo "\n" . Console::renderColoredString($tags['description']) . "\n\n";
+		}
+
+		$this->stdout("\nUSAGE\n\n", Console::BOLD);
+		$scriptName = $this->getScriptName();
+		if ($action->id === $controller->defaultAction) {
+			echo $scriptName . ' ' . $this->ansiFormat($controller->getUniqueId(), Console::FG_YELLOW);
+		} else {
+			echo $scriptName . ' ' . $this->ansiFormat($action->getUniqueId(), Console::FG_YELLOW);
+		}
+		list ($required, $optional) = $this->getArgHelps($method, isset($tags['param']) ? $tags['param'] : []);
+		foreach ($required as $arg => $description) {
+			$this->stdout(' <' . $arg . '>', Console::FG_CYAN);
+		}
+		foreach ($optional as $arg => $description) {
+			$this->stdout(' [' . $arg . ']', Console::FG_CYAN);
+		}
+		if (!empty($options)) {
+			$this->stdout(' [...options...]', Console::FG_RED);
+		}
+		echo "\n\n";
+
+		if (!empty($required) || !empty($optional)) {
+			echo implode("\n\n", array_merge($required, $optional)) . "\n\n";
+		}
+
+		$options = $this->getOptionHelps($controller);
+		if (!empty($options)) {
+			$this->stdout("\nOPTIONS\n\n", Console::BOLD);
+			echo implode("\n\n", $options) . "\n\n";
+		}
+	}
+
+	/**
+	 * Returns the help information about arguments.
+	 * @param \ReflectionMethod $method
+	 * @param string $tags the parsed comment block related with arguments
+	 * @return array the required and optional argument help information
+	 */
+	protected function getArgHelps($method, $tags)
+	{
+		if (is_string($tags)) {
+			$tags = [$tags];
+		}
+		$params = $method->getParameters();
+		$optional = $required = [];
+		foreach ($params as $i => $param) {
+			$name = $param->getName();
+			$tag = isset($tags[$i]) ? $tags[$i] : '';
+			if (preg_match('/^([^\s]+)\s+(\$\w+\s+)?(.*)/s', $tag, $matches)) {
+				$type = $matches[1];
+				$comment = $matches[3];
+			} else {
+				$type = null;
+				$comment = $tag;
+			}
+			if ($param->isDefaultValueAvailable()) {
+				$optional[$name] = $this->formatOptionHelp('- ' . $this->ansiFormat($name, Console::FG_CYAN), false, $type, $param->getDefaultValue(), $comment);
+			} else {
+				$required[$name] = $this->formatOptionHelp('- ' . $this->ansiFormat($name, Console::FG_CYAN), true, $type, null, $comment);
+			}
+		}
+
+		return [$required, $optional];
+	}
+
+	/**
+	 * Returns the help information about the options available for a console controller.
+	 * @param Controller $controller the console controller
+	 * @return array the help information about the options
+	 */
+	protected function getOptionHelps($controller)
+	{
+		$optionNames = $controller->globalOptions();
+		if (empty($optionNames)) {
+			return [];
+		}
+
+		$class = new \ReflectionClass($controller);
+		$options = [];
+		foreach ($class->getProperties() as $property) {
+			$name = $property->getName();
+			if (!in_array($name, $optionNames, true)) {
+				continue;
+			}
+			$defaultValue = $property->getValue($controller);
+			$tags = $this->parseComment($property->getDocComment());
+			if (isset($tags['var']) || isset($tags['property'])) {
+				$doc = isset($tags['var']) ? $tags['var'] : $tags['property'];
+				if (is_array($doc)) {
+					$doc = reset($doc);
+				}
+				if (preg_match('/^([^\s]+)(.*)/s', $doc, $matches)) {
+					$type = $matches[1];
+					$comment = $matches[2];
+				} else {
+					$type = null;
+					$comment = $doc;
+				}
+				$options[$name] = $this->formatOptionHelp($this->ansiFormat('--' . $name, Console::FG_RED), false, $type, $defaultValue, $comment);
+			} else {
+				$options[$name] = $this->formatOptionHelp($this->ansiFormat('--' . $name, Console::FG_RED), false, null, $defaultValue, '');
+			}
+		}
+		ksort($options);
+		return $options;
+	}
+
+	/**
+	 * Parses the comment block into tags.
+	 * @param string $comment the comment block
+	 * @return array the parsed tags
+	 */
+	protected function parseComment($comment)
+	{
+		$tags = [];
+		$comment = "@description \n" . strtr(trim(preg_replace('/^\s*\**( |\t)?/m', '', trim($comment, '/'))), "\r", '');
+		$parts = preg_split('/^\s*@/m', $comment, -1, PREG_SPLIT_NO_EMPTY);
+		foreach ($parts as $part) {
+			if (preg_match('/^(\w+)(.*)/ms', trim($part), $matches)) {
+				$name = $matches[1];
+				if (!isset($tags[$name])) {
+					$tags[$name] = trim($matches[2]);
+				} elseif (is_array($tags[$name])) {
+					$tags[$name][] = trim($matches[2]);
+				} else {
+					$tags[$name] = [$tags[$name], trim($matches[2])];
+				}
+			}
+		}
+		return $tags;
+	}
+
+	/**
+	 * Generates a well-formed string for an argument or option.
+	 * @param string $name the name of the argument or option
+	 * @param boolean $required whether the argument is required
+	 * @param string $type the type of the option or argument
+	 * @param mixed $defaultValue the default value of the option or argument
+	 * @param string $comment comment about the option or argument
+	 * @return string the formatted string for the argument or option
+	 */
+	protected function formatOptionHelp($name, $required, $type, $defaultValue, $comment)
+	{
+		$doc = '';
+		$comment = trim($comment);
+
+		if ($defaultValue !== null && !is_array($defaultValue)) {
+			if ($type === null) {
+				$type = gettype($defaultValue);
+			}
+			if (is_bool($defaultValue)) {
+				// show as integer to avoid confusion
+				$defaultValue = (int)$defaultValue;
+			}
+			$doc = "$type (defaults to " . var_export($defaultValue, true) . ")";
+		} elseif (trim($type) !== '') {
+			$doc = $type;
+		}
+
+		if ($doc === '') {
+			$doc = $comment;
+		} elseif ($comment !== '') {
+			$doc .= "\n" . preg_replace("/^/m", "  ", $comment);
+		}
+
+		$name = $required ? "$name (required)" : $name;
+		return $doc === '' ? $name : "$name: $doc";
+	}
+
+	/**
+	 * @return string the name of the cli script currently running.
+	 */
+	protected function getScriptName()
+	{
+		return basename(Yii::$app->request->scriptFile);
+	}
+}

+ 288 - 0
php-yii2/app/vendor/yiisoft/yii2/console/controllers/MessageController.php

@@ -0,0 +1,288 @@
+<?php
+/**
+ * @author Qiang Xue <[email protected]>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\console\controllers;
+
+use Yii;
+use yii\console\Controller;
+use yii\console\Exception;
+use yii\helpers\FileHelper;
+
+/**
+ * This command extracts messages to be translated from source files.
+ * The extracted messages are saved either as PHP message source files
+ * or ".po" files under the specified directory. Format depends on `format`
+ * setting in config file.
+ *
+ * Usage:
+ * 1. Create a configuration file using the 'message/config' command:
+ *    yii message/config /path/to/myapp/messages/config.php
+ * 2. Edit the created config file, adjusting it for your web application needs.
+ * 3. Run the 'message/extract' command, using created config:
+ *    yii message /path/to/myapp/messages/config.php
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class MessageController extends Controller
+{
+	/**
+	 * @var string controller default action ID.
+	 */
+	public $defaultAction = 'extract';
+
+
+	/**
+	 * Creates a configuration file for the "extract" command.
+	 *
+	 * The generated configuration file contains detailed instructions on
+	 * how to customize it to fit for your needs. After customization,
+	 * you may use this configuration file with the "extract" command.
+	 *
+	 * @param string $filePath output file name or alias.
+	 * @throws Exception on failure.
+	 */
+	public function actionConfig($filePath)
+	{
+		$filePath = Yii::getAlias($filePath);
+		if (file_exists($filePath)) {
+			if (!$this->confirm("File '{$filePath}' already exists. Do you wish to overwrite it?")) {
+				return;
+			}
+		}
+		copy(Yii::getAlias('@yii/views/messageConfig.php'), $filePath);
+		echo "Configuration file template created at '{$filePath}'.\n\n";
+	}
+
+	/**
+	 * Extracts messages to be translated from source code.
+	 *
+	 * This command will search through source code files and extract
+	 * messages that need to be translated in different languages.
+	 *
+	 * @param string $configFile the path or alias of the configuration file.
+	 * You may use the "yii message/config" command to generate
+	 * this file and then customize it for your needs.
+	 * @throws Exception on failure.
+	 */
+	public function actionExtract($configFile)
+	{
+		$configFile = Yii::getAlias($configFile);
+		if (!is_file($configFile)) {
+			throw new Exception("The configuration file does not exist: $configFile");
+		}
+
+		$config = array_merge([
+			'translator' => 'Yii::t',
+			'overwrite' => false,
+			'removeUnused' => false,
+			'sort' => false,
+			'format' => 'php',
+		], require($configFile));
+
+		if (!isset($config['sourcePath'], $config['messagePath'], $config['languages'])) {
+			throw new Exception('The configuration file must specify "sourcePath", "messagePath" and "languages".');
+		}
+		if (!is_dir($config['sourcePath'])) {
+			throw new Exception("The source path {$config['sourcePath']} is not a valid directory.");
+		}
+		if (!is_dir($config['messagePath'])) {
+			throw new Exception("The message path {$config['messagePath']} is not a valid directory.");
+		}
+		if (empty($config['languages'])) {
+			throw new Exception("Languages cannot be empty.");
+		}
+		if (empty($config['format']) || !in_array($config['format'], ['php', 'po'])) {
+			throw new Exception('Format should be either "php" or "po".');
+		}
+
+		$files = FileHelper::findFiles(realpath($config['sourcePath']), $config);
+
+		$messages = [];
+		foreach ($files as $file) {
+			$messages = array_merge_recursive($messages, $this->extractMessages($file, $config['translator']));
+		}
+
+		foreach ($config['languages'] as $language) {
+			$dir = $config['messagePath'] . DIRECTORY_SEPARATOR . $language;
+			if (!is_dir($dir)) {
+				@mkdir($dir);
+			}
+			foreach ($messages as $category => $msgs) {
+				$file = str_replace("\\", '/', "$dir/$category." . $config['format']);
+				$path = dirname($file);
+				if (!is_dir($path)) {
+					mkdir($path, 0755, true);
+				}
+				$msgs = array_values(array_unique($msgs));
+				$this->generateMessageFile($msgs, $file, $config['overwrite'], $config['removeUnused'], $config['sort'], $config['format']);
+			}
+		}
+	}
+
+	/**
+	 * Extracts messages from a file
+	 *
+	 * @param string $fileName name of the file to extract messages from
+	 * @param string $translator name of the function used to translate messages
+	 * @return array
+	 */
+	protected function extractMessages($fileName, $translator)
+	{
+		echo "Extracting messages from $fileName...\n";
+		$subject = file_get_contents($fileName);
+		$messages = [];
+		if (!is_array($translator)) {
+			$translator = [$translator];
+		}
+		foreach ($translator as $currentTranslator) {
+			$n = preg_match_all(
+				'/\b' . $currentTranslator . '\s*\(\s*(\'.*?(?<!\\\\)\'|".*?(?<!\\\\)")\s*,\s*(\'.*?(?<!\\\\)\'|".*?(?<!\\\\)")\s*[,\)]/s',
+				$subject, $matches, PREG_SET_ORDER);
+			for ($i = 0; $i < $n; ++$i) {
+				if (($pos = strpos($matches[$i][1], '.')) !== false) {
+					$category = substr($matches[$i][1], $pos + 1, -1);
+				} else {
+					$category = substr($matches[$i][1], 1, -1);
+				}
+				$message = $matches[$i][2];
+				$messages[$category][] = eval("return $message;"); // use eval to eliminate quote escape
+			}
+		}
+		return $messages;
+	}
+
+	/**
+	 * Writes messages into file
+	 *
+	 * @param array $messages
+	 * @param string $fileName name of the file to write to
+	 * @param boolean $overwrite if existing file should be overwritten without backup
+	 * @param boolean $removeUnused if obsolete translations should be removed
+	 * @param boolean $sort if translations should be sorted
+	 * @param string $format output format
+	 */
+	protected function generateMessageFile($messages, $fileName, $overwrite, $removeUnused, $sort, $format)
+	{
+		echo "Saving messages to $fileName...";
+		if (is_file($fileName)) {
+			if($format === 'po'){
+				$translated = file_get_contents($fileName);
+				preg_match_all('/(?<=msgid ").*(?="\n(#*)msgstr)/', $translated, $keys);
+				preg_match_all('/(?<=msgstr ").*(?="\n\n)/', $translated, $values);
+				$translated = array_combine($keys[0], $values[0]);
+			} else {
+				$translated = require($fileName);
+			}
+			sort($messages);
+			ksort($translated);
+			if (array_keys($translated) == $messages) {
+				echo "nothing new...skipped.\n";
+				return;
+			}
+			$merged = [];
+			$untranslated = [];
+			foreach ($messages as $message) {
+				if($format === 'po'){
+					$message = preg_replace('/\"/', '\"', $message);
+				}
+				if (array_key_exists($message, $translated) && strlen($translated[$message]) > 0) {
+					$merged[$message] = $translated[$message];
+				} else {
+					$untranslated[] = $message;
+				}
+			}
+			ksort($merged);
+			sort($untranslated);
+			$todo = [];
+			foreach ($untranslated as $message) {
+				$todo[$message] = '';
+			}
+			ksort($translated);
+			foreach ($translated as $message => $translation) {
+				if (!isset($merged[$message]) && !isset($todo[$message]) && !$removeUnused) {
+					if (substr($translation, 0, 2) === '@@' && substr($translation, -2) === '@@') {
+						$todo[$message] = $translation;
+					} else {
+						$todo[$message] = '@@' . $translation . '@@';
+					}
+				}
+			}
+			$merged = array_merge($todo, $merged);
+			if ($sort) {
+				ksort($merged);
+			}
+			if (false === $overwrite) {
+				$fileName .= '.merged';
+			}
+			if ($format === 'po'){
+				$out_str = '';
+				foreach ($merged as $k => $v){
+					$k = preg_replace('/(\")|(\\\")/', "\\\"", $k);
+					$v = preg_replace('/(\")|(\\\")/', "\\\"", $v);
+					if (substr($v, 0, 2) === '@@' && substr($v, -2) === '@@') {
+						$out_str .= "#msgid \"$k\"\n";
+						$out_str .= "#msgstr \"$v\"\n";
+					} else {
+						$out_str .= "msgid \"$k\"\n";
+						$out_str .= "msgstr \"$v\"\n";
+					}
+					$out_str .= "\n";
+				}
+				$merged = $out_str;
+			}
+			echo "translation merged.\n";
+		} else {
+			if ($format === 'po') {
+				$merged = '';
+				sort($messages);
+				foreach($messages as $message) {
+					$message = preg_replace('/(\")|(\\\")/', '\\\"', $message);
+					$merged .= "msgid \"$message\"\n";
+					$merged .= "msgstr \"\"\n";
+					$merged .= "\n";
+				}
+			} else {
+				$merged = [];
+				foreach ($messages as $message) {
+					$merged[$message] = '';
+				}
+				ksort($merged);
+			}
+			echo "saved.\n";
+		}
+		if ($format === 'po') {
+			$content = $merged;
+		} else {
+			$array = str_replace("\r", '', var_export($merged, true));
+			$content = <<<EOD
+<?php
+/**
+ * Message translations.
+ *
+ * This file is automatically generated by 'yii {$this->id}' command.
+ * It contains the localizable messages extracted from source code.
+ * You may modify this file by translating the extracted messages.
+ *
+ * Each array element represents the translation (value) of a message (key).
+ * If the value is empty, the message is considered as not translated.
+ * Messages that no longer need translation will have their translations
+ * enclosed between a pair of '@@' marks.
+ *
+ * Message string can be used with plural forms format. Check i18n section
+ * of the guide for details.
+ *
+ * NOTE: this file must be saved in UTF-8 encoding.
+ */
+return $array;
+
+EOD;
+		}
+		file_put_contents($fileName, $content);
+	}
+}

+ 633 - 0
php-yii2/app/vendor/yiisoft/yii2/console/controllers/MigrateController.php

@@ -0,0 +1,633 @@
+<?php
+/**
+ * @author Qiang Xue <[email protected]>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\console\controllers;
+
+use Yii;
+use yii\console\Exception;
+use yii\console\Controller;
+use yii\db\Connection;
+use yii\db\Query;
+use yii\helpers\ArrayHelper;
+
+/**
+ * This command manages application migrations.
+ *
+ * A migration means a set of persistent changes to the application environment
+ * that is shared among different developers. For example, in an application
+ * backed by a database, a migration may refer to a set of changes to
+ * the database, such as creating a new table, adding a new table column.
+ *
+ * This command provides support for tracking the migration history, upgrading
+ * or downloading with migrations, and creating new migration skeletons.
+ *
+ * The migration history is stored in a database table named
+ * as [[migrationTable]]. The table will be automatically created the first time
+ * this command is executed, if it does not exist. You may also manually
+ * create it as follows:
+ *
+ * ~~~
+ * CREATE TABLE tbl_migration (
+ *     version varchar(180) PRIMARY KEY,
+ *     apply_time integer
+ * )
+ * ~~~
+ *
+ * Below are some common usages of this command:
+ *
+ * ~~~
+ * # creates a new migration named 'create_user_table'
+ * yii migrate/create create_user_table
+ *
+ * # applies ALL new migrations
+ * yii migrate
+ *
+ * # reverts the last applied migration
+ * yii migrate/down
+ * ~~~
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class MigrateController extends Controller
+{
+	/**
+	 * The name of the dummy migration that marks the beginning of the whole migration history.
+	 */
+	const BASE_MIGRATION = 'm000000_000000_base';
+
+	/**
+	 * @var string the default command action.
+	 */
+	public $defaultAction = 'up';
+	/**
+	 * @var string the directory storing the migration classes. This can be either
+	 * a path alias or a directory.
+	 */
+	public $migrationPath = '@app/migrations';
+	/**
+	 * @var string the name of the table for keeping applied migration information.
+	 */
+	public $migrationTable = '{{%migration}}';
+	/**
+	 * @var string the template file for generating new migrations.
+	 * This can be either a path alias (e.g. "@app/migrations/template.php")
+	 * or a file path.
+	 */
+	public $templateFile = '@yii/views/migration.php';
+	/**
+	 * @var boolean whether to execute the migration in an interactive mode.
+	 */
+	public $interactive = true;
+	/**
+	 * @var Connection|string the DB connection object or the application
+	 * component ID of the DB connection.
+	 */
+	public $db = 'db';
+
+	/**
+	 * Returns the names of the global options for this command.
+	 * @return array the names of the global options for this command.
+	 */
+	public function globalOptions()
+	{
+		return array_merge(parent::globalOptions(), [
+			'migrationPath', 'migrationTable', 'db', 'templateFile', 'interactive', 'color'
+		]);
+	}
+
+	/**
+	 * This method is invoked right before an action is to be executed (after all possible filters.)
+	 * It checks the existence of the [[migrationPath]].
+	 * @param \yii\base\Action $action the action to be executed.
+	 * @return boolean whether the action should continue to be executed.
+	 * @throws Exception if the migration directory does not exist.
+	 */
+	public function beforeAction($action)
+	{
+		if (parent::beforeAction($action)) {
+			$path = Yii::getAlias($this->migrationPath);
+			if (!is_dir($path)) {
+				throw new Exception("The migration directory \"{$path}\" does not exist.");
+			}
+			$this->migrationPath = $path;
+
+			if ($action->id !== 'create') {
+				if (is_string($this->db)) {
+					$this->db = Yii::$app->getComponent($this->db);
+				}
+				if (!$this->db instanceof Connection) {
+					throw new Exception("The 'db' option must refer to the application component ID of a DB connection.");
+				}
+			}
+
+			$version = Yii::getVersion();
+			echo "Yii Migration Tool (based on Yii v{$version})\n\n";
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * Upgrades the application by applying new migrations.
+	 * For example,
+	 *
+	 * ~~~
+	 * yii migrate     # apply all new migrations
+	 * yii migrate 3   # apply the first 3 new migrations
+	 * ~~~
+	 *
+	 * @param integer $limit the number of new migrations to be applied. If 0, it means
+	 * applying all available new migrations.
+	 */
+	public function actionUp($limit = 0)
+	{
+		$migrations = $this->getNewMigrations();
+		if (empty($migrations)) {
+			echo "No new migration found. Your system is up-to-date.\n";
+			return;
+		}
+
+		$total = count($migrations);
+		$limit = (int)$limit;
+		if ($limit > 0) {
+			$migrations = array_slice($migrations, 0, $limit);
+		}
+
+		$n = count($migrations);
+		if ($n === $total) {
+			echo "Total $n new " . ($n === 1 ? 'migration' : 'migrations') . " to be applied:\n";
+		} else {
+			echo "Total $n out of $total new " . ($total === 1 ? 'migration' : 'migrations') . " to be applied:\n";
+		}
+
+		foreach ($migrations as $migration) {
+			echo "    $migration\n";
+		}
+		echo "\n";
+
+		if ($this->confirm('Apply the above ' . ($n === 1 ? 'migration' : 'migrations') . "?")) {
+			foreach ($migrations as $migration) {
+				if (!$this->migrateUp($migration)) {
+					echo "\nMigration failed. The rest of the migrations are canceled.\n";
+					return;
+				}
+			}
+			echo "\nMigrated up successfully.\n";
+		}
+	}
+
+	/**
+	 * Downgrades the application by reverting old migrations.
+	 * For example,
+	 *
+	 * ~~~
+	 * yii migrate/down     # revert the last migration
+	 * yii migrate/down 3   # revert the last 3 migrations
+	 * ~~~
+	 *
+	 * @param integer $limit the number of migrations to be reverted. Defaults to 1,
+	 * meaning the last applied migration will be reverted.
+	 * @throws Exception if the number of the steps specified is less than 1.
+	 */
+	public function actionDown($limit = 1)
+	{
+		$limit = (int)$limit;
+		if ($limit < 1) {
+			throw new Exception("The step argument must be greater than 0.");
+		}
+
+		$migrations = $this->getMigrationHistory($limit);
+		if (empty($migrations)) {
+			echo "No migration has been done before.\n";
+			return;
+		}
+		$migrations = array_keys($migrations);
+
+		$n = count($migrations);
+		echo "Total $n " . ($n === 1 ? 'migration' : 'migrations') . " to be reverted:\n";
+		foreach ($migrations as $migration) {
+			echo "    $migration\n";
+		}
+		echo "\n";
+
+		if ($this->confirm('Revert the above ' . ($n === 1 ? 'migration' : 'migrations') . "?")) {
+			foreach ($migrations as $migration) {
+				if (!$this->migrateDown($migration)) {
+					echo "\nMigration failed. The rest of the migrations are canceled.\n";
+					return;
+				}
+			}
+			echo "\nMigrated down successfully.\n";
+		}
+	}
+
+	/**
+	 * Redoes the last few migrations.
+	 *
+	 * This command will first revert the specified migrations, and then apply
+	 * them again. For example,
+	 *
+	 * ~~~
+	 * yii migrate/redo     # redo the last applied migration
+	 * yii migrate/redo 3   # redo the last 3 applied migrations
+	 * ~~~
+	 *
+	 * @param integer $limit the number of migrations to be redone. Defaults to 1,
+	 * meaning the last applied migration will be redone.
+	 * @throws Exception if the number of the steps specified is less than 1.
+	 */
+	public function actionRedo($limit = 1)
+	{
+		$limit = (int)$limit;
+		if ($limit < 1) {
+			throw new Exception("The step argument must be greater than 0.");
+		}
+
+		$migrations = $this->getMigrationHistory($limit);
+		if (empty($migrations)) {
+			echo "No migration has been done before.\n";
+			return;
+		}
+		$migrations = array_keys($migrations);
+
+		$n = count($migrations);
+		echo "Total $n " . ($n === 1 ? 'migration' : 'migrations') . " to be redone:\n";
+		foreach ($migrations as $migration) {
+			echo "    $migration\n";
+		}
+		echo "\n";
+
+		if ($this->confirm('Redo the above ' . ($n === 1 ? 'migration' : 'migrations') . "?")) {
+			foreach ($migrations as $migration) {
+				if (!$this->migrateDown($migration)) {
+					echo "\nMigration failed. The rest of the migrations are canceled.\n";
+					return;
+				}
+			}
+			foreach (array_reverse($migrations) as $migration) {
+				if (!$this->migrateUp($migration)) {
+					echo "\nMigration failed. The rest of the migrations migrations are canceled.\n";
+					return;
+				}
+			}
+			echo "\nMigration redone successfully.\n";
+		}
+	}
+
+	/**
+	 * Upgrades or downgrades till the specified version.
+	 *
+	 * This command will first revert the specified migrations, and then apply
+	 * them again. For example,
+	 *
+	 * ~~~
+	 * yii migrate/to 101129_185401                      # using timestamp
+	 * yii migrate/to m101129_185401_create_user_table   # using full name
+	 * ~~~
+	 *
+	 * @param string $version the version name that the application should be migrated to.
+	 * This can be either the timestamp or the full name of the migration.
+	 * @throws Exception if the version argument is invalid
+	 */
+	public function actionTo($version)
+	{
+		$originalVersion = $version;
+		if (preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/', $version, $matches)) {
+			$version = 'm' . $matches[1];
+		} else {
+			throw new Exception("The version argument must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table).");
+		}
+
+		// try migrate up
+		$migrations = $this->getNewMigrations();
+		foreach ($migrations as $i => $migration) {
+			if (strpos($migration, $version . '_') === 0) {
+				$this->actionUp($i + 1);
+				return;
+			}
+		}
+
+		// try migrate down
+		$migrations = array_keys($this->getMigrationHistory(-1));
+		foreach ($migrations as $i => $migration) {
+			if (strpos($migration, $version . '_') === 0) {
+				if ($i === 0) {
+					echo "Already at '$originalVersion'. Nothing needs to be done.\n";
+				} else {
+					$this->actionDown($i);
+				}
+				return;
+			}
+		}
+
+		throw new Exception("Unable to find the version '$originalVersion'.");
+	}
+
+	/**
+	 * Modifies the migration history to the specified version.
+	 *
+	 * No actual migration will be performed.
+	 *
+	 * ~~~
+	 * yii migrate/mark 101129_185401                      # using timestamp
+	 * yii migrate/mark m101129_185401_create_user_table   # using full name
+	 * ~~~
+	 *
+	 * @param string $version the version at which the migration history should be marked.
+	 * This can be either the timestamp or the full name of the migration.
+	 * @throws Exception if the version argument is invalid or the version cannot be found.
+	 */
+	public function actionMark($version)
+	{
+		$originalVersion = $version;
+		if (preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/', $version, $matches)) {
+			$version = 'm' . $matches[1];
+		} else {
+			throw new Exception("The version argument must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table).");
+		}
+
+		// try mark up
+		$migrations = $this->getNewMigrations();
+		foreach ($migrations as $i => $migration) {
+			if (strpos($migration, $version . '_') === 0) {
+				if ($this->confirm("Set migration history at $originalVersion?")) {
+					$command = $this->db->createCommand();
+					for ($j = 0; $j <= $i; ++$j) {
+						$command->insert($this->migrationTable, [
+							'version' => $migrations[$j],
+							'apply_time' => time(),
+						])->execute();
+					}
+					echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n";
+				}
+				return;
+			}
+		}
+
+		// try mark down
+		$migrations = array_keys($this->getMigrationHistory(-1));
+		foreach ($migrations as $i => $migration) {
+			if (strpos($migration, $version . '_') === 0) {
+				if ($i === 0) {
+					echo "Already at '$originalVersion'. Nothing needs to be done.\n";
+				} else {
+					if ($this->confirm("Set migration history at $originalVersion?")) {
+						$command = $this->db->createCommand();
+						for ($j = 0; $j < $i; ++$j) {
+							$command->delete($this->migrationTable, [
+								'version' => $migrations[$j],
+							])->execute();
+						}
+						echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n";
+					}
+				}
+				return;
+			}
+		}
+
+		throw new Exception("Unable to find the version '$originalVersion'.");
+	}
+
+	/**
+	 * Displays the migration history.
+	 *
+	 * This command will show the list of migrations that have been applied
+	 * so far. For example,
+	 *
+	 * ~~~
+	 * yii migrate/history     # showing the last 10 migrations
+	 * yii migrate/history 5   # showing the last 5 migrations
+	 * yii migrate/history 0   # showing the whole history
+	 * ~~~
+	 *
+	 * @param integer $limit the maximum number of migrations to be displayed.
+	 * If it is 0, the whole migration history will be displayed.
+	 */
+	public function actionHistory($limit = 10)
+	{
+		$limit = (int)$limit;
+		$migrations = $this->getMigrationHistory($limit);
+		if (empty($migrations)) {
+			echo "No migration has been done before.\n";
+		} else {
+			$n = count($migrations);
+			if ($limit > 0) {
+				echo "Showing the last $n applied " . ($n === 1 ? 'migration' : 'migrations') . ":\n";
+			} else {
+				echo "Total $n " . ($n === 1 ? 'migration has' : 'migrations have') . " been applied before:\n";
+			}
+			foreach ($migrations as $version => $time) {
+				echo "    (" . date('Y-m-d H:i:s', $time) . ') ' . $version . "\n";
+			}
+		}
+	}
+
+	/**
+	 * Displays the un-applied new migrations.
+	 *
+	 * This command will show the new migrations that have not been applied.
+	 * For example,
+	 *
+	 * ~~~
+	 * yii migrate/new     # showing the first 10 new migrations
+	 * yii migrate/new 5   # showing the first 5 new migrations
+	 * yii migrate/new 0   # showing all new migrations
+	 * ~~~
+	 *
+	 * @param integer $limit the maximum number of new migrations to be displayed.
+	 * If it is 0, all available new migrations will be displayed.
+	 */
+	public function actionNew($limit = 10)
+	{
+		$limit = (int)$limit;
+		$migrations = $this->getNewMigrations();
+		if (empty($migrations)) {
+			echo "No new migrations found. Your system is up-to-date.\n";
+		} else {
+			$n = count($migrations);
+			if ($limit > 0 && $n > $limit) {
+				$migrations = array_slice($migrations, 0, $limit);
+				echo "Showing $limit out of $n new " . ($n === 1 ? 'migration' : 'migrations') . ":\n";
+			} else {
+				echo "Found $n new " . ($n === 1 ? 'migration' : 'migrations') . ":\n";
+			}
+
+			foreach ($migrations as $migration) {
+				echo "    " . $migration . "\n";
+			}
+		}
+	}
+
+	/**
+	 * Creates a new migration.
+	 *
+	 * This command creates a new migration using the available migration template.
+	 * After using this command, developers should modify the created migration
+	 * skeleton by filling up the actual migration logic.
+	 *
+	 * ~~~
+	 * yii migrate/create create_user_table
+	 * ~~~
+	 *
+	 * @param string $name the name of the new migration. This should only contain
+	 * letters, digits and/or underscores.
+	 * @throws Exception if the name argument is invalid.
+	 */
+	public function actionCreate($name)
+	{
+		if (!preg_match('/^\w+$/', $name)) {
+			throw new Exception("The migration name should contain letters, digits and/or underscore characters only.");
+		}
+
+		$name = 'm' . gmdate('ymd_His') . '_' . $name;
+		$file = $this->migrationPath . DIRECTORY_SEPARATOR . $name . '.php';
+
+		if ($this->confirm("Create new migration '$file'?")) {
+			$content = $this->renderFile(Yii::getAlias($this->templateFile), ['className' => $name]);
+			file_put_contents($file, $content);
+			echo "New migration created successfully.\n";
+		}
+	}
+
+	/**
+	 * Upgrades with the specified migration class.
+	 * @param string $class the migration class name
+	 * @return boolean whether the migration is successful
+	 */
+	protected function migrateUp($class)
+	{
+		if ($class === self::BASE_MIGRATION) {
+			return true;
+		}
+
+		echo "*** applying $class\n";
+		$start = microtime(true);
+		$migration = $this->createMigration($class);
+		if ($migration->up() !== false) {
+			$this->db->createCommand()->insert($this->migrationTable, [
+				'version' => $class,
+				'apply_time' => time(),
+			])->execute();
+			$time = microtime(true) - $start;
+			echo "*** applied $class (time: " . sprintf("%.3f", $time) . "s)\n\n";
+			return true;
+		} else {
+			$time = microtime(true) - $start;
+			echo "*** failed to apply $class (time: " . sprintf("%.3f", $time) . "s)\n\n";
+			return false;
+		}
+	}
+
+	/**
+	 * Downgrades with the specified migration class.
+	 * @param string $class the migration class name
+	 * @return boolean whether the migration is successful
+	 */
+	protected function migrateDown($class)
+	{
+		if ($class === self::BASE_MIGRATION) {
+			return true;
+		}
+
+		echo "*** reverting $class\n";
+		$start = microtime(true);
+		$migration = $this->createMigration($class);
+		if ($migration->down() !== false) {
+			$this->db->createCommand()->delete($this->migrationTable, [
+				'version' => $class,
+			])->execute();
+			$time = microtime(true) - $start;
+			echo "*** reverted $class (time: " . sprintf("%.3f", $time) . "s)\n\n";
+			return true;
+		} else {
+			$time = microtime(true) - $start;
+			echo "*** failed to revert $class (time: " . sprintf("%.3f", $time) . "s)\n\n";
+			return false;
+		}
+	}
+
+	/**
+	 * Creates a new migration instance.
+	 * @param string $class the migration class name
+	 * @return \yii\db\Migration the migration instance
+	 */
+	protected function createMigration($class)
+	{
+		$file = $this->migrationPath . DIRECTORY_SEPARATOR . $class . '.php';
+		require_once($file);
+		return new $class(['db' => $this->db]);
+	}
+
+	/**
+	 * Returns the migration history.
+	 * @param integer $limit the maximum number of records in the history to be returned
+	 * @return array the migration history
+	 */
+	protected function getMigrationHistory($limit)
+	{
+		if ($this->db->schema->getTableSchema($this->migrationTable, true) === null) {
+			$this->createMigrationHistoryTable();
+		}
+		$query = new Query;
+		$rows = $query->select(['version', 'apply_time'])
+			->from($this->migrationTable)
+			->orderBy('version DESC')
+			->limit($limit)
+			->createCommand($this->db)
+			->queryAll();
+		$history = ArrayHelper::map($rows, 'version', 'apply_time');
+		unset($history[self::BASE_MIGRATION]);
+		return $history;
+	}
+
+	/**
+	 * Creates the migration history table.
+	 */
+	protected function createMigrationHistoryTable()
+	{
+		echo 'Creating migration history table "' . $this->migrationTable . '"...';
+		$this->db->createCommand()->createTable($this->migrationTable, [
+			'version' => 'varchar(180) NOT NULL PRIMARY KEY',
+			'apply_time' => 'integer',
+		])->execute();
+		$this->db->createCommand()->insert($this->migrationTable, [
+			'version' => self::BASE_MIGRATION,
+			'apply_time' => time(),
+		])->execute();
+		echo "done.\n";
+	}
+
+	/**
+	 * Returns the migrations that are not applied.
+	 * @return array list of new migrations
+	 */
+	protected function getNewMigrations()
+	{
+		$applied = [];
+		foreach ($this->getMigrationHistory(-1) as $version => $time) {
+			$applied[substr($version, 1, 13)] = true;
+		}
+
+		$migrations = [];
+		$handle = opendir($this->migrationPath);
+		while (($file = readdir($handle)) !== false) {
+			if ($file === '.' || $file === '..') {
+				continue;
+			}
+			$path = $this->migrationPath . DIRECTORY_SEPARATOR . $file;
+			if (preg_match('/^(m(\d{6}_\d{6})_.*?)\.php$/', $file, $matches) && is_file($path) && !isset($applied[$matches[2]])) {
+				$migrations[] = $matches[1];
+			}
+		}
+		closedir($handle);
+		sort($migrations);
+		return $migrations;
+	}
+}

+ 182 - 0
php-yii2/app/vendor/yiisoft/yii2/data/ActiveDataProvider.php

@@ -0,0 +1,182 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\data;
+
+use Yii;
+use yii\db\ActiveQueryInterface;
+use yii\base\InvalidConfigException;
+use yii\base\Model;
+use yii\db\Connection;
+use yii\db\QueryInterface;
+
+/**
+ * ActiveDataProvider implements a data provider based on [[\yii\db\Query]] and [[\yii\db\ActiveQuery]].
+ *
+ * ActiveDataProvider provides data by performing DB queries using [[query]].
+ *
+ * The following is an example of using ActiveDataProvider to provide ActiveRecord instances:
+ *
+ * ~~~
+ * $provider = new ActiveDataProvider([
+ *     'query' => Post::find(),
+ *     'pagination' => [
+ *         'pageSize' => 20,
+ *     ],
+ * ]);
+ *
+ * // get the posts in the current page
+ * $posts = $provider->getModels();
+ * ~~~
+ *
+ * And the following example shows how to use ActiveDataProvider without ActiveRecord:
+ *
+ * ~~~
+ * $query = new Query;
+ * $provider = new ActiveDataProvider([
+ *     'query' => $query->from('tbl_post'),
+ *     'pagination' => [
+ *         'pageSize' => 20,
+ *     ],
+ * ]);
+ *
+ * // get the posts in the current page
+ * $posts = $provider->getModels();
+ * ~~~
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class ActiveDataProvider extends BaseDataProvider
+{
+	/**
+	 * @var QueryInterface the query that is used to fetch data models and [[totalCount]]
+	 * if it is not explicitly set.
+	 */
+	public $query;
+	/**
+	 * @var string|callable the column that is used as the key of the data models.
+	 * This can be either a column name, or a callable that returns the key value of a given data model.
+	 *
+	 * If this is not set, the following rules will be used to determine the keys of the data models:
+	 *
+	 * - If [[query]] is an [[ActiveQuery]] instance, the primary keys of [[ActiveQuery::modelClass]] will be used.
+	 * - Otherwise, the keys of the [[models]] array will be used.
+	 *
+	 * @see getKeys()
+	 */
+	public $key;
+	/**
+	 * @var Connection|string the DB connection object or the application component ID of the DB connection.
+	 * If not set, the default DB connection will be used.
+	 */
+	public $db;
+
+	/**
+	 * Initializes the DB connection component.
+	 * This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
+	 * @throws InvalidConfigException if [[db]] is invalid.
+	 */
+	public function init()
+	{
+		parent::init();
+		if (is_string($this->db)) {
+			$this->db = Yii::$app->getComponent($this->db);
+			if ($this->db === null) {
+				throw new InvalidConfigException('The "db" property must be a valid DB Connection application component.');
+			}
+		}
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	protected function prepareModels()
+	{
+		if (!$this->query instanceof QueryInterface) {
+			throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.');
+		}
+		if (($pagination = $this->getPagination()) !== false) {
+			$pagination->totalCount = $this->getTotalCount();
+			$this->query->limit($pagination->getLimit())->offset($pagination->getOffset());
+		}
+		if (($sort = $this->getSort()) !== false) {
+			$this->query->addOrderBy($sort->getOrders());
+		}
+		return $this->query->all($this->db);
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	protected function prepareKeys($models)
+	{
+		$keys = [];
+		if ($this->key !== null) {
+			foreach ($models as $model) {
+				if (is_string($this->key)) {
+					$keys[] = $model[$this->key];
+				} else {
+					$keys[] = call_user_func($this->key, $model);
+				}
+			}
+			return $keys;
+		} elseif ($this->query instanceof ActiveQueryInterface) {
+			/** @var \yii\db\ActiveRecord $class */
+			$class = $this->query->modelClass;
+			$pks = $class::primaryKey();
+			if (count($pks) === 1) {
+				$pk = $pks[0];
+				foreach ($models as $model) {
+					$keys[] = $model[$pk];
+				}
+			} else {
+				foreach ($models as $model) {
+					$kk = [];
+					foreach ($pks as $pk) {
+						$kk[$pk] = $model[$pk];
+					}
+					$keys[] = $kk;
+				}
+			}
+			return $keys;
+		} else {
+			return array_keys($models);
+		}
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	protected function prepareTotalCount()
+	{
+		if (!$this->query instanceof QueryInterface) {
+			throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.');
+		}
+		$query = clone $this->query;
+		return (int) $query->limit(-1)->offset(-1)->orderBy([])->count('*', $this->db);
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function setSort($value)
+	{
+		parent::setSort($value);
+		if (($sort = $this->getSort()) !== false && empty($sort->attributes) && $this->query instanceof ActiveQueryInterface) {
+			/** @var Model $model */
+			$model = new $this->query->modelClass;
+			foreach ($model->attributes() as $attribute) {
+				$sort->attributes[$attribute] = [
+					'asc' => [$attribute => SORT_ASC],
+					'desc' => [$attribute => SORT_DESC],
+					'label' => $model->getAttributeLabel($attribute),
+				];
+			}
+		}
+	}
+}

+ 131 - 0
php-yii2/app/vendor/yiisoft/yii2/data/ArrayDataProvider.php

@@ -0,0 +1,131 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\data;
+
+use yii\helpers\ArrayHelper;
+
+/**
+ * ArrayDataProvider implements a data provider based on a data array.
+ *
+ * The [[allModels]] property contains all data models that may be sorted and/or paginated.
+ * ArrayDataProvider will provide the data after sorting and/or pagination.
+ * You may configure the [[sort]] and [[pagination]] properties to
+ * customize the sorting and pagination behaviors.
+ *
+ * Elements in the [[allModels]] array may be either objects (e.g. model objects)
+ * or associative arrays (e.g. query results of DAO).
+ * Make sure to set the [[key]] property to the name of the field that uniquely
+ * identifies a data record or false if you do not have such a field.
+ *
+ * Compared to [[ActiveDataProvider]], ArrayDataProvider could be less efficient
+ * because it needs to have [[allModels]] ready.
+ *
+ * ArrayDataProvider may be used in the following way:
+ *
+ * ~~~
+ * $query = new Query;
+ * $provider = new ArrayDataProvider([
+ *     'allModels' => $query->from('tbl_post')->all(),
+ *     'sort' => [
+ *         'attributes' => ['id', 'username', 'email'],
+ *     ],
+ *     'pagination' => [
+ *         'pageSize' => 10,
+ *     ],
+ * ]);
+ * // get the posts in the current page
+ * $posts = $provider->getModels();
+ * ~~~
+ *
+ * Note: if you want to use the sorting feature, you must configure the [[sort]] property
+ * so that the provider knows which columns can be sorted.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class ArrayDataProvider extends BaseDataProvider
+{
+	/**
+	 * @var string|callable the column that is used as the key of the data models.
+	 * This can be either a column name, or a callable that returns the key value of a given data model.
+	 * If this is not set, the index of the [[models]] array will be used.
+	 * @see getKeys()
+	 */
+	public $key;
+	/**
+	 * @var array the data that is not paginated or sorted. When pagination is enabled,
+	 * this property usually contains more elements than [[models]].
+	 * The array elements must use zero-based integer keys.
+	 */
+	public $allModels;
+
+
+	/**
+	 * @inheritdoc
+	 */
+	protected function prepareModels()
+	{
+		if (($models = $this->allModels) === null) {
+			return [];
+		}
+
+		if (($sort = $this->getSort()) !== false) {
+			$models = $this->sortModels($models, $sort);
+		}
+
+		if (($pagination = $this->getPagination()) !== false) {
+			$pagination->totalCount = $this->getTotalCount();
+			$models = array_slice($models, $pagination->getOffset(), $pagination->getLimit());
+		}
+
+		return $models;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	protected function prepareKeys($models)
+	{
+		if ($this->key !== null) {
+			$keys = [];
+			foreach ($models as $model) {
+				if (is_string($this->key)) {
+					$keys[] = $model[$this->key];
+				} else {
+					$keys[] = call_user_func($this->key, $model);
+				}
+			}
+			return $keys;
+		} else {
+			return array_keys($models);
+		}
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	protected function prepareTotalCount()
+	{
+		return count($this->allModels);
+	}
+
+	/**
+	 * Sorts the data models according to the given sort definition
+	 * @param array $models the models to be sorted
+	 * @param Sort $sort the sort definition
+	 * @return array the sorted data models
+	 */
+	protected function sortModels($models, $sort)
+	{
+		$orders = $sort->getOrders();
+		if (!empty($orders)) {
+			ArrayHelper::multisort($models, array_keys($orders), array_values($orders));
+		}
+		return $models;
+	}
+}

+ 249 - 0
php-yii2/app/vendor/yiisoft/yii2/data/BaseDataProvider.php

@@ -0,0 +1,249 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\data;
+
+use Yii;
+use yii\base\Component;
+use yii\base\InvalidParamException;
+
+/**
+ * BaseDataProvider provides a base class that implements the [[DataProviderInterface]].
+ *
+ * @property integer $count The number of data models in the current page. This property is read-only.
+ * @property array $keys The list of key values corresponding to [[models]]. Each data model in [[models]] is
+ * uniquely identified by the corresponding key value in this array.
+ * @property array $models The list of data models in the current page.
+ * @property Pagination|boolean $pagination The pagination object. If this is false, it means the pagination
+ * is disabled. Note that the type of this property differs in getter and setter. See [[getPagination()]] and
+ * [[setPagination()]] for details.
+ * @property Sort|boolean $sort The sorting object. If this is false, it means the sorting is disabled. Note
+ * that the type of this property differs in getter and setter. See [[getSort()]] and [[setSort()]] for details.
+ * @property integer $totalCount Total number of possible data models.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+abstract class BaseDataProvider extends Component implements DataProviderInterface
+{
+	/**
+	 * @var string an ID that uniquely identifies the data provider among all data providers.
+	 * You should set this property if the same page contains two or more different data providers.
+	 * Otherwise, the [[pagination]] and [[sort]] mainly not work properly.
+	 */
+	public $id;
+
+	private $_sort;
+	private $_pagination;
+	private $_keys;
+	private $_models;
+	private $_totalCount;
+
+
+	/**
+	 * Prepares the data models that will be made available in the current page.
+	 * @return array the available data models
+	 */
+	abstract protected function prepareModels();
+
+	/**
+	 * Prepares the keys associated with the currently available data models.
+	 * @param array $models the available data models
+	 * @return array the keys
+	 */
+	abstract protected function prepareKeys($models);
+
+	/**
+	 * Returns a value indicating the total number of data models in this data provider.
+	 * @return integer total number of data models in this data provider.
+	 */
+	abstract protected function prepareTotalCount();
+
+	/**
+	 * Prepares the data models and keys.
+	 *
+	 * This method will prepare the data models and keys that can be retrieved via
+	 * [[getModels()]] and [[getKeys()]].
+	 *
+	 * This method will be implicitly called by [[getModels()]] and [[getKeys()]] if it has not been called before.
+	 *
+	 * @param boolean $forcePrepare whether to force data preparation even if it has been done before.
+	 */
+	public function prepare($forcePrepare = false)
+	{
+		if ($forcePrepare || $this->_models === null) {
+			$this->_models = $this->prepareModels();
+		}
+		if ($forcePrepare || $this->_keys === null) {
+			$this->_keys = $this->prepareKeys($this->_models);
+		}
+	}
+
+	/**
+	 * Returns the data models in the current page.
+	 * @return array the list of data models in the current page.
+	 */
+	public function getModels()
+	{
+		$this->prepare();
+		return $this->_models;
+	}
+
+	/**
+	 * Sets the data models in the current page.
+	 * @param array $models the models in the current page
+	 */
+	public function setModels($models)
+	{
+		$this->_models = $models;
+	}
+
+	/**
+	 * Returns the key values associated with the data models.
+	 * @return array the list of key values corresponding to [[models]]. Each data model in [[models]]
+	 * is uniquely identified by the corresponding key value in this array.
+	 */
+	public function getKeys()
+	{
+		$this->prepare();
+		return $this->_keys;
+	}
+
+	/**
+	 * Sets the key values associated with the data models.
+	 * @param array $keys the list of key values corresponding to [[models]].
+	 */
+	public function setKeys($keys)
+	{
+		$this->_keys = $keys;
+	}
+
+	/**
+	 * Returns the number of data models in the current page.
+	 * @return integer the number of data models in the current page.
+	 */
+	public function getCount()
+	{
+		return count($this->getModels());
+	}
+
+	/**
+	 * Returns the total number of data models.
+	 * When [[pagination]] is false, this returns the same value as [[count]].
+	 * Otherwise, it will call [[prepareTotalCount()]] to get the count.
+	 * @return integer total number of possible data models.
+	 */
+	public function getTotalCount()
+	{
+		if ($this->getPagination() === false) {
+			return $this->getCount();
+		} elseif ($this->_totalCount === null) {
+			$this->_totalCount = $this->prepareTotalCount();
+		}
+		return $this->_totalCount;
+	}
+
+	/**
+	 * Sets the total number of data models.
+	 * @param integer $value the total number of data models.
+	 */
+	public function setTotalCount($value)
+	{
+		$this->_totalCount = $value;
+	}
+
+	/**
+	 * Returns the pagination object used by this data provider.
+	 * Note that you should call [[prepare()]] or [[getModels()]] first to get correct values
+	 * of [[Pagination::totalCount]] and [[Pagination::pageCount]].
+	 * @return Pagination|boolean the pagination object. If this is false, it means the pagination is disabled.
+	 */
+	public function getPagination()
+	{
+		if ($this->_pagination === null) {
+			$this->setPagination([]);
+		}
+		return $this->_pagination;
+	}
+
+	/**
+	 * Sets the pagination for this data provider.
+	 * @param array|Pagination|boolean $value the pagination to be used by this data provider.
+	 * This can be one of the following:
+	 *
+	 * - a configuration array for creating the pagination object. The "class" element defaults
+	 *   to 'yii\data\Pagination'
+	 * - an instance of [[Pagination]] or its subclass
+	 * - false, if pagination needs to be disabled.
+	 *
+	 * @throws InvalidParamException
+	 */
+	public function setPagination($value)
+	{
+		if (is_array($value)) {
+			$config = ['class' => Pagination::className()];
+			if ($this->id !== null) {
+				$config['pageVar'] = $this->id . '-page';
+			}
+			$this->_pagination = Yii::createObject(array_merge($config, $value));
+		} elseif ($value instanceof Pagination || $value === false) {
+			$this->_pagination = $value;
+		} else {
+			throw new InvalidParamException('Only Pagination instance, configuration array or false is allowed.');
+		}
+	}
+
+	/**
+	 * @return Sort|boolean the sorting object. If this is false, it means the sorting is disabled.
+	 */
+	public function getSort()
+	{
+		if ($this->_sort === null) {
+			$this->setSort([]);
+		}
+		return $this->_sort;
+	}
+
+	/**
+	 * Sets the sort definition for this data provider.
+	 * @param array|Sort|boolean $value the sort definition to be used by this data provider.
+	 * This can be one of the following:
+	 *
+	 * - a configuration array for creating the sort definition object. The "class" element defaults
+	 *   to 'yii\data\Sort'
+	 * - an instance of [[Sort]] or its subclass
+	 * - false, if sorting needs to be disabled.
+	 *
+	 * @throws InvalidParamException
+	 */
+	public function setSort($value)
+	{
+		if (is_array($value)) {
+			$config = ['class' => Sort::className()];
+			if ($this->id !== null) {
+				$config['sortVar'] = $this->id . '-sort';
+			}
+			$this->_sort = Yii::createObject(array_merge($config, $value));
+		} elseif ($value instanceof Sort || $value === false) {
+			$this->_sort = $value;
+		} else {
+			throw new InvalidParamException('Only Sort instance, configuration array or false is allowed.');
+		}
+	}
+
+	/**
+	 * Refreshes the data provider.
+	 * After calling this method, if [[getModels()]], [[getKeys()]] or [[getTotalCount()]] is called again,
+	 * they will re-execute the query and return the latest data available.
+	 */
+	public function refresh()
+	{
+		$this->_totalCount = null;
+		$this->_models = null;
+		$this->_keys = null;
+	}
+}

+ 70 - 0
php-yii2/app/vendor/yiisoft/yii2/data/DataProviderInterface.php

@@ -0,0 +1,70 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\data;
+
+/**
+ * DataProviderInterface is the interface that must be implemented by data provider classes.
+ *
+ * Data providers are components that sort and paginate data, and provide them to widgets
+ * such as [[GridView]], [[ListView]].
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+interface DataProviderInterface
+{
+	/**
+	 * Prepares the data models and keys.
+	 *
+	 * This method will prepare the data models and keys that can be retrieved via
+	 * [[getModels()]] and [[getKeys()]].
+	 *
+	 * This method will be implicitly called by [[getModels()]] and [[getKeys()]] if it has not been called before.
+	 *
+	 * @param boolean $forcePrepare whether to force data preparation even if it has been done before.
+	 */
+	public function prepare($forcePrepare = false);
+
+	/**
+	 * Returns the number of data models in the current page.
+	 * This is equivalent to `count($provider->getModels())`.
+	 * When [[pagination]] is false, this is the same as [[totalCount]].
+	 * @return integer the number of data models in the current page.
+	 */
+	public function getCount();
+
+	/**
+	 * Returns the total number of data models.
+	 * When [[pagination]] is false, this is the same as [[count]].
+	 * @return integer total number of possible data models.
+	 */
+	public function getTotalCount();
+
+	/**
+	 * Returns the data models in the current page.
+	 * @return array the list of data models in the current page.
+	 */
+	public function getModels();
+
+	/**
+	 * Returns the key values associated with the data models.
+	 * @return array the list of key values corresponding to [[models]]. Each data model in [[models]]
+	 * is uniquely identified by the corresponding key value in this array.
+	 */
+	public function getKeys();
+
+	/**
+	 * @return Sort the sorting object. If this is false, it means the sorting is disabled.
+	 */
+	public function getSort();
+
+	/**
+	 * @return Pagination the pagination object. If this is false, it means the pagination is disabled.
+	 */
+	public function getPagination();
+}

+ 335 - 0
php-yii2/app/vendor/yiisoft/yii2/data/ModelSerializer.php

@@ -0,0 +1,335 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\data;
+
+use Yii;
+use yii\base\Component;
+use yii\base\Model;
+use yii\helpers\StringHelper;
+
+/**
+ * ModelSerializer converts a model or a list of models into an array representation with selected fields.
+ *
+ * Used together with [[\yii\web\ResponseFormatter]], ModelSerializer can be used to serve model data
+ * in JSON or XML format for REST APIs.
+ *
+ * ModelSerializer provides two methods [[export()]] and [[exportAll()]] to convert model(s) into array(s).
+ * The former works for a single model, while the latter for an array of models.
+ * During conversion, it will check which fields are requested and only provide valid fields (as declared
+ * in [[fields()]] and [[expand()]]) in the array result.
+ *
+ * @author Qiang Xue <[email protected]>
+ * @since 2.0
+ */
+class ModelSerializer extends Component
+{
+	/**
+	 * @var string the model class that this API is serving. If not set, it will be initialized
+	 * as the class of the model(s) being exported by [[export()]] or [[exportAll()]].
+	 */
+	public $modelClass;
+	/**
+	 * @var mixed the context information. If not set, it will be initialized as the "user" application component.
+	 * You can use the context information to conditionally control which fields can be returned for a model.
+	 */
+	public $context;
+	/**
+	 * @var array|string an array or a string of comma separated field names representing
+	 * which fields should be returned. Only fields declared in [[fields()]] will be respected.
+	 * If this property is empty, all fields declared in [[fields()]] will be returned.
+	 */
+	public $fields;
+	/**
+	 * @var array|string an array or a string of comma separated field names representing
+	 * which fields should be returned in addition to those declared in [[fields()]].
+	 * Only fields declared in [[expand()]] will be respected.
+	 */
+	public $expand;
+	/**
+	 * @var integer the error code to be used in the result of [[exportErrors()]].
+	 */
+	public $validationErrorCode = 1024;
+	/**
+	 * @var string the error message to be used in the result of [[exportErrors()]].
+	 */
+	public $validationErrorMessage = 'Validation Failed';
+	/**
+	 * @var array a list of serializer classes indexed by their corresponding model classes.
+	 * This property is used by [[createSerializer()]] to serialize embedded objects.
+	 * @see createSerializer()
+	 */
+	public $serializers = [];
+	/**
+	 * @var array a list of paths or path aliases specifying how to look for a serializer class
+	 * given a model class. If the base name of a model class is `Xyz`, the corresponding
+	 * serializer class being looked for would be `XyzSerializer` under each of the paths listed here.
+	 */
+	public $serializerPaths = ['@app/serializers'];
+	/**
+	 * @var array the loaded serializer objects indexed by the model class names
+	 */
+	private $_serializers = [];
+
+
+	/**
+	 * @inheritdoc
+	 */
+	public function init()
+	{
+		parent::init();
+		if ($this->context === null && Yii::$app) {
+			$this->context = Yii::$app->user;
+		}
+	}
+
+	/**
+	 * Exports a model object by converting it into an array based on the specified fields.
+	 * @param Model $model the model being exported
+	 * @return array the exported data
+	 */
+	public function export($model)
+	{
+		if ($this->modelClass === null) {
+			$this->modelClass = get_class($model);
+		}
+
+		$fields = $this->resolveFields($this->fields, $this->expand);
+		return $this->exportObject($model, $fields);
+	}
+
+	/**
+	 * Exports an array of model objects by converting it into an array based on the specified fields.
+	 * @param Model[] $models the models being exported
+	 * @return array the exported data
+	 */
+	public function exportAll(array $models)
+	{
+		if (empty($models)) {
+			return [];
+		}
+
+		if ($this->modelClass === null) {
+			$this->modelClass = get_class(reset($models));
+		}
+
+		$fields = $this->resolveFields($this->fields, $this->expand);
+		$result = [];
+		foreach ($models as $model) {
+			$result[] = $this->exportObject($model, $fields);
+		}
+		return $result;
+	}
+
+	/**
+	 * Exports the model validation errors.
+	 * @param Model $model
+	 * @return array
+	 */
+	public function exportErrors($model)
+	{
+		$result = [
+			'code' => $this->validationErrorCode,
+			'message' => $this->validationErrorMessage,
+			'errors' => [],
+		];
+		foreach ($model->getFirstErrors() as $name => $message) {
+			$result['errors'][] = [
+				'field' => $name,
+				'message' => $message,
+			];
+		}
+		return $result;
+	}
+
+	/**
+	 * Returns a list of fields that can be returned to end users.
+	 *
+	 * These are the fields that should be returned by default when a user does not explicitly specify which
+	 * fields to return for a model. If the user explicitly which fields to return, only the fields declared
+	 * in this method can be returned. All other fields will be ignored.
+	 *
+	 * By default, this method returns [[Model::attributes()]], which are the attributes defined by a model.
+	 *
+	 * You may override this method to select which fields can be returned or define new fields based
+	 * on model attributes.
+	 *
+	 * The value returned by this method should be an array of field definitions. The array keys
+	 * are the field names, and the array values are the corresponding attribute names or callbacks
+	 * returning field values. If a field name is the same as the corresponding attribute name,
+	 * you can use the field name without a key.
+	 *
+	 * @return array field name => attribute name or definition
+	 */
+	protected function fields()
+	{
+		if (is_subclass_of($this->modelClass, Model::className())) {
+			/** @var Model $model */
+			$model = new $this->modelClass;
+			return $model->attributes();
+		} else {
+			return array_keys(get_class_vars($this->modelClass));
+		}
+	}
+
+	/**
+	 * Returns a list of additional fields that can be returned to end users.
+	 *
+	 * The default implementation returns an empty array. You may override this method to return
+	 * a list of additional fields that can be returned to end users. Please refer to [[fields()]]
+	 * on the format of the return value.
+	 *
+	 * You usually override this method by returning a list of relation names.
+	 *
+	 * @return array field name => attribute name or definition
+	 */
+	protected function expand()
+	{
+		return [];
+	}
+
+	/**
+	 * Filters the data to be exported to end user.
+	 * The default implementation does nothing. You may override this method to remove
+	 * certain fields from the data being exported based on the [[context]] information.
+	 * You may also use this method to add some common fields, such as class name, to the data.
+	 * @param array $data the data being exported
+	 * @return array the filtered data
+	 */
+	protected function filter($data)
+	{
+		return $data;
+	}
+
+	/**
+	 * Returns the serializer for the specified model class.
+	 * @param string $modelClass fully qualified model class name
+	 * @return static the serializer
+	 */
+	protected function getSerializer($modelClass)
+	{
+		if (!isset($this->_serializers[$modelClass])) {
+			$this->_serializers[$modelClass] = $this->createSerializer($modelClass);
+		}
+		return $this->_serializers[$modelClass];
+	}
+
+	/**
+	 * Creates a serializer object for the specified model class.
+	 *
+	 * This method tries to create an appropriate serializer using the following algorithm:
+	 *
+	 * - Check if [[serializers]] specifies the serializer class for the model class and
+	 *   create an instance of it if available;
+	 * - Search for a class named `XyzSerializer` under the paths specified by [[serializerPaths]],
+	 *   where `Xyz` stands for the model class.
+	 * - If both of the above two strategies fail, simply return an instance of `ModelSerializer`.
+	 *
+	 * @param string $modelClass the model class
+	 * @return ModelSerializer the new model serializer
+	 */
+	protected function createSerializer($modelClass)
+	{
+		if (isset($this->serializers[$modelClass])) {
+			$config = $this->serializers[$modelClass];
+			if (!is_array($config)) {
+				$config = ['class' => $config];
+			}
+		} else {
+			$className = StringHelper::basename($modelClass) . 'Serializer';
+			foreach ($this->serializerPaths as $path) {
+				$path = Yii::getAlias($path);
+				if (is_file($path . "/$className.php")) {
+					$config = ['class' => $className];
+					break;
+				}
+			}
+		}
+
+		if (!isset($config)) {
+			$config = ['class' => __CLASS__];
+		}
+		$config['modelClass'] = $modelClass;
+		$config['context'] = $this->context;
+
+		return Yii::createObject($config);
+	}
+
+	/**
+	 * Returns the fields of the model that need to be returned to end user
+	 * @param string|array $fields an array or a string of comma separated field names representing
+	 * which fields should be returned.
+	 * @param string|array $expand an array or a string of comma separated field names representing
+	 * which additional fields should be returned.
+	 * @return array field name => field definition (attribute name or callback)
+	 */
+	protected function resolveFields($fields, $expand)
+	{
+		if (!is_array($fields)) {
+			$fields = preg_split('/\s*,\s*/', $fields, -1, PREG_SPLIT_NO_EMPTY);
+		}
+		if (!is_array($expand)) {
+			$expand = preg_split('/\s*,\s*/', $expand, -1, PREG_SPLIT_NO_EMPTY);
+		}
+
+		$result = [];
+
+		foreach ($this->fields() as $field => $definition) {
+			if (is_integer($field)) {
+				$field = $definition;
+			}
+			if (empty($fields) || in_array($field, $fields, true)) {
+				$result[$field] = $definition;
+			}
+		}
+
+		if (empty($expand)) {
+			return $result;
+		}
+
+		foreach ($this->expand() as $field => $definition) {
+			if (is_integer($field)) {
+				$field = $definition;
+			}
+			if (in_array($field, $expand, true)) {
+				$result[$field] = $definition;
+			}
+		}
+
+		return $result;
+	}
+
+	/**
+	 * Exports an object by converting it into an array based on the given field definitions.
+	 * @param object $model the model being exported
+	 * @param array $fields field definitions (field name => field definition)
+	 * @return array the exported model data
+	 */
+	protected function exportObject($model, $fields)
+	{
+		$data = [];
+		foreach ($fields as $field => $attribute) {
+			if (is_string($attribute)) {
+				$value = $model->$attribute;
+			} else {
+				$value = call_user_func($attribute, $model, $field);
+			}
+			if (is_object($value)) {
+				$value = $this->getSerializer(get_class($value))->export($value);
+			} elseif (is_array($value)) {
+				foreach ($value as $i => $v) {
+					if (is_object($v)) {
+						$value[$i] = $this->getSerializer(get_class($v))->export($v);
+					}
+					// todo: array of array
+				}
+			}
+			$data[$field] = $value;
+		}
+		return $this->filter($data);
+	}
+}

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