Browse Source

Merge branch 'refs/heads/master' into template-engines

tags/2.0.0-beta
Alexander Makarov 12 years ago
parent
commit
f6d405b962
  1. 4
      docs/api/db/ActiveRecord.md
  2. 19
      docs/autoloader.md
  3. 4
      docs/code_style.md
  4. 38
      framework/YiiBase.php
  5. 4
      framework/base/Action.php
  6. 4
      framework/base/ActionEvent.php
  7. 90
      framework/base/ActionFilter.php
  8. 132
      framework/base/Application.php
  9. 4
      framework/base/Behavior.php
  10. 10
      framework/base/Component.php
  11. 110
      framework/base/Controller.php
  12. 14
      framework/base/Dictionary.php
  13. 4
      framework/base/DictionaryIterator.php
  14. 81
      framework/base/ErrorException.php
  15. 19
      framework/base/ErrorHandler.php
  16. 4
      framework/base/Event.php
  17. 5
      framework/base/Exception.php
  18. 4
      framework/base/HttpException.php
  19. 4
      framework/base/InlineAction.php
  20. 4
      framework/base/InvalidCallException.php
  21. 4
      framework/base/InvalidConfigException.php
  22. 26
      framework/base/InvalidParamException.php
  23. 4
      framework/base/InvalidRequestException.php
  24. 4
      framework/base/InvalidRouteException.php
  25. 48
      framework/base/Model.php
  26. 4
      framework/base/ModelEvent.php
  27. 8
      framework/base/Module.php
  28. 4
      framework/base/NotSupportedException.php
  29. 12
      framework/base/Object.php
  30. 35
      framework/base/Request.php
  31. 4
      framework/base/Response.php
  32. 290
      framework/base/SecurityManager.php
  33. 15
      framework/base/Theme.php
  34. 4
      framework/base/UnknownMethodException.php
  35. 4
      framework/base/UnknownPropertyException.php
  36. 4
      framework/base/UserException.php
  37. 24
      framework/base/Vector.php
  38. 4
      framework/base/VectorIterator.php
  39. 565
      framework/base/View.php
  40. 4
      framework/base/ViewRenderer.php
  41. 40
      framework/base/Widget.php
  42. 4
      framework/caching/ApcCache.php
  43. 22
      framework/caching/Cache.php
  44. 4
      framework/caching/ChainedDependency.php
  45. 4
      framework/caching/DbCache.php
  46. 41
      framework/caching/DbDependency.php
  47. 4
      framework/caching/Dependency.php
  48. 4
      framework/caching/DummyCache.php
  49. 4
      framework/caching/ExpressionDependency.php
  50. 7
      framework/caching/FileCache.php
  51. 4
      framework/caching/FileDependency.php
  52. 4
      framework/caching/MemCache.php
  53. 4
      framework/caching/MemCacheServer.php
  54. 4
      framework/caching/WinCache.php
  55. 4
      framework/caching/XCache.php
  56. 4
      framework/caching/ZendDataCache.php
  57. 9
      framework/console/Application.php
  58. 4
      framework/console/Controller.php
  59. 4
      framework/console/Exception.php
  60. 40
      framework/console/Request.php
  61. 24
      framework/console/controllers/AppController.php
  62. 4
      framework/console/controllers/CacheController.php
  63. 6
      framework/console/controllers/HelpController.php
  64. 4
      framework/console/controllers/MessageController.php
  65. 8
      framework/console/controllers/MigrateController.php
  66. 2
      framework/console/webapp/config.php
  67. 0
      framework/console/webapp/default/index.php
  68. 0
      framework/console/webapp/default/protected/config/main.php
  69. 0
      framework/console/webapp/default/protected/controllers/SiteController.php
  70. 0
      framework/console/webapp/default/protected/views/layouts/main.php
  71. 0
      framework/console/webapp/default/protected/views/site/index.php
  72. 4
      framework/db/ActiveQuery.php
  73. 12
      framework/db/ActiveRecord.php
  74. 20
      framework/db/ActiveRelation.php
  75. 4
      framework/db/ColumnSchema.php
  76. 18
      framework/db/Command.php
  77. 8
      framework/db/Connection.php
  78. 4
      framework/db/DataReader.php
  79. 11
      framework/db/Exception.php
  80. 4
      framework/db/Expression.php
  81. 4
      framework/db/Migration.php
  82. 119
      framework/db/Query.php
  83. 90
      framework/db/QueryBuilder.php
  84. 11
      framework/db/Schema.php
  85. 8
      framework/db/TableSchema.php
  86. 4
      framework/db/Transaction.php
  87. 14
      framework/db/mysql/QueryBuilder.php
  88. 4
      framework/db/mysql/Schema.php
  89. 12
      framework/db/sqlite/QueryBuilder.php
  90. 4
      framework/db/sqlite/Schema.php
  91. 78
      framework/helpers/ArrayHelper.php
  92. 16
      framework/helpers/ConsoleColor.php
  93. 6
      framework/helpers/FileHelper.php
  94. 976
      framework/helpers/Html.php
  95. 272
      framework/helpers/SecurityHelper.php
  96. 35
      framework/helpers/StringHelper.php
  97. 134
      framework/helpers/VarDumper.php
  98. 2
      framework/helpers/mimeTypes.php
  99. 108
      framework/i18n/I18N.php
  100. 4
      framework/i18n/MessageSource.php
  101. Some files were not shown because too many files have changed in this diff Show More

4
docs/api/db/ActiveRecord.md

@ -300,7 +300,7 @@ foreach ($customers as $customer) {
~~~ ~~~
How many SQL queries will be performed in the above code, assuming there are more than 100 customers in How many SQL queries will be performed in the above code, assuming there are more than 100 customers in
the database? 101! The first SQL query brings back 100 customers. Then for each customer, another SQL query the database? 101! The first SQL query brings back 100 customers. Then for each customer, a SQL query
is performed to bring back the customer's orders. is performed to bring back the customer's orders.
To solve the above performance problem, you can use the so-called *eager loading* by calling [[ActiveQuery::with()]]: To solve the above performance problem, you can use the so-called *eager loading* by calling [[ActiveQuery::with()]]:
@ -318,7 +318,7 @@ foreach ($customers as $customer) {
} }
~~~ ~~~
As you can see, only two SQL queries were needed for the same task. As you can see, only two SQL queries are needed for the same task.
Sometimes, you may want to customize the relational queries on the fly. It can be Sometimes, you may want to customize the relational queries on the fly. It can be

19
docs/autoloader.md

@ -0,0 +1,19 @@
Yii2 class loader
=================
Yii 2 class loader is PSR-0 compliant. That means it can handle most of the PHP
libraries and frameworks out there.
In order to autoload a library you need to set a root alias for it.
PEAR-style libraries
--------------------
```php
\Yii::setAlias('@Twig', '@app/vendors/Twig');
```
References
----------
- YiiBase::autoload

4
docs/code_style.md

@ -251,10 +251,8 @@ switch ($this->phpType) {
~~~ ~~~
<?php <?php
/** /**
* Component class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
~~~ ~~~

38
framework/YiiBase.php

@ -1,14 +1,12 @@
<?php <?php
/** /**
* YiiBase class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
use yii\base\Exception; use yii\base\Exception;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
use yii\base\InvalidParamException;
use yii\logging\Logger; use yii\logging\Logger;
/** /**
@ -63,7 +61,7 @@ class YiiBase
*/ */
public static $classPath = array(); public static $classPath = array();
/** /**
* @var yii\base\Application the application instance * @var yii\console\Application|yii\web\Application the application instance
*/ */
public static $app; public static $app;
/** /**
@ -161,9 +159,7 @@ class YiiBase
return self::$_imported[$alias] = $className; return self::$_imported[$alias] = $className;
} }
if (($path = static::getAlias(dirname($alias))) === false) { $path = static::getAlias(dirname($alias));
throw new Exception('Invalid path alias: ' . $alias);
}
if ($isClass) { if ($isClass) {
if ($forceInclude) { if ($forceInclude) {
@ -193,25 +189,31 @@ class YiiBase
* *
* Note, this method does not ensure the existence of the resulting path. * Note, this method does not ensure the existence of the resulting path.
* @param string $alias alias * @param string $alias alias
* @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 path corresponding to the alias, false if the root alias is not previously registered. * @return string|boolean path corresponding to the alias, false if the root alias is not previously registered.
* @see setAlias * @see setAlias
*/ */
public static function getAlias($alias) public static function getAlias($alias, $throwException = true)
{ {
if (!is_string($alias)) { if (is_string($alias)) {
return false; if (isset(self::$aliases[$alias])) {
} elseif (isset(self::$aliases[$alias])) {
return self::$aliases[$alias]; return self::$aliases[$alias];
} elseif ($alias === '' || $alias[0] !== '@') { // not an alias } elseif ($alias === '' || $alias[0] !== '@') { // not an alias
return $alias; return $alias;
} elseif (($pos = strpos($alias, '/')) !== false) { } elseif (($pos = strpos($alias, '/')) !== false || ($pos = strpos($alias, '\\')) !== false) {
$rootAlias = substr($alias, 0, $pos); $rootAlias = substr($alias, 0, $pos);
if (isset(self::$aliases[$rootAlias])) { if (isset(self::$aliases[$rootAlias])) {
return self::$aliases[$alias] = self::$aliases[$rootAlias] . substr($alias, $pos); return self::$aliases[$alias] = self::$aliases[$rootAlias] . substr($alias, $pos);
} }
} }
}
if ($throwException) {
throw new InvalidParamException("Invalid path alias: $alias");
} else {
return false; return false;
} }
}
/** /**
* Registers a path alias. * Registers a path alias.
@ -238,10 +240,8 @@ class YiiBase
unset(self::$aliases[$alias]); unset(self::$aliases[$alias]);
} elseif ($path[0] !== '@') { } elseif ($path[0] !== '@') {
self::$aliases[$alias] = rtrim($path, '\\/'); self::$aliases[$alias] = rtrim($path, '\\/');
} elseif (($p = static::getAlias($path)) !== false) {
self::$aliases[$alias] = $p;
} else { } else {
throw new Exception('Invalid path: ' . $path); self::$aliases[$alias] = static::getAlias($path);
} }
} }
@ -275,14 +275,14 @@ class YiiBase
// namespaced class, e.g. yii\base\Component // namespaced class, e.g. yii\base\Component
// convert namespace to path alias, e.g. yii\base\Component to @yii/base/Component // convert namespace to path alias, e.g. yii\base\Component to @yii/base/Component
$alias = '@' . str_replace('\\', '/', ltrim($className, '\\')); $alias = '@' . str_replace('\\', '/', ltrim($className, '\\'));
if (($path = static::getAlias($alias)) !== false) { if (($path = static::getAlias($alias, false)) !== false) {
$classFile = $path . '.php'; $classFile = $path . '.php';
} }
} elseif (($pos = strpos($className, '_')) !== false) { } elseif (($pos = strpos($className, '_')) !== false) {
// PEAR-styled class, e.g. PHPUnit_Framework_TestCase // PEAR-styled class, e.g. PHPUnit_Framework_TestCase
// convert class name to path alias, e.g. PHPUnit_Framework_TestCase to @PHPUnit/Framework/TestCase // convert class name to path alias, e.g. PHPUnit_Framework_TestCase to @PHPUnit/Framework/TestCase
$alias = '@' . str_replace('_', '/', $className); $alias = '@' . str_replace('_', '/', $className);
if (($path = static::getAlias($alias)) !== false) { if (($path = static::getAlias($alias, false)) !== false) {
$classFile = $path . '.php'; $classFile = $path . '.php';
} }
} }
@ -298,7 +298,7 @@ class YiiBase
} }
} }
if (isset($classFile, $alias)) { if (isset($classFile, $alias) && is_file($classFile)) {
if (!YII_DEBUG || basename(realpath($classFile)) === basename($alias) . '.php') { if (!YII_DEBUG || basename(realpath($classFile)) === basename($alias) . '.php') {
include($classFile); include($classFile);
return true; return true;

4
framework/base/Action.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Action class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

4
framework/base/ActionEvent.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* ActionEvent class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

90
framework/base/ActionFilter.php

@ -0,0 +1,90 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* @author Qiang Xue <qiang.xue@gmail.com>
* @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]].
*/
public $only;
/**
* @var array list of action IDs that this filter should not apply to.
*/
public $except = array();
/**
* 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 array(
'beforeAction' => 'beforeFilter',
'afterAction' => 'afterFilter',
);
}
/**
* @param ActionEvent $event
* @return boolean
*/
public function beforeFilter($event)
{
if ($this->isActive($event->action)) {
$event->isValid = $this->beforeAction($event->action);
}
return $event->isValid;
}
/**
* @param ActionEvent $event
* @return boolean
*/
public function afterFilter($event)
{
if ($this->isActive($event->action)) {
$this->afterAction($event->action);
}
}
/**
* 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.
*/
public function afterAction($action)
{
}
/**
* 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));
}
}

132
framework/base/Application.php

@ -1,16 +1,14 @@
<?php <?php
/** /**
* Application class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\base; namespace yii\base;
use Yii; use Yii;
use yii\util\FileHelper; use yii\helpers\FileHelper;
/** /**
* Application is the base class for all application classes. * Application is the base class for all application classes.
@ -94,7 +92,12 @@ class Application extends Module
private $_runtimePath; private $_runtimePath;
private $_ended = false; private $_ended = false;
private $_language;
/**
* @var string Used to reserve memory for fatal error handler. This memory
* reserve can be removed if it's OK to write to PHP log only in this particular case.
*/
private $_memoryReserve;
/** /**
* Constructor. * Constructor.
@ -110,6 +113,7 @@ class Application extends Module
$this->setBasePath($basePath); $this->setBasePath($basePath);
if (YII_ENABLE_ERROR_HANDLER) { if (YII_ENABLE_ERROR_HANDLER) {
ini_set('display_errors', 0);
set_exception_handler(array($this, 'handleException')); set_exception_handler(array($this, 'handleException'));
set_error_handler(array($this, 'handleError'), error_reporting()); set_error_handler(array($this, 'handleError'), error_reporting());
} }
@ -142,12 +146,64 @@ class Application extends Module
$this->_ended = true; $this->_ended = true;
$this->afterRequest(); $this->afterRequest();
} }
$this->handleFatalError();
if ($exit) { if ($exit) {
exit($status); exit($status);
} }
} }
/** /**
* Handles fatal PHP errors
*/
public function handleFatalError()
{
if (YII_ENABLE_ERROR_HANDLER) {
$error = error_get_last();
if (ErrorException::isFatalErorr($error)) {
unset($this->_memoryReserve);
$exception = new ErrorException($error['message'], $error['type'], $error['type'], $error['file'], $error['line']);
if (function_exists('xdebug_get_function_stack')) {
$trace = array_slice(array_reverse(xdebug_get_function_stack()), 4, -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'] = '::';
}
// XDebug has a different key name
$frame['args'] = array();
if (isset($frame['params']) && !isset($frame['args'])) {
$frame['args'] = $frame['params'];
}
}
$ref = new \ReflectionProperty('Exception', 'trace');
$ref->setAccessible(true);
$ref->setValue($exception, $trace);
}
$this->logException($exception);
if (($handler = $this->getErrorHandler()) !== null) {
@$handler->handle($exception);
} else {
$this->renderException($exception);
}
exit(1);
}
}
}
/**
* Runs the application. * Runs the application.
* This is the main entrance of an application. * This is the main entrance of an application.
* @return integer the exit status (0 means normal, non-zero values mean abnormal) * @return integer the exit status (0 means normal, non-zero values mean abnormal)
@ -155,6 +211,10 @@ class Application extends Module
public function run() public function run()
{ {
$this->beforeRequest(); $this->beforeRequest();
// Allocating twice more than required to display memory exhausted error
// in case of trying to allocate last 1 byte while all memory is taken.
$this->_memoryReserve = str_repeat('x', 1024 * 256);
register_shutdown_function(array($this, 'end'), 0, false);
$status = $this->processRequest(); $status = $this->processRequest();
$this->afterRequest(); $this->afterRequest();
return $status; return $status;
@ -235,14 +295,6 @@ class Application extends Module
date_default_timezone_set($value); date_default_timezone_set($value);
} }
// /**
// * Returns the security manager component.
// * @return SecurityManager the security manager application component.
// */
// public function getSecurityManager()
// {
// return $this->getComponent('securityManager');
// }
// //
// /** // /**
// * Returns the locale instance. // * Returns the locale instance.
@ -293,15 +345,6 @@ class Application extends Module
} }
/** /**
* Returns the application theme.
* @return Theme the theme that this application is currently using.
*/
public function getTheme()
{
return $this->getComponent('theme');
}
/**
* Returns the cache component. * Returns the cache component.
* @return \yii\caching\Cache the cache application component. Null if the component is not enabled. * @return \yii\caching\Cache the cache application component. Null if the component is not enabled.
*/ */
@ -320,12 +363,21 @@ class Application extends Module
} }
/** /**
* Returns the view renderer. * Returns the view object.
* @return ViewRenderer the view renderer used by this application. * @return View the view object that is used to render various view files.
*/ */
public function getViewRenderer() public function getView()
{ {
return $this->getComponent('viewRenderer'); 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');
} }
/** /**
@ -343,8 +395,6 @@ class Application extends Module
public function registerDefaultAliases() public function registerDefaultAliases()
{ {
Yii::$aliases['@app'] = $this->getBasePath(); Yii::$aliases['@app'] = $this->getBasePath();
Yii::$aliases['@entry'] = dirname($_SERVER['SCRIPT_FILENAME']);
Yii::$aliases['@www'] = '';
} }
/** /**
@ -360,8 +410,11 @@ class Application extends Module
'i18n' => array( 'i18n' => array(
'class' => 'yii\i18n\I18N', 'class' => 'yii\i18n\I18N',
), ),
'securityManager' => array( 'urlManager' => array(
'class' => 'yii\base\SecurityManager', 'class' => 'yii\web\UrlManager',
),
'view' => array(
'class' => 'yii\base\View',
), ),
)); ));
} }
@ -375,12 +428,24 @@ class Application extends Module
* @param string $message the error message * @param string $message the error message
* @param string $file the filename that the error was raised in * @param string $file the filename that the error was raised in
* @param integer $line the line number the error was raised at * @param integer $line the line number the error was raised at
* @throws \ErrorException the error exception *
* @throws ErrorException
*/ */
public function handleError($code, $message, $file, $line) public function handleError($code, $message, $file, $line)
{ {
if (error_reporting() !== 0) { if (error_reporting() !== 0) {
throw new \ErrorException($message, 0, $code, $file, $line); $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);
}
}
throw $exception;
} }
} }
@ -414,6 +479,9 @@ class Application extends Module
$msg = (string)$e; $msg = (string)$e;
$msg .= "\nPrevious exception:\n"; $msg .= "\nPrevious exception:\n";
$msg .= (string)$exception; $msg .= (string)$exception;
if (YII_DEBUG) {
echo $msg;
}
$msg .= "\n\$_SERVER = " . var_export($_SERVER, true); $msg .= "\n\$_SERVER = " . var_export($_SERVER, true);
error_log($msg); error_log($msg);
exit(1); exit(1);

4
framework/base/Behavior.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Behavior class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

10
framework/base/Component.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Component class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -60,7 +58,7 @@ class Component extends \yii\base\Object
} }
} }
} }
throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '.' . $name); throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name);
} }
/** /**
@ -107,9 +105,9 @@ class Component extends \yii\base\Object
} }
} }
if (method_exists($this, 'get' . $name)) { if (method_exists($this, 'get' . $name)) {
throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '.' . $name); throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name);
} else { } else {
throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '.' . $name); throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name);
} }
} }

110
framework/base/Controller.php

@ -1,16 +1,15 @@
<?php <?php
/** /**
* Controller class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\base; namespace yii\base;
use Yii; use Yii;
use yii\util\StringHelper; use yii\helpers\FileHelper;
use yii\helpers\StringHelper;
/** /**
* Controller is the base class for classes containing controller logic. * Controller is the base class for classes containing controller logic.
@ -297,34 +296,46 @@ class Controller extends Component
/** /**
* Renders a view and applies layout if available. * Renders a view and applies layout if available.
* * @param string $view the view name. Please refer to [[findViewFile()]] on how to specify a view name.
* @param $view * @param array $params the parameters (name-value pairs) that should be made available in the view.
* @param array $params * These parameters will not be available in the layout.
* @return string * @return string the rendering result.
* @throws InvalidParamException if the view file or the layout file does not exist.
*/ */
public function render($view, $params = array()) public function render($view, $params = array())
{ {
return $this->createView()->render($view, $params); $output = Yii::$app->getView()->render($view, $params, $this);
$layoutFile = $this->findLayoutFile();
if ($layoutFile !== false) {
return Yii::$app->getView()->renderFile($layoutFile, array('content' => $output), $this);
} else {
return $output;
} }
public function renderContent($content)
{
return $this->createView()->renderContent($content);
} }
/**
* 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 [[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 renderPartial($view, $params = array()) public function renderPartial($view, $params = array())
{ {
return $this->createView()->renderPartial($view, $params); return Yii::$app->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 = array()) public function renderFile($file, $params = array())
{ {
return $this->createView()->renderFile($file, $params); return Yii::$app->getView()->renderFile($file, $params, $this);
}
public function createView()
{
return new View($this);
} }
/** /**
@ -337,4 +348,63 @@ class Controller extends Component
{ {
return $this->module->getViewPath() . DIRECTORY_SEPARATOR . $this->id; return $this->module->getViewPath() . DIRECTORY_SEPARATOR . $this->id;
} }
/**
* Finds the applicable layout file.
*
* This method locates an applicable layout file via two steps.
*
* 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.
*
* 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::viewPath|view path]] of the context module.
*
* If the layout name does not contain a file extension, it will use the default one `.php`.
*
* @return string|boolean the layout file path, or false if layout is not needed.
* @throws InvalidParamException if an invalid path alias is used to specify the layout
*/
protected function findLayoutFile()
{
$module = $this->module;
if (is_string($this->layout)) {
$view = $this->layout;
} elseif ($this->layout === null) {
while ($module !== null && $module->layout === null) {
$module = $module->module;
}
if ($module !== null && is_string($module->layout)) {
$view = $module->layout;
}
}
if (!isset($view)) {
return false;
}
if (strncmp($view, '@', 1) === 0) {
$file = Yii::getAlias($view);
} elseif (strncmp($view, '/', 1) === 0) {
$file = Yii::$app->getLayoutPath() . DIRECTORY_SEPARATOR . $view;
} else {
$file = $module->getLayoutPath() . DIRECTORY_SEPARATOR . $view;
}
if (FileHelper::getExtension($file) === '') {
$file .= '.php';
}
return $file;
}
} }

14
framework/base/Dictionary.php

@ -1,15 +1,13 @@
<?php <?php
/** /**
* Dictionary class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\base; namespace yii\base;
use yii\util\ArrayHelper; use yii\helpers\ArrayHelper;
/** /**
* Dictionary implements a collection that stores key-value pairs. * Dictionary implements a collection that stores key-value pairs.
@ -184,7 +182,7 @@ class Dictionary extends Object implements \IteratorAggregate, \ArrayAccess, \Co
* Copies iterable data into the dictionary. * Copies iterable data into the dictionary.
* Note, existing data in the dictionary will be cleared first. * Note, existing data in the dictionary will be cleared first.
* @param mixed $data the data to be copied from, must be an array or an object implementing `Traversable` * @param mixed $data the data to be copied from, must be an array or an object implementing `Traversable`
* @throws InvalidCallException if data is neither an array nor an iterator. * @throws InvalidParamException if data is neither an array nor an iterator.
*/ */
public function copyFrom($data) public function copyFrom($data)
{ {
@ -199,7 +197,7 @@ class Dictionary extends Object implements \IteratorAggregate, \ArrayAccess, \Co
$this->add($key, $value); $this->add($key, $value);
} }
} else { } else {
throw new InvalidCallException('Data must be either an array or an object implementing Traversable.'); throw new InvalidParamException('Data must be either an array or an object implementing Traversable.');
} }
} }
@ -216,7 +214,7 @@ class Dictionary extends Object implements \IteratorAggregate, \ArrayAccess, \Co
* *
* @param array|\Traversable $data the data to be merged with. It must be an array or object implementing Traversable * @param array|\Traversable $data the data to be merged with. It must be an array or object implementing Traversable
* @param boolean $recursive whether the merging should be recursive. * @param boolean $recursive whether the merging should be recursive.
* @throws InvalidCallException if data is neither an array nor an object implementing `Traversable`. * @throws InvalidParamException if data is neither an array nor an object implementing `Traversable`.
*/ */
public function mergeWith($data, $recursive = true) public function mergeWith($data, $recursive = true)
{ {
@ -240,7 +238,7 @@ class Dictionary extends Object implements \IteratorAggregate, \ArrayAccess, \Co
} }
} }
} else { } else {
throw new InvalidCallException('The data to be merged with must be an array or an object implementing Traversable.'); throw new InvalidParamException('The data to be merged with must be an array or an object implementing Traversable.');
} }
} }

4
framework/base/DictionaryIterator.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* DictionaryIterator class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

81
framework/base/ErrorException.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\base;
/**
* ErrorException represents a PHP error.
*
* @author Alexander Makarov <sam@rmcreative.ru>
* @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;
}
/**
* 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;
}
/**
* 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 isFatalErorr($error)
{
return isset($error['type']) && in_array($error['type'], array(E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING));
}
/**
* @return string the user-friendly name of this exception
*/
public function getName()
{
$names = array(
E_ERROR => \Yii::t('yii|Fatal Error'),
E_PARSE => \Yii::t('yii|Parse Error'),
E_CORE_ERROR => \Yii::t('yii|Core Error'),
E_COMPILE_ERROR => \Yii::t('yii|Compile Error'),
E_USER_ERROR => \Yii::t('yii|User Error'),
E_WARNING => \Yii::t('yii|Warning'),
E_CORE_WARNING => \Yii::t('yii|Core Warning'),
E_COMPILE_WARNING => \Yii::t('yii|Compile Warning'),
E_USER_WARNING => \Yii::t('yii|User Warning'),
E_STRICT => \Yii::t('yii|Strict'),
E_NOTICE => \Yii::t('yii|Notice'),
E_RECOVERABLE_ERROR => \Yii::t('yii|Recoverable Error'),
E_DEPRECATED => \Yii::t('yii|Deprecated'),
);
return isset($names[$this->getCode()]) ? $names[$this->getCode()] : \Yii::t('yii|Error');
}
}

19
framework/base/ErrorHandler.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* ErrorHandler class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -18,7 +16,7 @@ namespace yii\base;
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
use yii\util\VarDumper; use yii\helpers\VarDumper;
class ErrorHandler extends Component class ErrorHandler extends Component
{ {
@ -80,7 +78,7 @@ class ErrorHandler extends Component
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') { if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
\Yii::$app->renderException($exception); \Yii::$app->renderException($exception);
} else { } else {
$view = new View($this); $view = new View;
if (!YII_DEBUG || $exception instanceof UserException) { if (!YII_DEBUG || $exception instanceof UserException) {
$viewName = $this->errorView; $viewName = $this->errorView;
} else { } else {
@ -88,7 +86,7 @@ class ErrorHandler extends Component
} }
echo $view->render($viewName, array( echo $view->render($viewName, array(
'exception' => $exception, 'exception' => $exception,
)); ), $this);
} }
} else { } else {
\Yii::$app->renderException($exception); \Yii::$app->renderException($exception);
@ -255,15 +253,10 @@ class ErrorHandler extends Component
*/ */
public function renderAsHtml($exception) public function renderAsHtml($exception)
{ {
$view = new View($this); $view = new View;
if (!YII_DEBUG || $exception instanceof UserException) {
$viewName = $this->errorView;
} else {
$viewName = $this->exceptionView;
}
$name = !YII_DEBUG || $exception instanceof HttpException ? $this->errorView : $this->exceptionView; $name = !YII_DEBUG || $exception instanceof HttpException ? $this->errorView : $this->exceptionView;
echo $view->render($name, array( echo $view->render($name, array(
'exception' => $exception, 'exception' => $exception,
)); ), $this);
} }
} }

4
framework/base/Event.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Event class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

5
framework/base/Exception.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Exception class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -25,4 +23,3 @@ class Exception extends \Exception
return \Yii::t('yii|Exception'); return \Yii::t('yii|Exception');
} }
} }

4
framework/base/HttpException.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* HttpException class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

4
framework/base/InlineAction.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* InlineAction class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

4
framework/base/InvalidCallException.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* InvalidCallException class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

4
framework/base/InvalidConfigException.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* InvalidConfigException class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

26
framework/base/InvalidParamException.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;
/**
* InvalidParamException represents an exception caused by invalid parameters passed to a method.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class InvalidParamException extends Exception
{
/**
* @return string the user-friendly name of this exception
*/
public function getName()
{
return \Yii::t('yii|Invalid Parameter');
}
}

4
framework/base/InvalidRequestException.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* InvalidRequestException class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

4
framework/base/InvalidRouteException.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* InvalidRouteException class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

48
framework/base/Model.php

@ -1,15 +1,13 @@
<?php <?php
/** /**
* Model class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\base; namespace yii\base;
use yii\util\StringHelper; use yii\helpers\StringHelper;
use yii\validators\Validator; use yii\validators\Validator;
use yii\validators\RequiredValidator; use yii\validators\RequiredValidator;
@ -422,12 +420,31 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
} }
/** /**
* 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.
*/
public function getFirstErrors()
{
if (empty($this->_errors)) {
return array();
} else {
$errors = array();
foreach ($this->_errors as $errors) {
if (isset($errors[0])) {
$errors[] = $errors[0];
}
}
}
return $errors;
}
/**
* Returns the first error of the specified attribute. * Returns the first error of the specified attribute.
* @param string $attribute attribute name. * @param string $attribute attribute name.
* @return string the error message. Null is returned if no error. * @return string the error message. Null is returned if no error.
* @see getErrors * @see getErrors
*/ */
public function getError($attribute) public function getFirstError($attribute)
{ {
return isset($this->_errors[$attribute]) ? reset($this->_errors[$attribute]) : null; return isset($this->_errors[$attribute]) ? reset($this->_errors[$attribute]) : null;
} }
@ -443,25 +460,6 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
} }
/** /**
* Adds a list of errors.
* @param array $errors a list of errors. The array keys must be attribute names.
* The array values should be error messages. If an attribute has multiple errors,
* these errors must be given in terms of an array.
*/
public function addErrors($errors)
{
foreach ($errors as $attribute => $error) {
if (is_array($error)) {
foreach ($error as $e) {
$this->_errors[$attribute][] = $e;
}
} else {
$this->_errors[$attribute][] = $error;
}
}
}
/**
* Removes errors for all attributes or a single attribute. * Removes errors for all attributes or a single attribute.
* @param string $attribute attribute name. Use null to remove errors for all attribute. * @param string $attribute attribute name. Use null to remove errors for all attribute.
*/ */
@ -543,7 +541,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess
public function onUnsafeAttribute($name, $value) public function onUnsafeAttribute($name, $value)
{ {
if (YII_DEBUG) { if (YII_DEBUG) {
\Yii::warning("Failed to set unsafe attribute '$name' in '" . get_class($this) . "'."); \Yii::info("Failed to set unsafe attribute '$name' in '" . get_class($this) . "'.", __CLASS__);
} }
} }

4
framework/base/ModelEvent.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* ModelEvent class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

8
framework/base/Module.php

@ -1,17 +1,15 @@
<?php <?php
/** /**
* Module class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\base; namespace yii\base;
use Yii; use Yii;
use yii\util\StringHelper; use yii\helpers\StringHelper;
use yii\util\FileHelper; use yii\helpers\FileHelper;
/** /**
* Module is the base class for module and application classes. * Module is the base class for module and application classes.

4
framework/base/NotSupportedException.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* NotSupportedException class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

12
framework/base/Object.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Object class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -67,7 +65,7 @@ class Object
if (method_exists($this, $getter)) { if (method_exists($this, $getter)) {
return $this->$getter(); return $this->$getter();
} else { } else {
throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '.' . $name); throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name);
} }
} }
@ -88,9 +86,9 @@ class Object
if (method_exists($this, $setter)) { if (method_exists($this, $setter)) {
$this->$setter($value); $this->$setter($value);
} elseif (method_exists($this, 'get' . $name)) { } elseif (method_exists($this, 'get' . $name)) {
throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '.' . $name); throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name);
} else { } else {
throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '.' . $name); throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name);
} }
} }
@ -131,7 +129,7 @@ class Object
if (method_exists($this, $setter)) { if (method_exists($this, $setter)) {
$this->$setter(null); $this->$setter(null);
} elseif (method_exists($this, 'get' . $name)) { } elseif (method_exists($this, 'get' . $name)) {
throw new InvalidCallException('Unsetting read-only property: ' . get_class($this) . '.' . $name); throw new InvalidCallException('Unsetting read-only property: ' . get_class($this) . '::' . $name);
} }
} }

35
framework/base/Request.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Request class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -13,12 +11,18 @@ namespace yii\base;
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class Request extends Component abstract class Request extends Component
{ {
private $_scriptFile; private $_scriptFile;
private $_isConsoleRequest; 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 * 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 * @return boolean the value indicating whether the current request is made via console
*/ */
@ -39,24 +43,35 @@ class Request extends Component
/** /**
* Returns entry script file path. * Returns entry script file path.
* @return string entry script file path (processed w/ realpath()) * @return string entry script file path (processed w/ realpath())
* @throws InvalidConfigException if the entry script file path cannot be determined automatically.
*/ */
public function getScriptFile() public function getScriptFile()
{ {
if ($this->_scriptFile === null) { if ($this->_scriptFile === null) {
$this->_scriptFile = realpath($_SERVER['SCRIPT_FILENAME']); 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; return $this->_scriptFile;
} }
/** /**
* Sets the entry script file path. * Sets the entry script file path.
* This can be an absolute or relative file path, or a path alias. * The entry script file path can normally be determined based on the `SCRIPT_FILENAME` SERVER variable.
* Note that you normally do not have to set the script file path * However, for some server configurations, this may not be correct or feasible.
* as [[getScriptFile()]] can determine it based on `$_SERVER['SCRIPT_FILENAME']`. * This setter is provided so that the entry script file path can be manually specified.
* @param string $value the entry script file * @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) public function setScriptFile($value)
{ {
$this->_scriptFile = realpath(\Yii::getAlias($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.');
}
} }
} }

4
framework/base/Response.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Response class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

290
framework/base/SecurityManager.php

@ -1,290 +0,0 @@
<?php
/**
* SecurityManager class file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* SecurityManager provides private keys, hashing and encryption functions.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class SecurityManager extends Component
{
const STATE_VALIDATION_KEY = 'Yii.SecurityManager.validationkey';
const STATE_ENCRYPTION_KEY = 'Yii.SecurityManager.encryptionkey';
/**
* @var string the name of the hashing algorithm to be used by {@link computeHMAC}.
* See {@link http://php.net/manual/en/function.hash-algos.php hash-algos} for the list of possible
* hash algorithms. Note that if you are using PHP 5.1.1 or below, you can only use 'sha1' or 'md5'.
*
* Defaults to 'sha1', meaning using SHA1 hash algorithm.
*/
public $hashAlgorithm = 'sha1';
/**
* @var mixed the name of the crypt algorithm to be used by {@link encrypt} and {@link decrypt}.
* This will be passed as the first parameter to {@link http://php.net/manual/en/function.mcrypt-module-open.php mcrypt_module_open}.
*
* This property can also be configured as an array. In this case, the array elements will be passed in order
* as parameters to mcrypt_module_open. For example, <code>array('rijndael-256', '', 'ofb', '')</code>.
*
* Defaults to 'des', meaning using DES crypt algorithm.
*/
public $cryptAlgorithm = 'des';
private $_validationKey;
private $_encryptionKey;
/**
* @return string a randomly generated private key
*/
protected function generateRandomKey()
{
return sprintf('%08x%08x%08x%08x', mt_rand(), mt_rand(), mt_rand(), mt_rand());
}
/**
* @return string the private key used to generate HMAC.
* If the key is not explicitly set, a random one is generated and returned.
*/
public function getValidationKey()
{
if ($this->_validationKey !== null) {
return $this->_validationKey;
} else {
if (($key = \Yii::$app->getGlobalState(self::STATE_VALIDATION_KEY)) !== null) {
$this->setValidationKey($key);
} else {
$key = $this->generateRandomKey();
$this->setValidationKey($key);
\Yii::$app->setGlobalState(self::STATE_VALIDATION_KEY, $key);
}
return $this->_validationKey;
}
}
/**
* @param string $value the key used to generate HMAC
* @throws CException if the key is empty
*/
public function setValidationKey($value)
{
if (!empty($value)) {
$this->_validationKey = $value;
} else {
throw new CException(Yii::t('yii|SecurityManager.validationKey cannot be empty.'));
}
}
/**
* @return string the private key used to encrypt/decrypt data.
* If the key is not explicitly set, a random one is generated and returned.
*/
public function getEncryptionKey()
{
if ($this->_encryptionKey !== null) {
return $this->_encryptionKey;
} else {
if (($key = \Yii::$app->getGlobalState(self::STATE_ENCRYPTION_KEY)) !== null) {
$this->setEncryptionKey($key);
} else {
$key = $this->generateRandomKey();
$this->setEncryptionKey($key);
\Yii::$app->setGlobalState(self::STATE_ENCRYPTION_KEY, $key);
}
return $this->_encryptionKey;
}
}
/**
* @param string $value the key used to encrypt/decrypt data.
* @throws CException if the key is empty
*/
public function setEncryptionKey($value)
{
if (!empty($value)) {
$this->_encryptionKey = $value;
} else {
throw new CException(Yii::t('yii|SecurityManager.encryptionKey cannot be empty.'));
}
}
/**
* This method has been deprecated since version 1.1.3.
* Please use {@link hashAlgorithm} instead.
* @return string
*/
public function getValidation()
{
return $this->hashAlgorithm;
}
/**
* This method has been deprecated since version 1.1.3.
* Please use {@link hashAlgorithm} instead.
* @param string $value -
*/
public function setValidation($value)
{
$this->hashAlgorithm = $value;
}
/**
* Encrypts data.
* @param string $data data to be encrypted.
* @param string $key the decryption key. This defaults to null, meaning using {@link getEncryptionKey EncryptionKey}.
* @return string the encrypted data
* @throws CException if PHP Mcrypt extension is not loaded
*/
public function encrypt($data, $key = null)
{
$module = $this->openCryptModule();
$key = $this->substr($key === null ? md5($this->getEncryptionKey()) : $key, 0, mcrypt_enc_get_key_size($module));
srand();
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND);
mcrypt_generic_init($module, $key, $iv);
$encrypted = $iv . mcrypt_generic($module, $data);
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
return $encrypted;
}
/**
* Decrypts data
* @param string $data data to be decrypted.
* @param string $key the decryption key. This defaults to null, meaning using {@link getEncryptionKey EncryptionKey}.
* @return string the decrypted data
* @throws CException if PHP Mcrypt extension is not loaded
*/
public function decrypt($data, $key = null)
{
$module = $this->openCryptModule();
$key = $this->substr($key === null ? md5($this->getEncryptionKey()) : $key, 0, mcrypt_enc_get_key_size($module));
$ivSize = mcrypt_enc_get_iv_size($module);
$iv = $this->substr($data, 0, $ivSize);
mcrypt_generic_init($module, $key, $iv);
$decrypted = mdecrypt_generic($module, $this->substr($data, $ivSize, $this->strlen($data)));
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
return rtrim($decrypted, "\0");
}
/**
* Opens the mcrypt module with the configuration specified in {@link cryptAlgorithm}.
* @return resource the mycrypt module handle.
* @since 1.1.3
*/
protected function openCryptModule()
{
if (extension_loaded('mcrypt')) {
if (is_array($this->cryptAlgorithm)) {
$module = @call_user_func_array('mcrypt_module_open', $this->cryptAlgorithm);
} else {
$module = @mcrypt_module_open($this->cryptAlgorithm, '', MCRYPT_MODE_CBC, '');
}
if ($module === false) {
throw new CException(Yii::t('yii|Failed to initialize the mcrypt module.'));
}
return $module;
} else {
throw new CException(Yii::t('yii|SecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.'));
}
}
/**
* Prefixes data with an HMAC.
* @param string $data data to be hashed.
* @param string $key the private key to be used for generating HMAC. Defaults to null, meaning using {@link validationKey}.
* @return string data prefixed with HMAC
*/
public function hashData($data, $key = null)
{
return $this->computeHMAC($data, $key) . $data;
}
/**
* Validates if data is tampered.
* @param string $data data to be validated. The data must be previously
* generated using {@link hashData()}.
* @param string $key the private key to be used for generating HMAC. Defaults to null, meaning using {@link validationKey}.
* @return string the real data with HMAC stripped off. False if the data
* is tampered.
*/
public function validateData($data, $key = null)
{
$len = $this->strlen($this->computeHMAC('test'));
if ($this->strlen($data) >= $len) {
$hmac = $this->substr($data, 0, $len);
$data2 = $this->substr($data, $len, $this->strlen($data));
return $hmac === $this->computeHMAC($data2, $key) ? $data2 : false;
} else {
return false;
}
}
/**
* Computes the HMAC for the data with {@link getValidationKey ValidationKey}.
* @param string $data data to be generated HMAC
* @param string $key the private key to be used for generating HMAC. Defaults to null, meaning using {@link validationKey}.
* @return string the HMAC for the data
*/
protected function computeHMAC($data, $key = null)
{
if ($key === null) {
$key = $this->getValidationKey();
}
if (function_exists('hash_hmac')) {
return hash_hmac($this->hashAlgorithm, $data, $key);
}
if (!strcasecmp($this->hashAlgorithm, 'sha1')) {
$pack = 'H40';
$func = 'sha1';
} else {
$pack = 'H32';
$func = 'md5';
}
if ($this->strlen($key) > 64) {
$key = pack($pack, $func($key));
}
if ($this->strlen($key) < 64) {
$key = str_pad($key, 64, chr(0));
}
$key = $this->substr($key, 0, 64);
return $func((str_repeat(chr(0x5C), 64) ^ $key) . pack($pack, $func((str_repeat(chr(0x36), 64) ^ $key) . $data)));
}
/**
* Returns the length of the given string.
* If available uses the multibyte string function mb_strlen.
* @param string $string the string being measured for length
* @return int the length of the string
*/
private function strlen($string)
{
return function_exists('mb_strlen') ? mb_strlen($string, '8bit') : strlen($string);
}
/**
* Returns the portion of string specified by the start and length parameters.
* If available uses the multibyte string function mb_substr
* @param string $string the input string. Must be one character or longer.
* @param int $start the starting position
* @param int $length the desired portion length
* @return string the extracted part of string, or FALSE on failure or an empty string.
*/
private function substr($string, $start, $length)
{
return function_exists('mb_substr') ? mb_substr($string, $start, $length, '8bit') : substr($string, $start, $length);
}
}

15
framework/base/Theme.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Theme class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -11,7 +9,7 @@ namespace yii\base;
use Yii; use Yii;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
use yii\util\FileHelper; use yii\helpers\FileHelper;
/** /**
* Theme represents an application theme. * Theme represents an application theme.
@ -42,7 +40,8 @@ class Theme extends Component
/** /**
* @var array the mapping between view directories and their corresponding themed versions. * @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]]. * If not set, it will be initialized as a mapping from [[Application::basePath]] to [[basePath]].
* This property is used by [[apply()]] when a view is trying to apply the theme. * 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; public $pathMap;
@ -65,7 +64,9 @@ class Theme extends Component
} }
$paths = array(); $paths = array();
foreach ($this->pathMap as $from => $to) { foreach ($this->pathMap as $from => $to) {
$paths[FileHelper::normalizePath($from) . DIRECTORY_SEPARATOR] = FileHelper::normalizePath($to) . DIRECTORY_SEPARATOR; $from = FileHelper::normalizePath(Yii::getAlias($from));
$to = FileHelper::normalizePath(Yii::getAlias($to));
$paths[$from . DIRECTORY_SEPARATOR] = $to . DIRECTORY_SEPARATOR;
} }
$this->pathMap = $paths; $this->pathMap = $paths;
} }
@ -95,7 +96,7 @@ class Theme extends Component
* @param string $path the file to be themed * @param string $path the file to be themed
* @return string the themed file, or the original file if the themed version is not available. * @return string the themed file, or the original file if the themed version is not available.
*/ */
public function apply($path) public function applyTo($path)
{ {
$path = FileHelper::normalizePath($path); $path = FileHelper::normalizePath($path);
foreach ($this->pathMap as $from => $to) { foreach ($this->pathMap as $from => $to) {

4
framework/base/UnknownMethodException.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* UnknownMethodException class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

4
framework/base/UnknownPropertyException.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* UnknownPropertyException class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

4
framework/base/UserException.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* UserException class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

24
framework/base/Vector.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Vector class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -101,7 +99,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
* Returns the item at the specified index. * Returns the item at the specified index.
* @param integer $index the index of the item * @param integer $index the index of the item
* @return mixed the item at the index * @return mixed the item at the index
* @throws InvalidCallException if the index is out of range * @throws InvalidParamException if the index is out of range
*/ */
public function itemAt($index) public function itemAt($index)
{ {
@ -110,7 +108,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
} elseif ($index >= 0 && $index < $this->_c) { // in case the value is null } elseif ($index >= 0 && $index < $this->_c) { // in case the value is null
return $this->_d[$index]; return $this->_d[$index];
} else { } else {
throw new InvalidCallException('Index out of range: ' . $index); throw new InvalidParamException('Index out of range: ' . $index);
} }
} }
@ -132,7 +130,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
* one step towards the end. * one step towards the end.
* @param integer $index the specified position. * @param integer $index the specified position.
* @param mixed $item new item to be inserted into the vector * @param mixed $item new item to be inserted into the vector
* @throws InvalidCallException if the index specified is out of range, or the vector is read-only. * @throws InvalidParamException if the index specified is out of range, or the vector is read-only.
*/ */
public function insertAt($index, $item) public function insertAt($index, $item)
{ {
@ -142,7 +140,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
array_splice($this->_d, $index, 0, array($item)); array_splice($this->_d, $index, 0, array($item));
$this->_c++; $this->_c++;
} else { } else {
throw new InvalidCallException('Index out of range: ' . $index); throw new InvalidParamException('Index out of range: ' . $index);
} }
} }
@ -169,7 +167,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
* Removes an item at the specified position. * Removes an item at the specified position.
* @param integer $index the index of the item to be removed. * @param integer $index the index of the item to be removed.
* @return mixed the removed item. * @return mixed the removed item.
* @throws InvalidCallException if the index is out of range, or the vector is read only. * @throws InvalidParamException if the index is out of range, or the vector is read only.
*/ */
public function removeAt($index) public function removeAt($index)
{ {
@ -183,7 +181,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
return $item; return $item;
} }
} else { } else {
throw new InvalidCallException('Index out of range: ' . $index); throw new InvalidParamException('Index out of range: ' . $index);
} }
} }
@ -242,7 +240,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
* Copies iterable data into the vector. * Copies iterable data into the vector.
* Note, existing data in the vector will be cleared first. * Note, existing data in the vector will be cleared first.
* @param mixed $data the data to be copied from, must be an array or an object implementing `Traversable` * @param mixed $data the data to be copied from, must be an array or an object implementing `Traversable`
* @throws InvalidCallException if data is neither an array nor an object implementing `Traversable`. * @throws InvalidParamException if data is neither an array nor an object implementing `Traversable`.
*/ */
public function copyFrom($data) public function copyFrom($data)
{ {
@ -257,7 +255,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
$this->add($item); $this->add($item);
} }
} else { } else {
throw new InvalidCallException('Data must be either an array or an object implementing Traversable.'); throw new InvalidParamException('Data must be either an array or an object implementing Traversable.');
} }
} }
@ -265,7 +263,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
* Merges iterable data into the vector. * Merges iterable data into the vector.
* New items will be appended to the end of the existing items. * New items will be appended to the end of the existing items.
* @param array|\Traversable $data the data to be merged with. It must be an array or object implementing Traversable * @param array|\Traversable $data the data to be merged with. It must be an array or object implementing Traversable
* @throws InvalidCallException if data is neither an array nor an object implementing `Traversable`. * @throws InvalidParamException if data is neither an array nor an object implementing `Traversable`.
*/ */
public function mergeWith($data) public function mergeWith($data)
{ {
@ -277,7 +275,7 @@ class Vector extends Object implements \IteratorAggregate, \ArrayAccess, \Counta
$this->add($item); $this->add($item);
} }
} else { } else {
throw new InvalidCallException('The data to be merged with must be an array or an object implementing Traversable.'); throw new InvalidParamException('The data to be merged with must be an array or an object implementing Traversable.');
} }
} }

4
framework/base/VectorIterator.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* VectorIterator class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

565
framework/base/View.php

@ -1,17 +1,15 @@
<?php <?php
/** /**
* View class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\base; namespace yii\base;
use Yii; use Yii;
use yii\util\FileHelper;
use yii\base\Application; use yii\base\Application;
use yii\helpers\FileHelper;
/** /**
* View represents a view object in the MVC pattern. * View represents a view object in the MVC pattern.
@ -26,134 +24,124 @@ class View extends Component
/** /**
* @var object the object that owns this view. This can be a controller, a widget, or any other object. * @var object the object that owns this view. This can be a controller, a widget, or any other object.
*/ */
public $owner; public $context;
/** /**
* @var string the layout to be applied when [[render()]] or [[renderContent()]] is called. * @var mixed custom parameters that are shared among view templates.
* If not set, it will use the [[Module::layout]] of the currently active module.
*/ */
public $layout; public $params;
/** /**
* @var string the language that the view should be rendered in. If not set, it will use * @var ViewRenderer|array the view renderer object or the configuration array for
* the value of [[Application::language]]. * creating the view renderer. If not set, view files will be treated as normal PHP files.
*/ */
public $language; public $renderer;
/** /**
* @var string the language that the original view is in. If not set, it will use * @var Theme|array the theme object or the configuration array for creating the theme.
* the value of [[Application::sourceLanguage]]. * If not set, it means theming is not enabled.
*/ */
public $sourceLanguage; public $theme;
/** /**
* @var boolean whether to localize the view when possible. Defaults to true. * @var array a list of named output clips. You can call [[beginClip()]] and [[endClip()]]
* Note that when this is true, if a localized view cannot be found, the original view will be rendered. * to capture small fragments of a view. They can be later accessed at somewhere else
* No error will be reported. * through this property.
*/ */
public $enableI18N = true; public $clips;
/** /**
* @var boolean whether to theme the view when possible. Defaults to true. * @var Widget[] the widgets that are currently being rendered (not ended). This property
* Note that theming will be disabled if [[Application::theme]] is not set. * is maintained by [[beginWidget()]] and [[endWidget()]] methods. Do not modify it directly.
*/ */
public $enableTheme = true; public $widgetStack = array();
/** /**
* @var mixed custom parameters that are available in the view template * @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.
*/ */
public $params; public $cacheStack = array();
/** /**
* @var Widget[] the widgets that are currently not ended * @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.
*/ */
private $_widgetStack = array(); public $dynamicPlaceholders = array();
/**
* Constructor.
* @param object $owner the owner of this view. This usually is a controller or a widget.
* @param array $config name-value pairs that will be used to initialize the object properties
*/
public function __construct($owner, $config = array())
{
$this->owner = $owner;
parent::__construct($config);
}
/** /**
* Renders a view within a layout. * Initializes the view component.
* This method is similar to [[renderPartial()]] except that if a layout is available,
* this method will embed the view result into the layout and then return it.
* @param string $view the view to be rendered. Please refer to [[findViewFile()]] on possible formats of the view name.
* @param array $params the parameters that should be made available in the view. The PHP function `extract()`
* will be called on this variable to extract the variables from this parameter.
* @return string the rendering result
* @throws InvalidConfigException if the view file or layout file cannot be found
* @see findViewFile()
* @see findLayoutFile()
*/ */
public function render($view, $params = array()) public function init()
{ {
$content = $this->renderPartial($view, $params); parent::init();
return $this->renderContent($content); if (is_array($this->renderer)) {
$this->renderer = Yii::createObject($this->renderer);
} }
if (is_array($this->theme)) {
/** $this->theme = Yii::createObject($this->theme);
* Renders a text content within a layout.
* The layout being used is resolved by [[findLayout()]].
* If no layout is available, the content will be returned back.
* @param string $content the content to be rendered
* @return string the rendering result
* @throws InvalidConfigException if the layout file cannot be found
* @see findLayoutFile()
*/
public function renderContent($content)
{
$layoutFile = $this->findLayoutFile();
if ($layoutFile !== false) {
return $this->renderFile($layoutFile, array('content' => $content));
} else {
return $content;
} }
} }
/** /**
* Renders a view. * Renders a view.
* *
* The method first finds the actual view file corresponding to the specified view. * This method will call [[findViewFile()]] to convert the view name into the corresponding view
* It then calls [[renderFile()]] to render the view file. The rendering result is returned * file path, and it will then call [[renderFile()]] to render the view.
* as a string. If the view file does not exist, an exception will be thrown.
* *
* @param string $view the view to be rendered. Please refer to [[findViewFile()]] on possible formats of the view name. * @param string $view the view name. Please refer to [[findViewFile()]] on how to specify this parameter.
* @param array $params the parameters that should be made available in the view. The PHP function `extract()` * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.
* will be called on this variable to extract the variables from this parameter. * @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 * @return string the rendering result
* @throws InvalidCallException if the view file cannot be found * @throws InvalidParamException if the view cannot be resolved or the view file does not exist.
* @see findViewFile() * @see renderFile
* @see findViewFile
*/ */
public function renderPartial($view, $params = array()) public function render($view, $params = array(), $context = null)
{ {
$file = $this->findViewFile($view); $viewFile = $this->findViewFile($context, $view);
if ($file !== false) { return $this->renderFile($viewFile, $params, $context);
return $this->renderFile($file, $params);
} else {
throw new InvalidCallException("Unable to find the view file for view '$view'.");
}
} }
/** /**
* Renders a view file. * Renders a view file.
* *
* If a [[ViewRenderer|view renderer]] is installed, this method will try to use the view renderer * If [[theme]] is enabled (not null), it will try to render the themed version of the view file as long
* to render the view file. Otherwise, it will simply include the view file, capture its output * as it is available.
* and return it as a string. *
* 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 $file the view file. * @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 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 * @return string the rendering result
* @throws InvalidParamException if the view file does not exist
*/ */
public function renderFile($file, $params = array()) public function renderFile($viewFile, $params = array(), $context = null)
{ {
$renderer = Yii::$app->getViewRenderer(); $viewFile = Yii::getAlias($viewFile);
if ($renderer !== null) { if (is_file($viewFile)) {
return $renderer->render($this, $file, $params); if ($this->theme !== null) {
$viewFile = $this->theme->applyTo($viewFile);
}
$viewFile = FileHelper::localize($viewFile);
} else { } else {
return $this->renderPhpFile($file, $params); throw new InvalidParamException("The view file does not exist: $viewFile");
} }
$oldContext = $this->context;
if ($context !== null) {
$this->context = $context;
}
if ($this->renderer !== null) {
$output = $this->renderer->render($this, $viewFile, $params);
} else {
$output = $this->renderPhpFile($viewFile, $params);
}
$this->context = $oldContext;
return $output;
} }
/** /**
@ -163,6 +151,8 @@ class View extends Component
* It extracts the given parameters and makes them available in the view 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. * 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 string $_file_ the view file.
* @param array $_params_ the parameters (name-value pairs) that will be extracted and made available in 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 * @return string the rendering result
@ -177,6 +167,95 @@ class View extends Component
} }
/** /**
* 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[YDP-$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);
}
/**
* Finds the view file based on the given view name.
*
* A view name 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 [[Controller::viewPath|viewPath]]
* of the context object, assuming the context is either a [[Controller]] or a [[Widget]].
*
* If the view name does not contain a file extension, it will use the default one `.php`.
*
* @param object $context the view context object
* @param string $view the view name or the path alias of the view file.
* @return string the view file path. Note that the file may not exist.
* @throws InvalidParamException if the view file is an invalid path alias or the context cannot be
* used to determine the actual view file corresponding to the specified view.
*/
protected function findViewFile($context, $view)
{
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"
$file = Yii::$app->controller->module->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
} elseif ($context instanceof Controller || $context instanceof Widget) {
/** @var $context Controller|Widget */
$file = $context->getViewPath() . DIRECTORY_SEPARATOR . $view;
} else {
throw new InvalidParamException("Unable to resolve the view file for '$view'.");
}
return FileHelper::getExtension($file) === '' ? $file . '.php' : $file;
}
/**
* Creates a widget. * Creates a widget.
* This method will use [[Yii::createObject()]] to create the widget. * This method will use [[Yii::createObject()]] to create the widget.
* @param string $class the widget class name or path alias * @param string $class the widget class name or path alias
@ -186,7 +265,7 @@ class View extends Component
public function createWidget($class, $properties = array()) public function createWidget($class, $properties = array())
{ {
$properties['class'] = $class; $properties['class'] = $class;
return Yii::createObject($properties, $this->owner); return Yii::createObject($properties, $this->context);
} }
/** /**
@ -225,7 +304,7 @@ class View extends Component
public function beginWidget($class, $properties = array()) public function beginWidget($class, $properties = array())
{ {
$widget = $this->createWidget($class, $properties); $widget = $this->createWidget($class, $properties);
$this->_widgetStack[] = $widget; $this->widgetStack[] = $widget;
return $widget; return $widget;
} }
@ -235,260 +314,108 @@ class View extends Component
* If you want to capture the rendering result of a widget, you may use * If you want to capture the rendering result of a widget, you may use
* [[createWidget()]] and [[Widget::run()]]. * [[createWidget()]] and [[Widget::run()]].
* @return Widget the widget instance * @return Widget the widget instance
* @throws Exception if [[beginWidget()]] and [[endWidget()]] calls are not properly nested * @throws InvalidCallException if [[beginWidget()]] and [[endWidget()]] calls are not properly nested
*/ */
public function endWidget() public function endWidget()
{ {
$widget = array_pop($this->_widgetStack); $widget = array_pop($this->widgetStack);
if ($widget instanceof Widget) { if ($widget instanceof Widget) {
$widget->run(); $widget->run();
return $widget; return $widget;
} else { } else {
throw new Exception("Unmatched beginWidget() and endWidget() calls."); throw new InvalidCallException("Unmatched beginWidget() and endWidget() calls.");
} }
} }
//
// /**
// * Begins recording a clip.
// * This method is a shortcut to beginning [[yii\widgets\Clip]]
// * @param string $id the clip ID.
// * @param array $properties initial property values for [[yii\widgets\Clip]]
// */
// public function beginClip($id, $properties = array())
// {
// $properties['id'] = $id;
// $this->beginWidget('yii\widgets\Clip', $properties);
// }
//
// /**
// * Ends recording a clip.
// */
// public function endClip()
// {
// $this->endWidget();
// }
//
// /**
// * 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 [[yii\widgets\OutputCache]]
// * @return boolean whether we need to generate content for caching. False if cached version is available.
// * @see endCache
// */
// public function beginCache($id, $properties = array())
// {
// $properties['id'] = $id;
// $cache = $this->beginWidget('yii\widgets\OutputCache', $properties);
// if ($cache->getIsContentCached()) {
// $this->endCache();
// return false;
// } else {
// return true;
// }
// }
//
// /**
// * Ends fragment caching.
// * This is an alias to [[endWidget()]]
// * @see beginCache
// */
// public function endCache()
// {
// $this->endWidget();
// }
//
// /**
// * Begins the rendering of content that is to be decorated by the specified view.
// * @param mixed $view the name of the view that will be used to decorate the content. The actual view script
// * is resolved via {@link getViewFile}. If this parameter is null (default),
// * the default layout will be used as the decorative view.
// * Note that if the current controller does not belong to
// * any module, the default layout refers to the application's {@link CWebApplication::layout default layout};
// * If the controller belongs to a module, the default layout refers to the module's
// * {@link CWebModule::layout default layout}.
// * @param array $params the variables (name=>value) to be extracted and made available in the decorative view.
// * @see endContent
// * @see yii\widgets\ContentDecorator
// */
// public function beginContent($view, $params = array())
// {
// $this->beginWidget('yii\widgets\ContentDecorator', array(
// 'view' => $view,
// 'params' => $params,
// ));
// }
//
// /**
// * Ends the rendering of content.
// * @see beginContent
// */
// public function endContent()
// {
// $this->endWidget();
// }
/** /**
* Finds the view file based on the given view name. * Begins recording a clip.
* * This method is a shortcut to beginning [[yii\widgets\Clip]]
* A view name can be specified in one of the following formats: * @param string $id the clip ID.
* * @param boolean $renderInPlace whether to render the clip content in place.
* - path alias (e.g. "@app/views/site/index"); * Defaults to false, meaning the captured clip will not be displayed.
* - absolute path within application (e.g. "//site/index"): the view name starts with double slashes. * @return \yii\widgets\Clip the Clip widget instance
* The actual view file will be looked for under the [[Application::viewPath|view path]] of the application. * @see \yii\widgets\Clip
* - 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 the [[owner]]'s view path.
* If [[owner]] is a widget or a controller, its view path is given by their `viewPath` property.
* If [[owner]] is an object of any other type, its view path is the `view` sub-directory of the directory
* containing the owner class file.
*
* If the view name does not contain a file extension, it will default to `.php`.
*
* If [[enableTheme]] is true and there is an active application them, the method will also
* attempt to use a themed version of the view file, when available.
*
* And if [[enableI18N]] is true, the method will attempt to use a translated version of the view file,
* when available.
*
* @param string $view the view name or path alias. If the view name does not specify
* the view file extension name, it will use `.php` as the extension name.
* @return string the view file path if it exists. False if the view file cannot be found.
* @throws InvalidConfigException if the view file does not exist
*/ */
public function findViewFile($view) public function beginClip($id, $renderInPlace = false)
{ {
if (FileHelper::getExtension($view) === '') { return $this->beginWidget('yii\widgets\Clip', array(
$view .= '.php'; 'id' => $id,
} 'renderInPlace' => $renderInPlace,
if (strncmp($view, '@', 1) === 0) { 'view' => $this,
// e.g. "@app/views/common" ));
if (($file = Yii::getAlias($view)) === false) {
throw new InvalidConfigException("Invalid path alias: $view");
}
} elseif (strncmp($view, '/', 1) !== 0) {
// e.g. "index"
if ($this->owner instanceof Controller || $this->owner instanceof Widget) {
$file = $this->owner->getViewPath() . DIRECTORY_SEPARATOR . $view;
} elseif ($this->owner !== null) {
$class = new \ReflectionClass($this->owner);
$file = dirname($class->getFileName()) . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . $view;
} else {
$file = Yii::$app->getViewPath() . DIRECTORY_SEPARATOR . $view;
}
} elseif (strncmp($view, '//', 2) !== 0 && Yii::$app->controller !== null) {
// e.g. "/site/index"
$file = Yii::$app->controller->module->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
} else {
// e.g. "//layouts/main"
$file = Yii::$app->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
} }
if (is_file($file)) { /**
if ($this->enableTheme && ($theme = Yii::$app->getTheme()) !== null) { * Ends recording a clip.
$file = $theme->apply($file); */
public function endClip()
{
$this->endWidget();
} }
return $this->enableI18N ? FileHelper::localize($file, $this->language, $this->sourceLanguage) : $file;
} else { /**
throw new InvalidConfigException("View file for view '$view' does not exist: $file"); * Begins the rendering of content that is to be decorated by the specified view.
* @param string $view the name of the view that will be used to decorate the content enclosed by this widget.
* Please refer to [[View::findViewFile()]] on how to set this property.
* @param array $params the variables (name=>value) to be extracted and made available in the decorative view.
* @return \yii\widgets\ContentDecorator the ContentDecorator widget instance
* @see \yii\widgets\ContentDecorator
*/
public function beginContent($view, $params = array())
{
return $this->beginWidget('yii\widgets\ContentDecorator', array(
'view' => $this,
'viewName' => $view,
'params' => $params,
));
} }
/**
* Ends the rendering of content.
*/
public function endContent()
{
$this->endWidget();
} }
/** /**
* Finds the layout file that can be applied to the view. * Begins fragment caching.
* * This method will display cached content if it is available.
* The applicable layout is resolved according to the following rules: * If not, it will start caching and would expect an [[endCache()]]
* * call to end the cache and save the content into cache.
* - If [[layout]] is specified as a string, use it as the layout name and search for the layout file * A typical usage of fragment caching is as follows,
* under the layout path of the currently active module;
* - If [[layout]] is null and [[owner]] is a controller:
* * If the controller's [[Controller::layout|layout]] is a string, use it as the layout name
* and search for the layout file under the layout path of the parent module of the controller;
* * If the controller's [[Controller::layout|layout]] is null, look through its ancestor modules
* and find the first one whose [[Module::layout|layout]] is not null. Use the layout specified
* by that module;
* - Returns false for all other cases.
* *
* Like view names, a layout name can take several formats: * ~~~
* if($this->beginCache($id)) {
* // ...generate content here
* $this->endCache();
* }
* ~~~
* *
* - path alias (e.g. "@app/views/layouts/main"); * @param string $id a unique ID identifying the fragment to be cached.
* - absolute path (e.g. "/main"): the layout name starts with a slash. The actual layout file will be * @param array $properties initial property values for [[\yii\widgets\FragmentCache]]
* looked for under the [[Application::layoutPath|layout path]] of the application; * @return boolean whether you should generate the content for caching.
* - relative path (e.g. "main"): the actual layout layout file will be looked for under the * False if the cached version is available.
* [[Module::viewPath|view path]] of the context module determined by the above layout resolution process.
*
* If the layout name does not contain a file extension, it will default to `.php`.
*
* If [[enableTheme]] is true and there is an active application them, the method will also
* attempt to use a themed version of the layout file, when available.
*
* And if [[enableI18N]] is true, the method will attempt to use a translated version of the layout file,
* when available.
*
* @return string|boolean the layout file path, or false if layout is not needed.
* @throws InvalidConfigException if the layout file cannot be found
*/ */
public function findLayoutFile() public function beginCache($id, $properties = array())
{ {
/** @var $module Module */ $properties['id'] = $id;
if (is_string($this->layout)) { $properties['view'] = $this;
if (Yii::$app->controller) { /** @var $cache \yii\widgets\FragmentCache */
$module = Yii::$app->controller->module; $cache = $this->beginWidget('yii\widgets\FragmentCache', $properties);
} else { if ($cache->getCachedContent() !== false) {
$module = Yii::$app; $this->endCache();
}
$view = $this->layout;
} elseif ($this->owner instanceof Controller) {
if (is_string($this->owner->layout)) {
$module = $this->owner->module;
$view = $this->owner->layout;
} elseif ($this->owner->layout === null) {
$module = $this->owner->module;
while ($module !== null && $module->layout === null) {
$module = $module->module;
}
if ($module !== null && is_string($module->layout)) {
$view = $module->layout;
}
}
}
if (!isset($view)) {
return false; return false;
}
if (FileHelper::getExtension($view) === '') {
$view .= '.php';
}
if (strncmp($view, '@', 1) === 0) {
if (($file = Yii::getAlias($view)) === false) {
throw new InvalidConfigException("Invalid path alias: $view");
}
} elseif (strncmp($view, '/', 1) === 0) {
$file = Yii::$app->getLayoutPath() . DIRECTORY_SEPARATOR . $view;
} else { } else {
$file = $module->getLayoutPath() . DIRECTORY_SEPARATOR . $view; return true;
}
if (is_file($file)) {
if ($this->enableTheme && ($theme = Yii::$app->getTheme()) !== null) {
$file = $theme->apply($file);
} }
return $this->enableI18N ? FileHelper::localize($file, $this->language, $this->sourceLanguage) : $file;
} else {
throw new InvalidConfigException("Layout file for layout '$view' does not exist: $file");
} }
/**
* Ends fragment caching.
*/
public function endCache()
{
$this->endWidget();
} }
} }

4
framework/base/ViewRenderer.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* ViewRenderer class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

40
framework/base/Widget.php

@ -1,14 +1,15 @@
<?php <?php
/** /**
* Widget class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\base; namespace yii\base;
use Yii;
use yii\helpers\FileHelper;
/** /**
* Widget is the base class for widgets. * Widget is the base class for widgets.
* *
@ -72,35 +73,26 @@ class Widget extends Component
/** /**
* Renders a view. * Renders a view.
* * @param string $view the view name. Please refer to [[findViewFile()]] on how to specify a view name.
* The method first finds the actual view file corresponding to the specified view. * @param array $params the parameters (name-value pairs) that should be made available in the view.
* It then calls [[renderFile()]] to render the view file. The rendering result is returned * @return string the rendering result.
* as a string. If the view file does not exist, an exception will be thrown. * @throws InvalidParamException if the view file does not exist.
*
* To determine which view file should be rendered, the method calls [[findViewFile()]] which
* will search in the directories as specified by [[basePath]].
*
* View name can be a path alias representing an absolute file path (e.g. `@app/views/layout/index`),
* or a path relative to [[basePath]]. The file suffix is optional and defaults to `.php` if not given
* in the view name.
*
* @param string $view the view to be rendered. This can be either a path alias or a path relative to [[basePath]].
* @param array $params the parameters that should be made available in the view. The PHP function `extract()`
* will be called on this variable to extract the variables from this parameter.
* @return string the rendering result
* @throws Exception if the view file cannot be found
*/ */
public function render($view, $params = array()) public function render($view, $params = array())
{ {
return $this->createView()->renderPartial($view, $params); return Yii::$app->getView()->render($view, $params, $this);
} }
/** /**
* @return View * 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 createView() public function renderFile($file, $params = array())
{ {
return new View($this); return Yii::$app->getView()->renderFile($file, $params, $this);
} }
/** /**

4
framework/caching/ApcCache.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* ApcCache class file
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

22
framework/caching/Cache.php

@ -1,15 +1,14 @@
<?php <?php
/** /**
* Cache class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\caching; namespace yii\caching;
use yii\base\Component; use yii\base\Component;
use yii\helpers\StringHelper;
/** /**
* Cache is the base class for cache classes supporting different cache storage implementation. * Cache is the base class for cache classes supporting different cache storage implementation.
@ -72,13 +71,13 @@ abstract class Cache extends Component implements \ArrayAccess
/** /**
* Builds a normalized cache key from one or multiple parameters. * Builds a normalized cache key from a given key.
* *
* The generated key contains letters and digits only, and its length is no more than 32. * The generated key contains letters and digits only, and its length is no more than 32.
* *
* If only one parameter is given and it is already a normalized key, then * If the given key is a string containing alphanumeric characters only and no more than 32 characters,
* it will be returned back without change. Otherwise, a normalized key * then the key will be returned back without change. Otherwise, a normalized key
* is generated by serializing all given parameters and applying MD5 hashing. * is generated by serializing the given key and applying MD5 hashing.
* *
* The following example builds a cache key using three parameters: * The following example builds a cache key using three parameters:
* *
@ -86,16 +85,15 @@ abstract class Cache extends Component implements \ArrayAccess
* $key = $cache->buildKey($className, $method, $id); * $key = $cache->buildKey($className, $method, $id);
* ~~~ * ~~~
* *
* @param string $key the first parameter * @param array|string $key the key to be normalized
* @return string the generated cache key * @return string the generated cache key
*/ */
public function buildKey($key) public function buildKey($key)
{ {
if (func_num_args() === 1 && ctype_alnum($key) && strlen($key) <= 32) { if (is_string($key)) {
return (string)$key; return ctype_alnum($key) && StringHelper::strlen($key) <= 32 ? $key : md5($key);
} else { } else {
$params = func_get_args(); return md5(json_encode($key));
return md5(serialize($params));
} }
} }

4
framework/caching/ChainedDependency.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* ChainedDependency class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

4
framework/caching/DbCache.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* DbCache class file
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

41
framework/caching/DbDependency.php

@ -1,23 +1,21 @@
<?php <?php
/** /**
* DbDependency class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\caching; namespace yii\caching;
use Yii;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
use yii\db\Connection; use yii\db\Connection;
use yii\db\Query;
/** /**
* DbDependency represents a dependency based on the query result of a SQL statement. * DbDependency represents a dependency based on the query result of a SQL statement.
* *
* If the query result changes, the dependency is considered as changed. * If the query result changes, the dependency is considered as changed.
* The query is specified via the [[query]] property. * The query is specified via the [[sql]] property.
* *
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
@ -29,23 +27,25 @@ class DbDependency extends Dependency
*/ */
public $connectionID = 'db'; public $connectionID = 'db';
/** /**
* @var Query the SQL query whose result is used to determine if the dependency has been changed. * @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. * Only the first row of the query result will be used.
*/ */
public $query; public $sql;
/** /**
* @var Connection the DB connection instance * @var array the parameters (name=>value) to be bound to the SQL statement specified by [[sql]].
*/ */
private $_db; public $params;
/** /**
* Constructor. * Constructor.
* @param Query $query the SQL query whose result is used to determine if the dependency has been changed. * @param string $sql the SQL query whose result is used to determine if the dependency has been changed.
* @param array $params the parameters (name=>value) to be bound to the SQL statement specified by [[sql]].
* @param array $config name-value pairs that will be used to initialize the object properties * @param array $config name-value pairs that will be used to initialize the object properties
*/ */
public function __construct($query = null, $config = array()) public function __construct($sql, $params = array(), $config = array())
{ {
$this->query = $query; $this->sql = $sql;
$this->params = $params;
parent::__construct($config); parent::__construct($config);
} }
@ -68,22 +68,23 @@ class DbDependency extends Dependency
protected function generateDependencyData() protected function generateDependencyData()
{ {
$db = $this->getDb(); $db = $this->getDb();
/**
* @var \yii\db\Command $command
*/
$command = $this->query->createCommand($db);
if ($db->enableQueryCache) { if ($db->enableQueryCache) {
// temporarily disable and re-enable query caching // temporarily disable and re-enable query caching
$db->enableQueryCache = false; $db->enableQueryCache = false;
$result = $command->queryRow(); $result = $db->createCommand($this->sql, $this->params)->queryRow();
$db->enableQueryCache = true; $db->enableQueryCache = true;
} else { } else {
$result = $command->queryRow(); $result = $db->createCommand($this->sql, $this->params)->queryRow();
} }
return $result; return $result;
} }
/** /**
* @var Connection the DB connection instance
*/
private $_db;
/**
* Returns the DB connection instance used for caching purpose. * Returns the DB connection instance used for caching purpose.
* @return Connection the DB connection instance * @return Connection the DB connection instance
* @throws InvalidConfigException if [[connectionID]] does not point to a valid application component. * @throws InvalidConfigException if [[connectionID]] does not point to a valid application component.
@ -91,11 +92,11 @@ class DbDependency extends Dependency
public function getDb() public function getDb()
{ {
if ($this->_db === null) { if ($this->_db === null) {
$db = \Yii::$app->getComponent($this->connectionID); $db = Yii::$app->getComponent($this->connectionID);
if ($db instanceof Connection) { if ($db instanceof Connection) {
$this->_db = $db; $this->_db = $db;
} else { } else {
throw new InvalidConfigException("DbCache::connectionID must refer to the ID of a DB application component."); throw new InvalidConfigException("DbCacheDependency::connectionID must refer to the ID of a DB application component.");
} }
} }
return $this->_db; return $this->_db;

4
framework/caching/Dependency.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Dependency class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

4
framework/caching/DummyCache.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* DummyCache class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

4
framework/caching/ExpressionDependency.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* ExpressionDependency class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

7
framework/caching/FileCache.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* FileCache class file
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -54,9 +52,6 @@ class FileCache extends Cache
{ {
parent::init(); parent::init();
$this->cachePath = \Yii::getAlias($this->cachePath); $this->cachePath = \Yii::getAlias($this->cachePath);
if ($this->cachePath === false) {
throw new InvalidConfigException('FileCache.cachePath must be a valid path alias.');
}
if (!is_dir($this->cachePath)) { if (!is_dir($this->cachePath)) {
mkdir($this->cachePath, 0777, true); mkdir($this->cachePath, 0777, true);
} }

4
framework/caching/FileDependency.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* FileDependency class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

4
framework/caching/MemCache.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* MemCache class file
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

4
framework/caching/MemCacheServer.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* MemCacheServer class file
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

4
framework/caching/WinCache.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* WinCache class file
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

4
framework/caching/XCache.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* XCache class file
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

4
framework/caching/ZendDataCache.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* ZendDataCache class file
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

9
framework/console/Application.php

@ -3,7 +3,7 @@
* Console Application class file. * Console Application class file.
* *
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -91,9 +91,10 @@ class Application extends \yii\base\Application
/** @var $request Request */ /** @var $request Request */
$request = $this->getRequest(); $request = $this->getRequest();
if ($request->getIsConsoleRequest()) { if ($request->getIsConsoleRequest()) {
return $this->runAction($request->route, $request->params); list ($route, $params) = $request->resolve();
return $this->runAction($route, $params);
} else { } else {
throw new Exception(\Yii::t('yii|this script must be run from the command line.')); throw new Exception(\Yii::t('yii|This script must be run from the command line.'));
} }
} }
@ -126,7 +127,7 @@ class Application extends \yii\base\Application
'message' => 'yii\console\controllers\MessageController', 'message' => 'yii\console\controllers\MessageController',
'help' => 'yii\console\controllers\HelpController', 'help' => 'yii\console\controllers\HelpController',
'migrate' => 'yii\console\controllers\MigrateController', 'migrate' => 'yii\console\controllers\MigrateController',
'app' => 'yii\console\controllers\CreateController', 'app' => 'yii\console\controllers\AppController',
'cache' => 'yii\console\controllers\CacheController', 'cache' => 'yii\console\controllers\CacheController',
); );
} }

4
framework/console/Controller.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Controller class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

4
framework/console/Exception.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Exception class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

40
framework/console/Request.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Request class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -17,49 +15,37 @@ class Request extends \yii\base\Request
{ {
const ANONYMOUS_PARAMS = '-args'; const ANONYMOUS_PARAMS = '-args';
/**
* @var string the controller route specified by this request. If this is an empty string,
* it means the [[Application::defaultRoute|default route]] will be used.
* Note that the value of this property may not be a correct route. The console application
* will determine it is valid or not when it attempts to execute with this route.
*/
public $route;
/**
* @var array
*/
public $params;
public function init()
{
parent::init();
$this->resolveRequest();
}
public function getRawParams() public function getRawParams()
{ {
return isset($_SERVER['argv']) ? $_SERVER['argv'] : array(); return isset($_SERVER['argv']) ? $_SERVER['argv'] : array();
} }
protected function resolveRequest() /**
* 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->getRawParams(); $rawParams = $this->getRawParams();
array_shift($rawParams); // the 1st argument is the yiic script name array_shift($rawParams); // the 1st argument is the yiic script name
if (isset($rawParams[0])) { if (isset($rawParams[0])) {
$this->route = $rawParams[0]; $route = $rawParams[0];
array_shift($rawParams); array_shift($rawParams);
} else { } else {
$this->route = ''; $route = '';
} }
$this->params = array(self::ANONYMOUS_PARAMS => array()); $params = array(self::ANONYMOUS_PARAMS => array());
foreach ($rawParams as $param) { foreach ($rawParams as $param) {
if (preg_match('/^--(\w+)(=(.*))?$/', $param, $matches)) { if (preg_match('/^--(\w+)(=(.*))?$/', $param, $matches)) {
$name = $matches[1]; $name = $matches[1];
$this->params[$name] = isset($matches[3]) ? $matches[3] : true; $params[$name] = isset($matches[3]) ? $matches[3] : true;
} else { } else {
$this->params[self::ANONYMOUS_PARAMS][] = $param; $params[self::ANONYMOUS_PARAMS][] = $param;
} }
} }
return array($route, $params);
} }
} }

24
framework/console/controllers/CreateController.php → framework/console/controllers/AppController.php

@ -1,16 +1,14 @@
<?php <?php
/** /**
* CreateController class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\console\controllers; namespace yii\console\controllers;
use yii\console\Controller; use yii\console\Controller;
use yii\util\FileHelper; use yii\helpers\FileHelper;
use yii\base\Exception; use yii\base\Exception;
/** /**
@ -20,14 +18,14 @@ use yii\base\Exception;
* @author Alexander Makarov <sam@rmcreative.ru> * @author Alexander Makarov <sam@rmcreative.ru>
* @since 2.0 * @since 2.0
*/ */
class CreateController extends Controller class AppController extends Controller
{ {
private $_rootPath; private $_rootPath;
private $_config; private $_config;
/** /**
* @var string custom template path. If specified, templates will be * @var string custom template path. If specified, templates will be
* searched there additionally to `framework/console/create`. * searched there additionally to `framework/console/webapp`.
*/ */
public $templatesPath; public $templatesPath;
@ -46,6 +44,16 @@ class CreateController extends Controller
} }
} }
public function globalOptions()
{
return array('templatesPath', 'type');
}
public function actionIndex()
{
$this->forward('help/index', array('-args' => array('app/create')));
}
/** /**
* Generates Yii application at the path specified via appPath parameter. * Generates Yii application at the path specified via appPath parameter.
* *
@ -56,7 +64,7 @@ class CreateController extends Controller
* @throws \yii\base\Exception if path specified is not valid * @throws \yii\base\Exception if path specified is not valid
* @return integer the exit status * @return integer the exit status
*/ */
public function actionIndex($path) public function actionCreate($path)
{ {
$path = strtr($path, '/\\', DIRECTORY_SEPARATOR); $path = strtr($path, '/\\', DIRECTORY_SEPARATOR);
if(strpos($path, DIRECTORY_SEPARATOR) === false) { if(strpos($path, DIRECTORY_SEPARATOR) === false) {
@ -127,7 +135,7 @@ class CreateController extends Controller
*/ */
protected function getDefaultTemplatesPath() protected function getDefaultTemplatesPath()
{ {
return realpath(__DIR__.'/../create'); return realpath(__DIR__.'/../webapp');
} }
/** /**

4
framework/console/controllers/CacheController.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* CacheController class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

6
framework/console/controllers/HelpController.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* HelpController class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -15,7 +13,7 @@ use yii\console\Exception;
use yii\base\InlineAction; use yii\base\InlineAction;
use yii\console\Controller; use yii\console\Controller;
use yii\console\Request; use yii\console\Request;
use yii\util\StringHelper; use yii\helpers\StringHelper;
/** /**
* This command provides help information about console commands. * This command provides help information about console commands.

4
framework/console/controllers/MessageController.php

@ -1,10 +1,8 @@
<?php <?php
/** /**
* MessageController class file.
*
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

8
framework/console/controllers/MigrateController.php

@ -1,10 +1,8 @@
<?php <?php
/** /**
* MigrateController class file.
*
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -15,7 +13,7 @@ use yii\console\Exception;
use yii\console\Controller; use yii\console\Controller;
use yii\db\Connection; use yii\db\Connection;
use yii\db\Query; use yii\db\Query;
use yii\util\ArrayHelper; use yii\helpers\ArrayHelper;
/** /**
* This command manages application migrations. * This command manages application migrations.
@ -116,7 +114,7 @@ class MigrateController extends Controller
{ {
if (parent::beforeAction($action)) { if (parent::beforeAction($action)) {
$path = Yii::getAlias($this->migrationPath); $path = Yii::getAlias($this->migrationPath);
if ($path === false || !is_dir($path)) { if (!is_dir($path)) {
throw new Exception("The migration directory \"{$this->migrationPath}\" does not exist."); throw new Exception("The migration directory \"{$this->migrationPath}\" does not exist.");
} }
$this->migrationPath = $path; $this->migrationPath = $path;

2
framework/console/create/config.php → framework/console/webapp/config.php

@ -1,5 +1,5 @@
<?php <?php
/** @var $controller \yii\console\controllers\CreateController */ /** @var $controller \yii\console\controllers\AppController */
$controller = $this; $controller = $this;
return array( return array(

0
framework/console/create/default/index.php → framework/console/webapp/default/index.php

0
framework/console/create/default/protected/config/main.php → framework/console/webapp/default/protected/config/main.php

0
framework/console/create/default/protected/controllers/SiteController.php → framework/console/webapp/default/protected/controllers/SiteController.php

0
framework/console/create/default/protected/views/layouts/main.php → framework/console/webapp/default/protected/views/layouts/main.php

0
framework/console/create/default/protected/views/site/index.php → framework/console/webapp/default/protected/views/site/index.php

4
framework/db/ActiveQuery.php

@ -1,10 +1,8 @@
<?php <?php
/** /**
* ActiveQuery class file.
*
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

12
framework/db/ActiveRecord.php

@ -1,24 +1,22 @@
<?php <?php
/** /**
* ActiveRecord class file.
*
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\db; namespace yii\db;
use yii\base\Model; use yii\base\Model;
use yii\base\Event; use yii\base\InvalidParamException;
use yii\base\ModelEvent; use yii\base\ModelEvent;
use yii\base\UnknownMethodException; use yii\base\UnknownMethodException;
use yii\base\InvalidCallException; use yii\base\InvalidCallException;
use yii\db\Connection; use yii\db\Connection;
use yii\db\TableSchema; use yii\db\TableSchema;
use yii\db\Expression; use yii\db\Expression;
use yii\util\StringHelper; use yii\helpers\StringHelper;
/** /**
* ActiveRecord is the base class for classes representing relational data in terms of objects. * ActiveRecord is the base class for classes representing relational data in terms of objects.
@ -1045,7 +1043,7 @@ class ActiveRecord extends Model
* It can be declared in either the Active Record class itself or one of its behaviors. * It can be declared in either the Active Record class itself or one of its behaviors.
* @param string $name the relation name * @param string $name the relation name
* @return ActiveRelation the relation object * @return ActiveRelation the relation object
* @throws InvalidCallException if the named relation does not exist. * @throws InvalidParamException if the named relation does not exist.
*/ */
public function getRelation($name) public function getRelation($name)
{ {
@ -1057,7 +1055,7 @@ class ActiveRecord extends Model
} }
} catch (UnknownMethodException $e) { } catch (UnknownMethodException $e) {
} }
throw new InvalidCallException(get_class($this) . ' has no relation named "' . $name . '".'); throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".');
} }
/** /**

20
framework/db/ActiveRelation.php

@ -1,10 +1,8 @@
<?php <?php
/** /**
* ActiveRelation class file.
*
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -57,16 +55,16 @@ class ActiveRelation extends ActiveQuery
/** /**
* Specifies the relation associated with the pivot table. * Specifies the relation associated with the pivot table.
* @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]]. * @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]].
* @param callback $callback a PHP callback for customizing the relation associated with the pivot table. * @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
* Its signature should be `function($query)`, where `$query` is the query to be customized. * Its signature should be `function($query)`, where `$query` is the query to be customized.
* @return ActiveRelation the relation object itself. * @return ActiveRelation the relation object itself.
*/ */
public function via($relationName, $callback = null) public function via($relationName, $callable = null)
{ {
$relation = $this->primaryModel->getRelation($relationName); $relation = $this->primaryModel->getRelation($relationName);
$this->via = array($relationName, $relation); $this->via = array($relationName, $relation);
if ($callback !== null) { if ($callable !== null) {
call_user_func($callback, $relation); call_user_func($callable, $relation);
} }
return $this; return $this;
} }
@ -77,11 +75,11 @@ class ActiveRelation extends ActiveQuery
* @param array $link the link between the pivot table and the table associated with [[primaryModel]]. * @param array $link the link between the pivot table and the table associated with [[primaryModel]].
* The keys of the array represent the columns in the pivot table, and the values represent the columns * The keys of the array represent the columns in the pivot table, and the values represent the columns
* in the [[primaryModel]] table. * in the [[primaryModel]] table.
* @param callback $callback a PHP callback for customizing the relation associated with the pivot table. * @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
* Its signature should be `function($query)`, where `$query` is the query to be customized. * Its signature should be `function($query)`, where `$query` is the query to be customized.
* @return ActiveRelation * @return ActiveRelation
*/ */
public function viaTable($tableName, $link, $callback = null) public function viaTable($tableName, $link, $callable = null)
{ {
$relation = new ActiveRelation(array( $relation = new ActiveRelation(array(
'modelClass' => get_class($this->primaryModel), 'modelClass' => get_class($this->primaryModel),
@ -91,8 +89,8 @@ class ActiveRelation extends ActiveQuery
'asArray' => true, 'asArray' => true,
)); ));
$this->via = $relation; $this->via = $relation;
if ($callback !== null) { if ($callable !== null) {
call_user_func($callback, $relation); call_user_func($callable, $relation);
} }
return $this; return $this;
} }

4
framework/db/ColumnSchema.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* ColumnSchema class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

18
framework/db/Command.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Command class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -136,7 +134,7 @@ class Command extends \yii\base\Component
} catch (\Exception $e) { } catch (\Exception $e) {
\Yii::error($e->getMessage() . "\nFailed to prepare SQL: $sql", __CLASS__); \Yii::error($e->getMessage() . "\nFailed to prepare SQL: $sql", __CLASS__);
$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null; $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
throw new Exception($e->getMessage(), (int)$e->getCode(), $errorInfo); throw new Exception($e->getMessage(), $errorInfo, (int)$e->getCode());
} }
} }
} }
@ -294,7 +292,7 @@ class Command extends \yii\base\Component
\Yii::error("$message\nFailed to execute SQL: {$sql}{$paramLog}", __CLASS__); \Yii::error("$message\nFailed to execute SQL: {$sql}{$paramLog}", __CLASS__);
$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null; $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
throw new Exception($message, (int)$e->getCode(), $errorInfo); throw new Exception($message, $errorInfo, (int)$e->getCode());
} }
} }
@ -391,7 +389,13 @@ class Command extends \yii\base\Component
} }
if (isset($cache)) { if (isset($cache)) {
$cacheKey = $cache->buildKey(__CLASS__, $db->dsn, $db->username, $sql, $paramLog); $cacheKey = $cache->buildKey(array(
__CLASS__,
$db->dsn,
$db->username,
$sql,
$paramLog,
));
if (($result = $cache->get($cacheKey)) !== false) { if (($result = $cache->get($cacheKey)) !== false) {
\Yii::trace('Query result found in cache', __CLASS__); \Yii::trace('Query result found in cache', __CLASS__);
return $result; return $result;
@ -433,7 +437,7 @@ class Command extends \yii\base\Component
$message = $e->getMessage(); $message = $e->getMessage();
\Yii::error("$message\nCommand::$method() failed: {$sql}{$paramLog}", __CLASS__); \Yii::error("$message\nCommand::$method() failed: {$sql}{$paramLog}", __CLASS__);
$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null; $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
throw new Exception($message, (int)$e->getCode(), $errorInfo); throw new Exception($message, $errorInfo, (int)$e->getCode());
} }
} }

8
framework/db/Connection.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Connection class file
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -322,7 +320,7 @@ class Connection extends Component
{ {
if ($this->pdo === null) { if ($this->pdo === null) {
if (empty($this->dsn)) { if (empty($this->dsn)) {
throw new InvalidConfigException('Connection.dsn cannot be empty.'); throw new InvalidConfigException('Connection::dsn cannot be empty.');
} }
try { try {
\Yii::trace('Opening DB connection: ' . $this->dsn, __CLASS__); \Yii::trace('Opening DB connection: ' . $this->dsn, __CLASS__);
@ -332,7 +330,7 @@ class Connection extends Component
catch (\PDOException $e) { catch (\PDOException $e) {
\Yii::error("Failed to open DB connection ({$this->dsn}): " . $e->getMessage(), __CLASS__); \Yii::error("Failed to open DB connection ({$this->dsn}): " . $e->getMessage(), __CLASS__);
$message = YII_DEBUG ? 'Failed to open DB connection: ' . $e->getMessage() : 'Failed to open DB connection.'; $message = YII_DEBUG ? 'Failed to open DB connection: ' . $e->getMessage() : 'Failed to open DB connection.';
throw new Exception($message, (int)$e->getCode(), $e->errorInfo); throw new Exception($message, $e->errorInfo, (int)$e->getCode());
} }
} }
} }

4
framework/db/DataReader.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* DataReader class file
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

11
framework/db/Exception.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Exception class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -26,13 +24,14 @@ class Exception extends \yii\base\Exception
/** /**
* Constructor. * Constructor.
* @param string $message PDO error message * @param string $message PDO error message
* @param integer $code PDO error code
* @param mixed $errorInfo PDO error info * @param mixed $errorInfo PDO error info
* @param integer $code PDO error code
* @param \Exception $previous The previous exception used for the exception chaining.
*/ */
public function __construct($message, $code = 0, $errorInfo = null) public function __construct($message, $errorInfo = null, $code = 0, \Exception $previous = null)
{ {
$this->errorInfo = $errorInfo; $this->errorInfo = $errorInfo;
parent::__construct($message, $code); parent::__construct($message, $code, $previous);
} }
/** /**

4
framework/db/Expression.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Expression class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

4
framework/db/Migration.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Migration class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

119
framework/db/Query.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Query class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -37,9 +35,19 @@ namespace yii\db;
class Query extends \yii\base\Component class Query extends \yii\base\Component
{ {
/** /**
* @var string|array the columns being selected. This refers to the SELECT clause in a SQL * Sort ascending
* statement. It can be either a string (e.g. `'id, name'`) or an array (e.g. `array('id', 'name')`). * @see orderBy
* If not set, if means all columns. */
const SORT_ASC = false;
/**
* Sort ascending
* @see orderBy
*/
const SORT_DESC = true;
/**
* @var array the columns being selected. For example, `array('id', 'name')`.
* This is used to construct the SELECT clause in a SQL statement. If not set, if means selecting all columns.
* @see select() * @see select()
*/ */
public $select; public $select;
@ -54,8 +62,8 @@ class Query extends \yii\base\Component
*/ */
public $distinct; public $distinct;
/** /**
* @var string|array the table(s) to be selected from. This refers to the FROM clause in a SQL statement. * @var array the table(s) to be selected from. For example, `array('tbl_user', 'tbl_post')`.
* It can be either a string (e.g. `'tbl_user, tbl_post'`) or an array (e.g. `array('tbl_user', 'tbl_post')`). * This is used to construct the FROM clause in a SQL statement.
* @see from() * @see from()
*/ */
public $from; public $from;
@ -75,20 +83,33 @@ class Query extends \yii\base\Component
*/ */
public $offset; public $offset;
/** /**
* @var string|array how to sort the query results. This refers to the ORDER BY clause in a SQL statement. * @var array how to sort the query results. This is used to construct the ORDER BY clause in a SQL statement.
* It can be either a string (e.g. `'id ASC, name DESC'`) or an array (e.g. `array('id ASC', 'name DESC')`). * The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which
* can be either [[Query::SORT_ASC]] or [[Query::SORT_DESC]]. The array may also contain [[Expression]] objects.
* If that is the case, the expressions will be converted into strings without any change.
*/ */
public $orderBy; public $orderBy;
/** /**
* @var string|array how to group the query results. This refers to the GROUP BY clause in a SQL statement. * @var array how to group the query results. For example, `array('company', 'department')`.
* It can be either a string (e.g. `'company, department'`) or an array (e.g. `array('company', 'department')`). * This is used to construct the GROUP BY clause in a SQL statement.
*/ */
public $groupBy; public $groupBy;
/** /**
* @var string|array how to join with other tables. This refers to the JOIN clause in a SQL statement. * @var array how to join with other tables. Each array element represents the specification
* It can be either a string (e.g. `'LEFT JOIN tbl_user ON tbl_user.id=author_id'`) or an array (e.g. * of one join which has the following structure:
* `array('LEFT JOIN tbl_user ON tbl_user.id=author_id', 'LEFT JOIN tbl_team ON tbl_team.id=team_id')`). *
* @see join() * ~~~
* array($joinType, $tableName, $joinCondition)
* ~~~
*
* For example,
*
* ~~~
* array(
* array('INNER JOIN', 'tbl_user', 'tbl_user.id = author_id'),
* array('LEFT JOIN', 'tbl_team', 'tbl_team.id = team_id'),
* )
* ~~~
*/ */
public $join; public $join;
/** /**
@ -97,9 +118,8 @@ class Query extends \yii\base\Component
*/ */
public $having; public $having;
/** /**
* @var string|Query[] the UNION clause(s) in a SQL statement. This can be either a string * @var array this is used to construct the UNION clause(s) in a SQL statement.
* representing a single UNION clause or an array representing multiple UNION clauses. * Each array element can be either a string or a [[Query]] object representing a sub-query.
* Each union clause can be a string or a `Query` object which refers to the SQL statement.
*/ */
public $union; public $union;
/** /**
@ -136,6 +156,9 @@ class Query extends \yii\base\Component
*/ */
public function select($columns, $option = null) public function select($columns, $option = null)
{ {
if (!is_array($columns)) {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
}
$this->select = $columns; $this->select = $columns;
$this->selectOption = $option; $this->selectOption = $option;
return $this; return $this;
@ -163,6 +186,9 @@ class Query extends \yii\base\Component
*/ */
public function from($tables) public function from($tables)
{ {
if (!is_array($tables)) {
$tables = preg_split('/\s*,\s*/', trim($tables), -1, PREG_SPLIT_NO_EMPTY);
}
$this->from = $tables; $this->from = $tables;
return $this; return $this;
} }
@ -362,10 +388,13 @@ class Query extends \yii\base\Component
* The method will automatically quote the column names unless a column contains some parenthesis * The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression). * (which means the column contains a DB expression).
* @return Query the query object itself * @return Query the query object itself
* @see addGroup() * @see addGroupBy()
*/ */
public function groupBy($columns) public function groupBy($columns)
{ {
if (!is_array($columns)) {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
}
$this->groupBy = $columns; $this->groupBy = $columns;
return $this; return $this;
} }
@ -377,19 +406,16 @@ class Query extends \yii\base\Component
* The method will automatically quote the column names unless a column contains some parenthesis * The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression). * (which means the column contains a DB expression).
* @return Query the query object itself * @return Query the query object itself
* @see group() * @see groupBy()
*/ */
public function addGroup($columns) public function addGroupBy($columns)
{ {
if (empty($this->groupBy)) {
$this->groupBy = $columns;
} else {
if (!is_array($this->groupBy)) {
$this->groupBy = preg_split('/\s*,\s*/', trim($this->groupBy), -1, PREG_SPLIT_NO_EMPTY);
}
if (!is_array($columns)) { if (!is_array($columns)) {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY); $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
} }
if ($this->groupBy === null) {
$this->groupBy = $columns;
} else {
$this->groupBy = array_merge($this->groupBy, $columns); $this->groupBy = array_merge($this->groupBy, $columns);
} }
return $this; return $this;
@ -456,41 +482,56 @@ class Query extends \yii\base\Component
/** /**
* Sets the ORDER BY part of the query. * Sets the ORDER BY part of the query.
* @param string|array $columns the columns (and the directions) to be ordered by. * @param string|array $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array (e.g. array('id ASC', 'name DESC')). * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* (e.g. `array('id' => Query::SORT_ASC ASC, 'name' => Query::SORT_DESC)`).
* The method will automatically quote the column names unless a column contains some parenthesis * The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression). * (which means the column contains a DB expression).
* @return Query the query object itself * @return Query the query object itself
* @see addOrder() * @see addOrderBy()
*/ */
public function orderBy($columns) public function orderBy($columns)
{ {
$this->orderBy = $columns; $this->orderBy = $this->normalizeOrderBy($columns);
return $this; return $this;
} }
/** /**
* Adds additional ORDER BY columns to the query. * Adds additional ORDER BY columns to the query.
* @param string|array $columns the columns (and the directions) to be ordered by. * @param string|array $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array (e.g. array('id ASC', 'name DESC')). * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* (e.g. `array('id' => Query::SORT_ASC ASC, 'name' => Query::SORT_DESC)`).
* The method will automatically quote the column names unless a column contains some parenthesis * The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression). * (which means the column contains a DB expression).
* @return Query the query object itself * @return Query the query object itself
* @see order() * @see orderBy()
*/ */
public function addOrderBy($columns) public function addOrderBy($columns)
{ {
if (empty($this->orderBy)) { $columns = $this->normalizeOrderBy($columns);
if ($this->orderBy === null) {
$this->orderBy = $columns; $this->orderBy = $columns;
} else { } else {
if (!is_array($this->orderBy)) { $this->orderBy = array_merge($this->orderBy, $columns);
$this->orderBy = preg_split('/\s*,\s*/', trim($this->orderBy), -1, PREG_SPLIT_NO_EMPTY);
} }
if (!is_array($columns)) { return $this;
}
protected function normalizeOrderBy($columns)
{
if (is_array($columns)) {
return $columns;
} else {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY); $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
$result = array();
foreach ($columns as $column) {
if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) {
$result[$matches[1]] = strcasecmp($matches[2], 'desc') ? self::SORT_ASC : self::SORT_DESC;
} else {
$result[$column] = self::SORT_ASC;
} }
$this->orderBy = array_merge($this->orderBy, $columns);
} }
return $this; return $result;
}
} }
/** /**

90
framework/db/QueryBuilder.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* QueryBuilder class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -62,10 +60,10 @@ class QueryBuilder extends \yii\base\Object
$this->buildFrom($query->from), $this->buildFrom($query->from),
$this->buildJoin($query->join), $this->buildJoin($query->join),
$this->buildWhere($query->where), $this->buildWhere($query->where),
$this->buildGroup($query->groupBy), $this->buildGroupBy($query->groupBy),
$this->buildHaving($query->having), $this->buildHaving($query->having),
$this->buildUnion($query->union), $this->buildUnion($query->union),
$this->buildOrder($query->orderBy), $this->buildOrderBy($query->orderBy),
$this->buildLimit($query->limit, $query->offset), $this->buildLimit($query->limit, $query->offset),
); );
return implode($this->separator, array_filter($clauses)); return implode($this->separator, array_filter($clauses));
@ -592,11 +590,11 @@ class QueryBuilder extends \yii\base\Object
return $operator === 'IN' ? '0=1' : ''; return $operator === 'IN' ? '0=1' : '';
} }
if (is_array($column)) {
if (count($column) > 1) { if (count($column) > 1) {
return $this->buildCompositeInCondition($operator, $column, $values); return $this->buildCompositeInCondition($operator, $column, $values);
} else { } elseif (is_array($column)) {
$column = reset($column); $column = reset($column);
}
foreach ($values as $i => $value) { foreach ($values as $i => $value) {
if (is_array($value)) { if (is_array($value)) {
$value = isset($value[$column]) ? $value[$column] : null; $value = isset($value[$column]) ? $value[$column] : null;
@ -607,8 +605,6 @@ class QueryBuilder extends \yii\base\Object
$values[$i] = is_string($value) ? $this->db->quoteValue($value) : (string)$value; $values[$i] = is_string($value) ? $this->db->quoteValue($value) : (string)$value;
} }
} }
}
}
if (strpos($column, '(') === false) { if (strpos($column, '(') === false) {
$column = $this->db->quoteColumnName($column); $column = $this->db->quoteColumnName($column);
} }
@ -677,7 +673,7 @@ class QueryBuilder extends \yii\base\Object
} }
/** /**
* @param string|array $columns * @param array $columns
* @param boolean $distinct * @param boolean $distinct
* @param string $selectOption * @param string $selectOption
* @return string the SELECT clause built from [[query]]. * @return string the SELECT clause built from [[query]].
@ -693,13 +689,6 @@ class QueryBuilder extends \yii\base\Object
return $select . ' *'; return $select . ' *';
} }
if (!is_array($columns)) {
if (strpos($columns, '(') !== false) {
return $select . ' ' . $columns;
} else {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
}
}
foreach ($columns as $i => $column) { foreach ($columns as $i => $column) {
if (is_object($column)) { if (is_object($column)) {
$columns[$i] = (string)$column; $columns[$i] = (string)$column;
@ -720,7 +709,7 @@ class QueryBuilder extends \yii\base\Object
} }
/** /**
* @param string|array $tables * @param array $tables
* @return string the FROM clause built from [[query]]. * @return string the FROM clause built from [[query]].
*/ */
public function buildFrom($tables) public function buildFrom($tables)
@ -729,13 +718,6 @@ class QueryBuilder extends \yii\base\Object
return ''; return '';
} }
if (!is_array($tables)) {
if (strpos($tables, '(') !== false) {
return 'FROM ' . $tables;
} else {
$tables = preg_split('/\s*,\s*/', trim($tables), -1, PREG_SPLIT_NO_EMPTY);
}
}
foreach ($tables as $i => $table) { foreach ($tables as $i => $table) {
if (strpos($table, '(') === false) { if (strpos($table, '(') === false) {
if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/i', $table, $matches)) { // with alias if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/i', $table, $matches)) { // with alias
@ -756,19 +738,19 @@ class QueryBuilder extends \yii\base\Object
/** /**
* @param string|array $joins * @param string|array $joins
* @return string the JOIN clause built from [[query]]. * @return string the JOIN clause built from [[query]].
* @throws Exception if the $joins parameter is not in proper format
*/ */
public function buildJoin($joins) public function buildJoin($joins)
{ {
if (empty($joins)) { if (empty($joins)) {
return ''; return '';
} }
if (is_string($joins)) {
return $joins;
}
foreach ($joins as $i => $join) { foreach ($joins as $i => $join) {
if (is_array($join)) { // 0:join type, 1:table name, 2:on-condition if (is_object($join)) {
if (isset($join[0], $join[1])) { $joins[$i] = (string)$join;
} elseif (is_array($join) && isset($join[0], $join[1])) {
// 0:join type, 1:table name, 2:on-condition
$table = $join[1]; $table = $join[1];
if (strpos($table, '(') === false) { if (strpos($table, '(') === false) {
if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $table, $matches)) { // with alias if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $table, $matches)) { // with alias
@ -785,8 +767,7 @@ class QueryBuilder extends \yii\base\Object
} }
} }
} else { } else {
throw new Exception('A join clause must be specified as an array of at least two elements.'); throw new Exception('A join clause must be specified as an array of join type, join table, and optionally join condition.');
}
} }
} }
@ -804,16 +785,12 @@ class QueryBuilder extends \yii\base\Object
} }
/** /**
* @param string|array $columns * @param array $columns
* @return string the GROUP BY clause * @return string the GROUP BY clause
*/ */
public function buildGroup($columns) public function buildGroupBy($columns)
{ {
if (empty($columns)) { return empty($columns) ? '' : 'GROUP BY ' . $this->buildColumns($columns);
return '';
} else {
return 'GROUP BY ' . $this->buildColumns($columns);
}
} }
/** /**
@ -827,36 +804,24 @@ class QueryBuilder extends \yii\base\Object
} }
/** /**
* @param string|array $columns * @param array $columns
* @return string the ORDER BY clause built from [[query]]. * @return string the ORDER BY clause built from [[query]].
*/ */
public function buildOrder($columns) public function buildOrderBy($columns)
{ {
if (empty($columns)) { if (empty($columns)) {
return ''; return '';
} }
if (!is_array($columns)) { $orders = array();
if (strpos($columns, '(') !== false) { foreach ($columns as $name => $direction) {
return 'ORDER BY ' . $columns; if (is_object($direction)) {
} else { $orders[] = (string)$direction;
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
}
}
foreach ($columns as $i => $column) {
if (is_object($column)) {
$columns[$i] = (string)$column;
} elseif (strpos($column, '(') === false) {
if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) {
$columns[$i] = $this->db->quoteColumnName($matches[1]) . ' ' . $matches[2];
} else { } else {
$columns[$i] = $this->db->quoteColumnName($column); $orders[] = $this->db->quoteColumnName($name) . ($direction === Query::SORT_DESC ? ' DESC' : '');
} }
} }
}
if (is_array($columns)) { return 'ORDER BY ' . implode(', ', $orders);
$columns = implode(', ', $columns);
}
return 'ORDER BY ' . $columns;
} }
/** /**
@ -877,7 +842,7 @@ class QueryBuilder extends \yii\base\Object
} }
/** /**
* @param string|array $unions * @param array $unions
* @return string the UNION clause built from [[query]]. * @return string the UNION clause built from [[query]].
*/ */
public function buildUnion($unions) public function buildUnion($unions)
@ -885,9 +850,6 @@ class QueryBuilder extends \yii\base\Object
if (empty($unions)) { if (empty($unions)) {
return ''; return '';
} }
if (!is_array($unions)) {
$unions = array($unions);
}
foreach ($unions as $i => $union) { foreach ($unions as $i => $union) {
if ($union instanceof Query) { if ($union instanceof Query) {
$unions[$i] = $this->build($union); $unions[$i] = $this->build($union);

11
framework/db/Schema.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Driver class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
@ -111,7 +109,12 @@ abstract class Schema extends \yii\base\Object
*/ */
public function getCacheKey($cache, $name) public function getCacheKey($cache, $name)
{ {
return $cache->buildKey(__CLASS__, $this->db->dsn, $this->db->username, $name); return $cache->buildKey(array(
__CLASS__,
$this->db->dsn,
$this->db->username,
$name,
));
} }
/** /**

8
framework/db/TableSchema.php

@ -1,7 +1,5 @@
<?php <?php
/** /**
* TableSchema class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2011 Yii Software LLC * @copyright Copyright &copy; 2008-2011 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
@ -9,7 +7,7 @@
namespace yii\db; namespace yii\db;
use yii\base\InvalidCallException; use yii\base\InvalidParamException;
/** /**
* TableSchema represents the metadata of a database table. * TableSchema represents the metadata of a database table.
@ -83,7 +81,7 @@ class TableSchema extends \yii\base\Object
/** /**
* Manually specifies the primary key for this table. * Manually specifies the primary key for this table.
* @param string|array $keys the primary key (can be composite) * @param string|array $keys the primary key (can be composite)
* @throws InvalidCallException if the specified key cannot be found in the table. * @throws InvalidParamException if the specified key cannot be found in the table.
*/ */
public function fixPrimaryKey($keys) public function fixPrimaryKey($keys)
{ {
@ -98,7 +96,7 @@ class TableSchema extends \yii\base\Object
if (isset($this->columns[$key])) { if (isset($this->columns[$key])) {
$this->columns[$key]->isPrimaryKey = true; $this->columns[$key]->isPrimaryKey = true;
} else { } else {
throw new InvalidCallException("Primary key '$key' cannot be found in table '{$this->name}'."); throw new InvalidParamException("Primary key '$key' cannot be found in table '{$this->name}'.");
} }
} }
} }

4
framework/db/Transaction.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Transaction class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

14
framework/db/mysql/QueryBuilder.php

@ -1,16 +1,14 @@
<?php <?php
/** /**
* QueryBuilder class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\db\mysql; namespace yii\db\mysql;
use yii\db\Exception; use yii\db\Exception;
use yii\base\InvalidCallException; use yii\base\InvalidParamException;
/** /**
* QueryBuilder is the query builder for MySQL databases. * QueryBuilder is the query builder for MySQL databases.
@ -54,7 +52,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
$quotedTable = $this->db->quoteTableName($table); $quotedTable = $this->db->quoteTableName($table);
$row = $this->db->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryRow(); $row = $this->db->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryRow();
if ($row === false) { if ($row === false) {
throw new Exception("Unable to find '$oldName' in table '$table'."); throw new Exception("Unable to find column '$oldName' in table '$table'.");
} }
if (isset($row['Create Table'])) { if (isset($row['Create Table'])) {
$sql = $row['Create Table']; $sql = $row['Create Table'];
@ -98,7 +96,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
* @param mixed $value the value for the primary key of the next new row inserted. If this is not set, * @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
* the next new row's primary key will have a value 1. * the next new row's primary key will have a value 1.
* @return string the SQL statement for resetting sequence * @return string the SQL statement for resetting sequence
* @throws InvalidCallException if the table does not exist or there is no sequence associated with the table. * @throws InvalidParamException if the table does not exist or there is no sequence associated with the table.
*/ */
public function resetSequence($tableName, $value = null) public function resetSequence($tableName, $value = null)
{ {
@ -113,9 +111,9 @@ class QueryBuilder extends \yii\db\QueryBuilder
} }
return "ALTER TABLE $tableName AUTO_INCREMENT=$value"; return "ALTER TABLE $tableName AUTO_INCREMENT=$value";
} elseif ($table === null) { } elseif ($table === null) {
throw new InvalidCallException("Table not found: $tableName"); throw new InvalidParamException("Table not found: $tableName");
} else { } else {
throw new InvalidCallException("There is not sequence associated with table '$tableName'.'"); throw new InvalidParamException("There is not sequence associated with table '$tableName'.'");
} }
} }

4
framework/db/mysql/Schema.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Schema class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

12
framework/db/sqlite/QueryBuilder.php

@ -1,17 +1,15 @@
<?php <?php
/** /**
* QueryBuilder class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\db\sqlite; namespace yii\db\sqlite;
use yii\db\Exception; use yii\db\Exception;
use yii\base\InvalidParamException;
use yii\base\NotSupportedException; use yii\base\NotSupportedException;
use yii\base\InvalidCallException;
/** /**
* QueryBuilder is the query builder for SQLite databases. * QueryBuilder is the query builder for SQLite databases.
@ -50,7 +48,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
* @param mixed $value the value for the primary key of the next new row inserted. If this is not set, * @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
* the next new row's primary key will have a value 1. * the next new row's primary key will have a value 1.
* @return string the SQL statement for resetting sequence * @return string the SQL statement for resetting sequence
* @throws InvalidCallException if the table does not exist or there is no sequence associated with the table. * @throws InvalidParamException if the table does not exist or there is no sequence associated with the table.
*/ */
public function resetSequence($tableName, $value = null) public function resetSequence($tableName, $value = null)
{ {
@ -70,9 +68,9 @@ class QueryBuilder extends \yii\db\QueryBuilder
} catch (Exception $e) { } catch (Exception $e) {
} }
} elseif ($table === null) { } elseif ($table === null) {
throw new InvalidCallException("Table not found: $tableName"); throw new InvalidParamException("Table not found: $tableName");
} else { } else {
throw new InvalidCallException("There is not sequence associated with table '$tableName'.'"); throw new InvalidParamException("There is not sequence associated with table '$tableName'.'");
} }
} }

4
framework/db/sqlite/Schema.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* Schema class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

78
framework/util/ArrayHelper.php → framework/helpers/ArrayHelper.php

@ -1,15 +1,14 @@
<?php <?php
/** /**
* ArrayHelper class file. * @copyright Copyright (c) 2008 Yii Software LLC
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\util; namespace yii\helpers;
use yii\base\InvalidCallException; use Yii;
use yii\base\InvalidParamException;
/** /**
* ArrayHelper provides additional array functionality you can use in your * ArrayHelper provides additional array functionality you can use in your
@ -60,11 +59,11 @@ class ArrayHelper
* *
* ~~~ * ~~~
* // working with array * // working with array
* $username = \yii\util\ArrayHelper::getValue($_POST, 'username'); * $username = \yii\helpers\ArrayHelper::getValue($_POST, 'username');
* // working with object * // working with object
* $username = \yii\util\ArrayHelper::getValue($user, 'username'); * $username = \yii\helpers\ArrayHelper::getValue($user, 'username');
* // working with anonymous function * // working with anonymous function
* $fullName = \yii\util\ArrayHelper::getValue($user, function($user, $defaultValue) { * $fullName = \yii\helpers\ArrayHelper::getValue($user, function($user, $defaultValue) {
* return $user->firstName . ' ' . $user->lastName; * return $user->firstName . ' ' . $user->lastName;
* }); * });
* ~~~ * ~~~
@ -242,7 +241,7 @@ class ArrayHelper
* value is for sorting strings in case-insensitive manner. Please refer to * value is for sorting strings in case-insensitive manner. Please refer to
* See [PHP manual](http://php.net/manual/en/function.sort.php) for more details. * See [PHP manual](http://php.net/manual/en/function.sort.php) for more details.
* When sorting by multiple keys with different sort flags, use an array of sort flags. * When sorting by multiple keys with different sort flags, use an array of sort flags.
* @throws InvalidCallException if the $ascending or $sortFlag parameters do not have * @throws InvalidParamException if the $ascending or $sortFlag parameters do not have
* correct number of elements as that of $key. * correct number of elements as that of $key.
*/ */
public static function multisort(&$array, $key, $ascending = true, $sortFlag = SORT_REGULAR) public static function multisort(&$array, $key, $ascending = true, $sortFlag = SORT_REGULAR)
@ -255,12 +254,12 @@ class ArrayHelper
if (is_scalar($ascending)) { if (is_scalar($ascending)) {
$ascending = array_fill(0, $n, $ascending); $ascending = array_fill(0, $n, $ascending);
} elseif (count($ascending) !== $n) { } elseif (count($ascending) !== $n) {
throw new InvalidCallException('The length of $ascending parameter must be the same as that of $keys.'); throw new InvalidParamException('The length of $ascending parameter must be the same as that of $keys.');
} }
if (is_scalar($sortFlag)) { if (is_scalar($sortFlag)) {
$sortFlag = array_fill(0, $n, $sortFlag); $sortFlag = array_fill(0, $n, $sortFlag);
} elseif (count($sortFlag) !== $n) { } elseif (count($sortFlag) !== $n) {
throw new InvalidCallException('The length of $ascending parameter must be the same as that of $keys.'); throw new InvalidParamException('The length of $ascending parameter must be the same as that of $keys.');
} }
$args = array(); $args = array();
foreach ($keys as $i => $key) { foreach ($keys as $i => $key) {
@ -281,4 +280,61 @@ class ArrayHelper
$args[] = &$array; $args[] = &$array;
call_user_func_array('array_multisort', $args); call_user_func_array('array_multisort', $args);
} }
/**
* Encodes special characters in an array of strings into HTML entities.
* Both the array keys and values will be encoded.
* If a value is an array, this method will also encode it recursively.
* @param array $data data to be encoded
* @param boolean $valuesOnly whether to encode array values only. If false,
* both the array keys and array values will be encoded.
* @param string $charset the charset that the data is using. If not set,
* [[\yii\base\Application::charset]] will be used.
* @return array the encoded data
* @see http://www.php.net/manual/en/function.htmlspecialchars.php
*/
public static function htmlEncode($data, $valuesOnly = true, $charset = null)
{
if ($charset === null) {
$charset = Yii::$app->charset;
}
$d = array();
foreach ($data as $key => $value) {
if (!$valuesOnly && is_string($key)) {
$key = htmlspecialchars($key, ENT_QUOTES, $charset);
}
if (is_string($value)) {
$d[$key] = htmlspecialchars($value, ENT_QUOTES, $charset);
} elseif (is_array($value)) {
$d[$key] = static::htmlEncode($value, $charset);
}
}
return $d;
}
/**
* Decodes HTML entities into the corresponding characters in an array of strings.
* Both the array keys and values will be decoded.
* If a value is an array, this method will also decode it recursively.
* @param array $data data to be decoded
* @param boolean $valuesOnly whether to decode array values only. If false,
* both the array keys and array values will be decoded.
* @return array the decoded data
* @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php
*/
public static function htmlDecode($data, $valuesOnly = true)
{
$d = array();
foreach ($data as $key => $value) {
if (!$valuesOnly && is_string($key)) {
$key = htmlspecialchars_decode($key, ENT_QUOTES);
}
if (is_string($value)) {
$d[$key] = htmlspecialchars_decode($value, ENT_QUOTES);
} elseif (is_array($value)) {
$d[$key] = static::htmlDecode($value);
}
}
return $d;
}
} }

16
framework/util/ConsoleColor.php → framework/helpers/ConsoleColor.php

@ -1,23 +1,13 @@
<?php <?php
/** /**
* ConsoleColor class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\console; namespace yii\helpers;
// todo define how subclassing will work
// todo add a run() or render() method
// todo test this on all kinds of terminals, especially windows (check out lib ncurses) // todo test this on all kinds of terminals, especially windows (check out lib ncurses)
// todo not sure if all methods should be static
// todo subclass DetailView
// todo subclass GridView
// todo more subclasses
/** /**
* Console View is the base class for console view components * Console View is the base class for console view components
@ -359,7 +349,7 @@ class ConsoleColor
} }
$styleString[] = array(); $styleString[] = array();
foreach($styleA as $name => $content) { foreach($styleA as $name => $content) {
if ($name = 'text-decoration') { if ($name === 'text-decoration') {
$content = implode(' ', $content); $content = implode(' ', $content);
} }
$styleString[] = $name.':'.$content; $styleString[] = $name.':'.$content;

6
framework/util/FileHelper.php → framework/helpers/FileHelper.php

@ -3,11 +3,11 @@
* Filesystem helper class file. * Filesystem helper class file.
* *
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\util; namespace yii\helpers;
use yii\base\Exception; use yii\base\Exception;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
@ -43,7 +43,7 @@ class FileHelper
public static function ensureDirectory($path) public static function ensureDirectory($path)
{ {
$p = \Yii::getAlias($path); $p = \Yii::getAlias($path);
if ($p !== false && ($p = realpath($p)) !== false && is_dir($p)) { if (($p = realpath($p)) !== false && is_dir($p)) {
return $p; return $p;
} else { } else {
throw new InvalidConfigException('Directory does not exist: ' . $path); throw new InvalidConfigException('Directory does not exist: ' . $path);

976
framework/helpers/Html.php

@ -0,0 +1,976 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\helpers;
use Yii;
use yii\base\InvalidParamException;
/**
* Html provides a set of static methods for generating commonly used HTML tags.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Html
{
/**
* @var boolean whether to close void (empty) elements. Defaults to true.
* @see voidElements
*/
public static $closeVoidElements = true;
/**
* @var array list of void elements (element name => 1)
* @see closeVoidElements
* @see http://www.w3.org/TR/html-markup/syntax.html#void-element
*/
public static $voidElements = array(
'area' => 1,
'base' => 1,
'br' => 1,
'col' => 1,
'command' => 1,
'embed' => 1,
'hr' => 1,
'img' => 1,
'input' => 1,
'keygen' => 1,
'link' => 1,
'meta' => 1,
'param' => 1,
'source' => 1,
'track' => 1,
'wbr' => 1,
);
/**
* @var boolean whether to show the values of boolean attributes in element tags.
* If false, only the attribute names will be generated.
* @see booleanAttributes
*/
public static $showBooleanAttributeValues = true;
/**
* @var array list of boolean attributes. The presence of a boolean attribute on
* an element represents the true value, and the absence of the attribute represents the false value.
* @see showBooleanAttributeValues
* @see http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes
*/
public static $booleanAttributes = array(
'async' => 1,
'autofocus' => 1,
'autoplay' => 1,
'checked' => 1,
'controls' => 1,
'declare' => 1,
'default' => 1,
'defer' => 1,
'disabled' => 1,
'formnovalidate' => 1,
'hidden' => 1,
'ismap' => 1,
'loop' => 1,
'multiple' => 1,
'muted' => 1,
'nohref' => 1,
'noresize' => 1,
'novalidate' => 1,
'open' => 1,
'readonly' => 1,
'required' => 1,
'reversed' => 1,
'scoped' => 1,
'seamless' => 1,
'selected' => 1,
'typemustmatch' => 1,
);
/**
* @var array the preferred order of attributes in a tag. This mainly affects the order of the attributes
* that are rendered by [[renderAttributes()]].
*/
public static $attributeOrder = array(
'type',
'id',
'class',
'name',
'value',
'href',
'src',
'action',
'method',
'selected',
'checked',
'readonly',
'disabled',
'multiple',
'size',
'maxlength',
'width',
'height',
'rows',
'cols',
'alt',
'title',
'rel',
'media',
);
/**
* Encodes special characters into HTML entities.
* The [[yii\base\Application::charset|application charset]] will be used for encoding.
* @param string $content the content to be encoded
* @return string the encoded content
* @see decode
* @see http://www.php.net/manual/en/function.htmlspecialchars.php
*/
public static function encode($content)
{
return htmlspecialchars($content, ENT_QUOTES, Yii::$app->charset);
}
/**
* Decodes special HTML entities back to the corresponding characters.
* This is the opposite of [[encode()]].
* @param string $content the content to be decoded
* @return string the decoded content
* @see encode
* @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php
*/
public static function decode($content)
{
return htmlspecialchars_decode($content, ENT_QUOTES);
}
/**
* Generates a complete HTML tag.
* @param string $name the tag name
* @param string $content the content to be enclosed between the start and end tags. It will not be HTML-encoded.
* If this is coming from end users, you should consider [[encode()]] it to prevent XSS attacks.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated HTML tag
* @see beginTag
* @see endTag
*/
public static function tag($name, $content = '', $options = array())
{
$html = '<' . $name . static::renderTagAttributes($options);
if (isset(static::$voidElements[strtolower($name)])) {
return $html . (static::$closeVoidElements ? ' />' : '>');
} else {
return $html . ">$content</$name>";
}
}
/**
* Generates a start tag.
* @param string $name the tag name
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated start tag
* @see endTag
* @see tag
*/
public static function beginTag($name, $options = array())
{
return '<' . $name . static::renderTagAttributes($options) . '>';
}
/**
* Generates an end tag.
* @param string $name the tag name
* @return string the generated end tag
* @see beginTag
* @see tag
*/
public static function endTag($name)
{
return "</$name>";
}
/**
* Encloses the given content within a CDATA tag.
* @param string $content the content to be enclosed within the CDATA tag
* @return string the CDATA tag with the enclosed content.
*/
public static function cdata($content)
{
return '<![CDATA[' . $content . ']]>';
}
/**
* Generates a style tag.
* @param string $content the style content
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* If the options does not contain "type", a "type" attribute with value "text/css" will be used.
* @return string the generated style tag
*/
public static function style($content, $options = array())
{
if (!isset($options['type'])) {
$options['type'] = 'text/css';
}
return static::tag('style', "/*<![CDATA[*/\n{$content}\n/*]]>*/", $options);
}
/**
* Generates a script tag.
* @param string $content the script content
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* If the options does not contain "type", a "type" attribute with value "text/javascript" will be rendered.
* @return string the generated script tag
*/
public static function script($content, $options = array())
{
if (!isset($options['type'])) {
$options['type'] = 'text/javascript';
}
return static::tag('script', "/*<![CDATA[*/\n{$content}\n/*]]>*/", $options);
}
/**
* Generates a link tag that refers to an external CSS file.
* @param array|string $url the URL of the external CSS file. This parameter will be processed by [[url()]].
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated link tag
* @see url
*/
public static function cssFile($url, $options = array())
{
$options['rel'] = 'stylesheet';
$options['type'] = 'text/css';
$options['href'] = static::url($url);
return static::tag('link', '', $options);
}
/**
* Generates a script tag that refers to an external JavaScript file.
* @param string $url the URL of the external JavaScript file. This parameter will be processed by [[url()]].
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated script tag
* @see url
*/
public static function jsFile($url, $options = array())
{
$options['type'] = 'text/javascript';
$options['src'] = static::url($url);
return static::tag('script', '', $options);
}
/**
* Generates a form start tag.
* @param array|string $action the form action URL. This parameter will be processed by [[url()]].
* @param string $method the form submission method, either "post" or "get" (case-insensitive)
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated form start tag.
* @see endForm
*/
public static function beginForm($action = '', $method = 'post', $options = array())
{
$action = static::url($action);
// query parameters in the action are ignored for GET method
// we use hidden fields to add them back
$hiddens = array();
if (!strcasecmp($method, 'get') && ($pos = strpos($action, '?')) !== false) {
foreach (explode('&', substr($action, $pos + 1)) as $pair) {
if (($pos1 = strpos($pair, '=')) !== false) {
$hiddens[] = static::hiddenInput(urldecode(substr($pair, 0, $pos1)), urldecode(substr($pair, $pos1 + 1)));
} else {
$hiddens[] = static::hiddenInput(urldecode($pair), '');
}
}
$action = substr($action, 0, $pos);
}
$options['action'] = $action;
$options['method'] = $method;
$form = static::beginTag('form', $options);
if ($hiddens !== array()) {
$form .= "\n" . implode("\n", $hiddens);
}
return $form;
}
/**
* Generates a form end tag.
* @return string the generated tag
* @see beginForm
*/
public static function endForm()
{
return '</form>';
}
/**
* Generates a hyperlink tag.
* @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code
* such as an image tag. If this is is coming from end users, you should consider [[encode()]]
* it to prevent XSS attacks.
* @param array|string|null $url the URL for the hyperlink tag. This parameter will be processed by [[url()]]
* and will be used for the "href" attribute of the tag. If this parameter is null, the "href" attribute
* will not be generated.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated hyperlink
* @see url
*/
public static function a($text, $url = null, $options = array())
{
if ($url !== null) {
$options['href'] = static::url($url);
}
return static::tag('a', $text, $options);
}
/**
* Generates a mailto hyperlink.
* @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code
* such as an image tag. If this is is coming from end users, you should consider [[encode()]]
* it to prevent XSS attacks.
* @param string $email email address. If this is null, the first parameter (link body) will be treated
* as the email address and used.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated mailto link
*/
public static function mailto($text, $email = null, $options = array())
{
return static::a($text, 'mailto:' . ($email === null ? $text : $email), $options);
}
/**
* Generates an image tag.
* @param string $src the image URL. This parameter will be processed by [[url()]].
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated image tag
*/
public static function img($src, $options = array())
{
$options['src'] = static::url($src);
if (!isset($options['alt'])) {
$options['alt'] = '';
}
return static::tag('img', null, $options);
}
/**
* Generates a label tag.
* @param string $content label text. It will NOT be HTML-encoded. Therefore you can pass in HTML code
* such as an image tag. If this is is coming from end users, you should consider [[encode()]]
* it to prevent XSS attacks.
* @param string $for the ID of the HTML element that this label is associated with.
* If this is null, the "for" attribute will not be generated.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated label tag
*/
public static function label($content, $for = null, $options = array())
{
$options['for'] = $for;
return static::tag('label', $content, $options);
}
/**
* Generates a button tag.
* @param string $name the name attribute. If it is null, the name attribute will not be generated.
* @param string $value the value attribute. If it is null, the value attribute will not be generated.
* @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
* Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
* you should consider [[encode()]] it to prevent XSS attacks.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* If the options does not contain "type", a "type" attribute with value "button" will be rendered.
* @return string the generated button tag
*/
public static function button($name = null, $value = null, $content = 'Button', $options = array())
{
$options['name'] = $name;
$options['value'] = $value;
if (!isset($options['type'])) {
$options['type'] = 'button';
}
return static::tag('button', $content, $options);
}
/**
* Generates a submit button tag.
* @param string $name the name attribute. If it is null, the name attribute will not be generated.
* @param string $value the value attribute. If it is null, the value attribute will not be generated.
* @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
* Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
* you should consider [[encode()]] it to prevent XSS attacks.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated submit button tag
*/
public static function submitButton($name = null, $value = null, $content = 'Submit', $options = array())
{
$options['type'] = 'submit';
return static::button($name, $value, $content, $options);
}
/**
* Generates a reset button tag.
* @param string $name the name attribute. If it is null, the name attribute will not be generated.
* @param string $value the value attribute. If it is null, the value attribute will not be generated.
* @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
* Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
* you should consider [[encode()]] it to prevent XSS attacks.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated reset button tag
*/
public static function resetButton($name = null, $value = null, $content = 'Reset', $options = array())
{
$options['type'] = 'reset';
return static::button($name, $value, $content, $options);
}
/**
* Generates an input type of the given type.
* @param string $type the type attribute.
* @param string $name the name attribute. If it is null, the name attribute will not be generated.
* @param string $value the value attribute. If it is null, the value attribute will not be generated.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated input tag
*/
public static function input($type, $name = null, $value = null, $options = array())
{
$options['type'] = $type;
$options['name'] = $name;
$options['value'] = $value;
return static::tag('input', null, $options);
}
/**
* Generates an input button.
* @param string $name the name attribute.
* @param string $value the value attribute. If it is null, the value attribute will not be generated.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated button tag
*/
public static function buttonInput($name, $value = 'Button', $options = array())
{
return static::input('button', $name, $value, $options);
}
/**
* Generates a submit input button.
* @param string $name the name attribute. If it is null, the name attribute will not be generated.
* @param string $value the value attribute. If it is null, the value attribute will not be generated.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated button tag
*/
public static function submitInput($name = null, $value = 'Submit', $options = array())
{
return static::input('submit', $name, $value, $options);
}
/**
* Generates a reset input button.
* @param string $name the name attribute. If it is null, the name attribute will not be generated.
* @param string $value the value attribute. If it is null, the value attribute will not be generated.
* @param array $options the attributes of the button tag. The values will be HTML-encoded using [[encode()]].
* Attributes whose value is null will be ignored and not put in the tag returned.
* @return string the generated button tag
*/
public static function resetInput($name = null, $value = 'Reset', $options = array())
{
return static::input('reset', $name, $value, $options);
}
/**
* Generates a text input field.
* @param string $name the name attribute.
* @param string $value the value attribute. If it is null, the value attribute will not be generated.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated button tag
*/
public static function textInput($name, $value = null, $options = array())
{
return static::input('text', $name, $value, $options);
}
/**
* Generates a hidden input field.
* @param string $name the name attribute.
* @param string $value the value attribute. If it is null, the value attribute will not be generated.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated button tag
*/
public static function hiddenInput($name, $value = null, $options = array())
{
return static::input('hidden', $name, $value, $options);
}
/**
* Generates a password input field.
* @param string $name the name attribute.
* @param string $value the value attribute. If it is null, the value attribute will not be generated.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated button tag
*/
public static function passwordInput($name, $value = null, $options = array())
{
return static::input('password', $name, $value, $options);
}
/**
* Generates a file input field.
* To use a file input field, you should set the enclosing form's "enctype" attribute to
* be "multipart/form-data". After the form is submitted, the uploaded file information
* can be obtained via $_FILES[$name] (see PHP documentation).
* @param string $name the name attribute.
* @param string $value the value attribute. If it is null, the value attribute will not be generated.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated button tag
*/
public static function fileInput($name, $value = null, $options = array())
{
return static::input('file', $name, $value, $options);
}
/**
* Generates a text area input.
* @param string $name the input name
* @param string $value the input value. Note that it will be encoded using [[encode()]].
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
* If a value is null, the corresponding attribute will not be rendered.
* @return string the generated text area tag
*/
public static function textarea($name, $value = '', $options = array())
{
$options['name'] = $name;
return static::tag('textarea', static::encode($value), $options);
}
/**
* Generates a radio button input.
* @param string $name the name attribute.
* @param boolean $checked whether the radio button should be checked.
* @param string $value the value attribute. If it is null, the value attribute will not be rendered.
* @param array $options the tag options in terms of name-value pairs. The following options are supported:
*
* - uncheck: string, the value associated with the uncheck state of the radio button. When this attribute
* is present, a hidden input will be generated so that if the radio button is not checked and is submitted,
* the value of this attribute will still be submitted to the server via the hidden input.
*
* The rest of the options will be rendered as the attributes of the resulting tag. The values will
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
*
* @return string the generated radio button tag
*/
public static function radio($name, $checked = false, $value = '1', $options = array())
{
$options['checked'] = $checked;
$options['value'] = $value;
if (isset($options['uncheck'])) {
// add a hidden field so that if the radio button is not selected, it still submits a value
$hidden = static::hiddenInput($name, $options['uncheck']);
unset($options['uncheck']);
} else {
$hidden = '';
}
return $hidden . static::input('radio', $name, $value, $options);
}
/**
* Generates a checkbox input.
* @param string $name the name attribute.
* @param boolean $checked whether the checkbox should be checked.
* @param string $value the value attribute. If it is null, the value attribute will not be rendered.
* @param array $options the tag options in terms of name-value pairs. The following options are supported:
*
* - uncheck: string, the value associated with the uncheck state of the checkbox. When this attribute
* is present, a hidden input will be generated so that if the checkbox is not checked and is submitted,
* the value of this attribute will still be submitted to the server via the hidden input.
*
* The rest of the options will be rendered as the attributes of the resulting tag. The values will
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
*
* @return string the generated checkbox tag
*/
public static function checkbox($name, $checked = false, $value = '1', $options = array())
{
$options['checked'] = $checked;
$options['value'] = $value;
if (isset($options['uncheck'])) {
// add a hidden field so that if the checkbox is not selected, it still submits a value
$hidden = static::hiddenInput($name, $options['uncheck']);
unset($options['uncheck']);
} else {
$hidden = '';
}
return $hidden . static::input('checkbox', $name, $value, $options);
}
/**
* Generates a drop-down list.
* @param string $name the input name
* @param string $selection the selected value
* @param array $items the option data items. The array keys are option values, and the array values
* are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
* For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
* If you have a list of data models, you may convert them into the format described above using
* [[\yii\helpers\ArrayHelper::map()]].
*
* Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
* the labels will also be HTML-encoded.
* @param array $options the tag options in terms of name-value pairs. The following options are supported:
*
* - prompt: string, a prompt text to be displayed as the first option;
* - options: array, the attributes for the select option tags. The array keys must be valid option values,
* and the array values are the extra attributes for the corresponding option tags. For example,
*
* ~~~
* array(
* 'value1' => array('disabled' => true),
* 'value2' => array('label' => 'value 2'),
* );
* ~~~
*
* - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
* except that the array keys represent the optgroup labels specified in $items.
*
* The rest of the options will be rendered as the attributes of the resulting tag. The values will
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
*
* @return string the generated drop-down list tag
*/
public static function dropDownList($name, $selection = null, $items = array(), $options = array())
{
$options['name'] = $name;
$selectOptions = static::renderSelectOptions($selection, $items, $options);
return static::tag('select', "\n" . $selectOptions . "\n", $options);
}
/**
* Generates a list box.
* @param string $name the input name
* @param string|array $selection the selected value(s)
* @param array $items the option data items. The array keys are option values, and the array values
* are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
* For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
* If you have a list of data models, you may convert them into the format described above using
* [[\yii\helpers\ArrayHelper::map()]].
*
* Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
* the labels will also be HTML-encoded.
* @param array $options the tag options in terms of name-value pairs. The following options are supported:
*
* - prompt: string, a prompt text to be displayed as the first option;
* - options: array, the attributes for the select option tags. The array keys must be valid option values,
* and the array values are the extra attributes for the corresponding option tags. For example,
*
* ~~~
* array(
* 'value1' => array('disabled' => true),
* 'value2' => array('label' => 'value 2'),
* );
* ~~~
*
* - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
* except that the array keys represent the optgroup labels specified in $items.
* - unselect: string, the value that will be submitted when no option is selected.
* When this attribute is set, a hidden field will be generated so that if no option is selected in multiple
* mode, we can still obtain the posted unselect value.
*
* The rest of the options will be rendered as the attributes of the resulting tag. The values will
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
*
* @return string the generated list box tag
*/
public static function listBox($name, $selection = null, $items = array(), $options = array())
{
if (!isset($options['size'])) {
$options['size'] = 4;
}
if (isset($options['multiple']) && $options['multiple'] && substr($name, -2) !== '[]') {
$name .= '[]';
}
$options['name'] = $name;
if (isset($options['unselect'])) {
// add a hidden field so that if the list box has no option being selected, it still submits a value
if (substr($name, -2) === '[]') {
$name = substr($name, 0, -2);
}
$hidden = static::hiddenInput($name, $options['unselect']);
unset($options['unselect']);
} else {
$hidden = '';
}
$selectOptions = static::renderSelectOptions($selection, $items, $options);
return $hidden . static::tag('select', "\n" . $selectOptions . "\n", $options);
}
/**
* Generates a list of checkboxes.
* A checkbox list allows multiple selection, like [[listBox()]].
* As a result, the corresponding submitted value is an array.
* @param string $name the name attribute of each checkbox.
* @param string|array $selection the selected value(s).
* @param array $items the data item used to generate the checkboxes.
* The array keys are the labels, while the array values are the corresponding checkbox values.
* Note that the labels will NOT be HTML-encoded, while the values will.
* @param array $options options (name => config) for the checkbox list. The following options are supported:
*
* - unselect: string, the value that should be submitted when none of the checkboxes is selected.
* By setting this option, a hidden input will be generated.
* - separator: string, the HTML code that separates items.
* - item: callable, a callback that can be used to customize the generation of the HTML code
* corresponding to a single item in $items. The signature of this callback must be:
*
* ~~~
* function ($index, $label, $name, $checked, $value)
* ~~~
*
* where $index is the zero-based index of the checkbox in the whole list; $label
* is the label for the checkbox; and $name, $value and $checked represent the name,
* value and the checked status of the checkbox input.
* @return string the generated checkbox list
*/
public static function checkboxList($name, $selection = null, $items = array(), $options = array())
{
if (substr($name, -2) !== '[]') {
$name .= '[]';
}
$formatter = isset($options['item']) ? $options['item'] : null;
$lines = array();
$index = 0;
foreach ($items as $value => $label) {
$checked = $selection !== null &&
(!is_array($selection) && !strcmp($value, $selection)
|| is_array($selection) && in_array($value, $selection));
if ($formatter !== null) {
$lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
} else {
$lines[] = static::label(static::checkbox($name, $checked, $value) . ' ' . $label);
}
$index++;
}
if (isset($options['unselect'])) {
// add a hidden field so that if the list box has no option being selected, it still submits a value
$name2 = substr($name, -2) === '[]' ? substr($name, 0, -2) : $name;
$hidden = static::hiddenInput($name2, $options['unselect']);
} else {
$hidden = '';
}
$separator = isset($options['separator']) ? $options['separator'] : "\n";
return $hidden . implode($separator, $lines);
}
/**
* Generates a list of radio buttons.
* A radio button list is like a checkbox list, except that it only allows single selection.
* @param string $name the name attribute of each radio button.
* @param string|array $selection the selected value(s).
* @param array $items the data item used to generate the radio buttons.
* The array keys are the labels, while the array values are the corresponding radio button values.
* Note that the labels will NOT be HTML-encoded, while the values will.
* @param array $options options (name => config) for the radio button list. The following options are supported:
*
* - unselect: string, the value that should be submitted when none of the radio buttons is selected.
* By setting this option, a hidden input will be generated.
* - separator: string, the HTML code that separates items.
* - item: callable, a callback that can be used to customize the generation of the HTML code
* corresponding to a single item in $items. The signature of this callback must be:
*
* ~~~
* function ($index, $label, $name, $checked, $value)
* ~~~
*
* where $index is the zero-based index of the radio button in the whole list; $label
* is the label for the radio button; and $name, $value and $checked represent the name,
* value and the checked status of the radio button input.
* @return string the generated radio button list
*/
public static function radioList($name, $selection = null, $items = array(), $options = array())
{
$formatter = isset($options['item']) ? $options['item'] : null;
$lines = array();
$index = 0;
foreach ($items as $value => $label) {
$checked = $selection !== null &&
(!is_array($selection) && !strcmp($value, $selection)
|| is_array($selection) && in_array($value, $selection));
if ($formatter !== null) {
$lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
} else {
$lines[] = static::label(static::radio($name, $checked, $value) . ' ' . $label);
}
$index++;
}
$separator = isset($options['separator']) ? $options['separator'] : "\n";
if (isset($options['unselect'])) {
// add a hidden field so that if the list box has no option being selected, it still submits a value
$hidden = static::hiddenInput($name, $options['unselect']);
} else {
$hidden = '';
}
return $hidden . implode($separator, $lines);
}
/**
* Renders the option tags that can be used by [[dropDownList()]] and [[listBox()]].
* @param string|array $selection the selected value(s). This can be either a string for single selection
* or an array for multiple selections.
* @param array $items the option data items. The array keys are option values, and the array values
* are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
* For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
* If you have a list of data models, you may convert them into the format described above using
* [[\yii\helpers\ArrayHelper::map()]].
*
* Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
* the labels will also be HTML-encoded.
* @param array $tagOptions the $options parameter that is passed to the [[dropDownList()]] or [[listBox()]] call.
* This method will take out these elements, if any: "prompt", "options" and "groups". See more details
* in [[dropDownList()]] for the explanation of these elements.
*
* @return string the generated list options
*/
public static function renderSelectOptions($selection, $items, &$tagOptions = array())
{
$lines = array();
if (isset($tagOptions['prompt'])) {
$prompt = str_replace(' ', '&nbsp;', static::encode($tagOptions['prompt']));
$lines[] = static::tag('option', $prompt, array('value' => ''));
}
$options = isset($tagOptions['options']) ? $tagOptions['options'] : array();
$groups = isset($tagOptions['groups']) ? $tagOptions['groups'] : array();
unset($tagOptions['prompt'], $tagOptions['options'], $tagOptions['groups']);
foreach ($items as $key => $value) {
if (is_array($value)) {
$groupAttrs = isset($groups[$key]) ? $groups[$key] : array();
$groupAttrs['label'] = $key;
$attrs = array('options' => $options, 'groups' => $groups);
$content = static::renderSelectOptions($selection, $value, $attrs);
$lines[] = static::tag('optgroup', "\n" . $content . "\n", $groupAttrs);
} else {
$attrs = isset($options[$key]) ? $options[$key] : array();
$attrs['value'] = $key;
$attrs['selected'] = $selection !== null &&
(!is_array($selection) && !strcmp($key, $selection)
|| is_array($selection) && in_array($key, $selection));
$lines[] = static::tag('option', str_replace(' ', '&nbsp;', static::encode($value)), $attrs);
}
}
return implode("\n", $lines);
}
/**
* Renders the HTML tag attributes.
* Boolean attributes such as s 'checked', 'disabled', 'readonly', will be handled specially
* according to [[booleanAttributes]] and [[showBooleanAttributeValues]].
* @param array $attributes attributes to be rendered. The attribute values will be HTML-encoded using [[encode()]].
* Attributes whose value is null will be ignored and not put in the rendering result.
* @return string the rendering result. If the attributes are not empty, they will be rendered
* into a string with a leading white space (such that it can be directly appended to the tag name
* in a tag. If there is no attribute, an empty string will be returned.
*/
public static function renderTagAttributes($attributes)
{
if (count($attributes) > 1) {
$sorted = array();
foreach (static::$attributeOrder as $name) {
if (isset($attributes[$name])) {
$sorted[$name] = $attributes[$name];
}
}
$attributes = array_merge($sorted, $attributes);
}
$html = '';
foreach ($attributes as $name => $value) {
if (isset(static::$booleanAttributes[strtolower($name)])) {
if ($value || strcasecmp($name, $value) === 0) {
$html .= static::$showBooleanAttributeValues ? " $name=\"$name\"" : " $name";
}
} elseif ($value !== null) {
$html .= " $name=\"" . static::encode($value) . '"';
}
}
return $html;
}
/**
* Normalizes the input parameter to be a valid URL.
*
* If the input parameter
*
* - is an empty string: the currently requested URL will be returned;
* - is a non-empty string: it will be processed by [[Yii::getAlias()]] which, if the string is an alias,
* will be resolved into a URL;
* - is an array: the first array element is considered a route, while the rest of the name-value
* pairs are considered as the parameters to be used for URL creation using [[\yii\base\Application::createUrl()]].
* Here are some examples: `array('post/index', 'page' => 2)`, `array('index')`.
*
* @param array|string $url the parameter to be used to generate a valid URL
* @return string the normalized URL
* @throws InvalidParamException if the parameter is invalid.
*/
public static function url($url)
{
if (is_array($url)) {
if (isset($url[0])) {
return Yii::$app->createUrl($url[0], array_splice($url, 1));
} else {
throw new InvalidParamException('The array specifying a URL must contain at least one element.');
}
} elseif ($url === '') {
return Yii::$app->getRequest()->getUrl();
} else {
return Yii::getAlias($url);
}
}
}

272
framework/helpers/SecurityHelper.php

@ -0,0 +1,272 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\helpers;
use Yii;
use yii\base\Exception;
use yii\base\InvalidConfigException;
use yii\base\InvalidParamException;
/**
* SecurityHelper provides a set of methods to handle common security-related tasks.
*
* In particular, SecurityHelper supports the following features:
*
* - Encryption/decryption: [[encrypt()]] and [[decrypt()]]
* - Data tampering prevention: [[hashData()]] and [[validateData()]]
* - Password validation: [[generatePasswordHash()]] and [[validatePassword()]]
*
* Additionally, SecurityHelper provides [[getSecretKey()]] to support generating
* named secret keys. These secret keys, once generated, will be stored in a file
* and made available in future requests.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Tom Worster <fsb@thefsb.org>
* @since 2.0
*/
class SecurityHelper
{
/**
* Encrypts data.
* @param string $data data to be encrypted.
* @param string $key the encryption secret key
* @return string the encrypted data
* @throws Exception if PHP Mcrypt extension is not loaded or failed to be initialized
* @see decrypt()
*/
public static function encrypt($data, $key)
{
$module = static::openCryptModule();
$key = StringHelper::substr($key, 0, mcrypt_enc_get_key_size($module));
srand();
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND);
mcrypt_generic_init($module, $key, $iv);
$encrypted = $iv . mcrypt_generic($module, $data);
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
return $encrypted;
}
/**
* Decrypts data
* @param string $data data to be decrypted.
* @param string $key the decryption secret key
* @return string the decrypted data
* @throws Exception if PHP Mcrypt extension is not loaded or failed to be initialized
* @see encrypt()
*/
public static function decrypt($data, $key)
{
$module = static::openCryptModule();
$key = StringHelper::substr($key, 0, mcrypt_enc_get_key_size($module));
$ivSize = mcrypt_enc_get_iv_size($module);
$iv = StringHelper::substr($data, 0, $ivSize);
mcrypt_generic_init($module, $key, $iv);
$decrypted = mdecrypt_generic($module, StringHelper::substr($data, $ivSize, StringHelper::strlen($data)));
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
return rtrim($decrypted, "\0");
}
/**
* Prefixes data with a keyed hash value so that it can later be detected if it is tampered.
* @param string $data the data to be protected
* @param string $key the secret key to be used for generating hash
* @param string $algorithm the hashing algorithm (e.g. "md5", "sha1", "sha256", etc.). Call PHP "hash_algos()"
* function to see the supported hashing algorithms on your system.
* @return string the data prefixed with the keyed hash
* @see validateData()
* @see getSecretKey()
*/
public static function hashData($data, $key, $algorithm = 'sha256')
{
return hash_hmac($algorithm, $data, $key) . $data;
}
/**
* Validates if the given data is tampered.
* @param string $data the data to be validated. The data must be previously
* generated by [[hashData()]].
* @param string $key the secret key that was previously used to generate the hash for the data in [[hashData()]].
* @param string $algorithm the hashing algorithm (e.g. "md5", "sha1", "sha256", etc.). Call PHP "hash_algos()"
* function to see the supported hashing algorithms on your system. This must be the same
* as the value passed to [[hashData()]] when generating the hash for the data.
* @return string the real data with the hash stripped off. False if the data is tampered.
* @see hashData()
*/
public static function validateData($data, $key, $algorithm = 'sha256')
{
$hashSize = StringHelper::strlen(hash_hmac($algorithm, 'test', $key));
$n = StringHelper::strlen($data);
if ($n >= $hashSize) {
$hash = StringHelper::substr($data, 0, $hashSize);
$data2 = StringHelper::substr($data, $hashSize, $n - $hashSize);
return $hash === hash_hmac($algorithm, $data2, $key) ? $data2 : false;
} else {
return false;
}
}
/**
* Returns a secret key associated with the specified name.
* If the secret key does not exist, a random key will be generated
* and saved in the file "keys.php" under the application's runtime directory
* so that the same secret key can be returned in future requests.
* @param string $name the name that is associated with the secret key
* @param integer $length the length of the key that should be generated if not exists
* @return string the secret key associated with the specified name
*/
public static function getSecretKey($name, $length = 32)
{
static $keys;
$keyFile = Yii::$app->getRuntimePath() . '/keys.php';
if ($keys === null) {
$keys = is_file($keyFile) ? require($keyFile) : array();
}
if (!isset($keys[$name])) {
// generate a 32-char random key
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$keys[$name] = substr(str_shuffle(str_repeat($chars, 5)), 0, $length);
file_put_contents($keyFile, "<?php\nreturn " . var_export($keys, true) . ";\n");
}
return $keys[$name];
}
/**
* Opens the mcrypt module.
* @return resource the mcrypt module handle.
* @throws InvalidConfigException if mcrypt extension is not installed
* @throws Exception if mcrypt initialization fails
*/
protected static function openCryptModule()
{
if (!extension_loaded('mcrypt')) {
throw new InvalidConfigException('The mcrypt PHP extension is not installed.');
}
$module = @mcrypt_module_open('rijndael-256', '', MCRYPT_MODE_CBC, '');
if ($module === false) {
throw new Exception('Failed to initialize the mcrypt module.');
}
return $module;
}
/**
* Generates a secure hash from a password and a random salt.
*
* The generated hash can be stored in database (e.g. `CHAR(64) CHARACTER SET latin1` on MySQL).
* Later when a password needs to be validated, the hash can be fetched and passed
* to [[validatePassword()]]. For example,
*
* ~~~
* // generates the hash (usually done during user registration or when the password is changed)
* $hash = SecurityHelper::hashPassword($password);
* // ...save $hash in database...
*
* // during login, validate if the password entered is correct using $hash fetched from database
* if (PasswordHelper::verifyPassword($password, $hash) {
* // password is good
* } else {
* // password is bad
* }
* ~~~
*
* @param string $password The password to be hashed.
* @param integer $cost Cost parameter used by the Blowfish hash algorithm.
* The higher the value of cost,
* the longer it takes to generate the hash and to verify a password against it. Higher cost
* therefore slows down a brute-force attack. For best protection against brute for attacks,
* set it to the highest value that is tolerable on production servers. The time taken to
* compute the hash doubles for every increment by one of $cost. So, for example, if the
* hash takes 1 second to compute when $cost is 14 then then the compute time varies as
* 2^($cost - 14) seconds.
* @throws Exception on bad password parameter or cost parameter
* @return string The password hash string, ASCII and not longer than 64 characters.
* @see validatePassword()
*/
public static function generatePasswordHash($password, $cost = 13)
{
$salt = static::generateSalt($cost);
$hash = crypt($password, $salt);
if (!is_string($hash) || strlen($hash) < 32) {
throw new Exception('Unknown error occurred while generating hash.');
}
return $hash;
}
/**
* Verifies a password against a hash.
* @param string $password The password to verify.
* @param string $hash The hash to verify the password against.
* @return boolean whether the password is correct.
* @throws InvalidParamException on bad password or hash parameters or if crypt() with Blowfish hash is not available.
* @see generatePasswordHash()
*/
public static function validatePassword($password, $hash)
{
if (!is_string($password) || $password === '') {
throw new InvalidParamException('Password must be a string and cannot be empty.');
}
if (!preg_match('/^\$2[axy]\$(\d\d)\$[\./0-9A-Za-z]{22}/', $hash, $matches) || $matches[1] < 4 || $matches[1] > 30) {
throw new InvalidParamException('Hash is invalid.');
}
$test = crypt($password, $hash);
$n = strlen($test);
if (strlen($test) < 32 || $n !== strlen($hash)) {
return false;
}
// Use a for-loop to compare two strings to prevent timing attacks. See:
// http://codereview.stackexchange.com/questions/13512
$check = 0;
for ($i = 0; $i < $n; ++$i) {
$check |= (ord($test[$i]) ^ ord($hash[$i]));
}
return $check === 0;
}
/**
* Generates a salt that can be used to generate a password hash.
*
* The PHP [crypt()](http://php.net/manual/en/function.crypt.php) built-in function
* requires, for the Blowfish hash algorithm, a salt string in a specific format:
* "$2a$", "$2x$" or "$2y$", a two digit cost parameter, "$", and 22 characters
* from the alphabet "./0-9A-Za-z".
*
* @param integer $cost the cost parameter
* @return string the random salt value.
* @throws InvalidParamException if the cost parameter is not between 4 and 30
*/
protected static function generateSalt($cost = 13)
{
$cost = (int)$cost;
if ($cost < 4 || $cost > 30) {
throw new InvalidParamException('Cost must be between 4 and 31.');
}
// Get 20 * 8bits of pseudo-random entropy from mt_rand().
$rand = '';
for ($i = 0; $i < 20; ++$i) {
$rand .= chr(mt_rand(0, 255));
}
// Add the microtime for a little more entropy.
$rand .= microtime();
// Mix the bits cryptographically into a 20-byte binary string.
$rand = sha1($rand, true);
// Form the prefix that specifies Blowfish algorithm and cost parameter.
$salt = sprintf("$2y$%02d$", $cost);
// Append the random salt data in the required base64 format.
$salt .= str_replace('+', '.', substr(base64_encode($rand), 0, 22));
return $salt;
}
}

35
framework/util/StringHelper.php → framework/helpers/StringHelper.php

@ -1,13 +1,11 @@
<?php <?php
/** /**
* StringHelper class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\util; namespace yii\helpers;
/** /**
* StringHelper * StringHelper
@ -19,6 +17,33 @@ namespace yii\util;
class StringHelper class StringHelper
{ {
/** /**
* Returns the number of bytes in the given string.
* This method ensures the string is treated as a byte array.
* It will use `mb_strlen()` if it is available.
* @param string $string the string being measured for length
* @return integer the number of bytes in the given string.
*/
public static function strlen($string)
{
return function_exists('mb_strlen') ? mb_strlen($string, '8bit') : strlen($string);
}
/**
* Returns the portion of string specified by the start and length parameters.
* This method ensures the string is treated as a byte array.
* It will use `mb_substr()` if it is available.
* @param string $string the input string. Must be one character or longer.
* @param integer $start the starting position
* @param integer $length the desired portion length
* @return string the extracted part of string, or FALSE on failure or an empty string.
* @see http://www.php.net/manual/en/function.substr.php
*/
public static function substr($string, $start, $length)
{
return function_exists('mb_substr') ? mb_substr($string, $start, $length, '8bit') : substr($string, $start, $length);
}
/**
* Converts a word to its plural form. * Converts a word to its plural form.
* Note that this is for English only! * Note that this is for English only!
* For example, 'apple' will become 'apples', and 'child' will become 'children'. * For example, 'apple' will become 'apples', and 'child' will become 'children'.
@ -27,7 +52,7 @@ class StringHelper
*/ */
public static function pluralize($name) public static function pluralize($name)
{ {
$rules = array( static $rules = array(
'/(m)ove$/i' => '\1oves', '/(m)ove$/i' => '\1oves',
'/(f)oot$/i' => '\1eet', '/(f)oot$/i' => '\1eet',
'/(c)hild$/i' => '\1hildren', '/(c)hild$/i' => '\1hildren',

134
framework/helpers/VarDumper.php

@ -0,0 +1,134 @@
<?php
/**
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2011 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\helpers;
/**
* VarDumper is intended to replace the buggy PHP function var_dump and print_r.
* It can correctly identify the recursively referenced objects in a complex
* object structure. It also has a recursive depth control to avoid indefinite
* recursive display of some peculiar variables.
*
* VarDumper can be used as follows,
*
* ~~~
* VarDumper::dump($var);
* ~~~
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class CVarDumper
{
private static $_objects;
private static $_output;
private static $_depth;
/**
* Displays a variable.
* This method achieves the similar functionality as var_dump and print_r
* but is more robust when handling complex objects such as Yii controllers.
* @param mixed $var variable to be dumped
* @param integer $depth maximum depth that the dumper should go into the variable. Defaults to 10.
* @param boolean $highlight whether the result should be syntax-highlighted
*/
public static function dump($var, $depth = 10, $highlight = false)
{
echo self::dumpAsString($var, $depth, $highlight);
}
/**
* Dumps a variable in terms of a string.
* This method achieves the similar functionality as var_dump and print_r
* but is more robust when handling complex objects such as Yii controllers.
* @param mixed $var variable to be dumped
* @param integer $depth maximum depth that the dumper should go into the variable. Defaults to 10.
* @param boolean $highlight whether the result should be syntax-highlighted
* @return string the string representation of the variable
*/
public static function dumpAsString($var, $depth = 10, $highlight = false)
{
self::$_output = '';
self::$_objects = array();
self::$_depth = $depth;
self::dumpInternal($var, 0);
if ($highlight) {
$result = highlight_string("<?php\n" . self::$_output, true);
self::$_output = preg_replace('/&lt;\\?php<br \\/>/', '', $result, 1);
}
return self::$_output;
}
/*
* @param mixed $var variable to be dumped
* @param integer $level depth level
*/
private static function dumpInternal($var, $level)
{
switch (gettype($var)) {
case 'boolean':
self::$_output .= $var ? 'true' : 'false';
break;
case 'integer':
self::$_output .= "$var";
break;
case 'double':
self::$_output .= "$var";
break;
case 'string':
self::$_output .= "'" . addslashes($var) . "'";
break;
case 'resource':
self::$_output .= '{resource}';
break;
case 'NULL':
self::$_output .= "null";
break;
case 'unknown type':
self::$_output .= '{unknown}';
break;
case 'array':
if (self::$_depth <= $level) {
self::$_output .= 'array(...)';
} elseif (empty($var)) {
self::$_output .= 'array()';
} else {
$keys = array_keys($var);
$spaces = str_repeat(' ', $level * 4);
self::$_output .= "array\n" . $spaces . '(';
foreach ($keys as $key) {
self::$_output .= "\n" . $spaces . ' ';
self::dumpInternal($key, 0);
self::$_output .= ' => ';
self::dumpInternal($var[$key], $level + 1);
}
self::$_output .= "\n" . $spaces . ')';
}
break;
case 'object':
if (($id = array_search($var, self::$_objects, true)) !== false) {
self::$_output .= get_class($var) . '#' . ($id + 1) . '(...)';
} elseif (self::$_depth <= $level) {
self::$_output .= get_class($var) . '(...)';
} else {
$id = self::$_objects[] = $var;
$className = get_class($var);
$members = (array)$var;
$spaces = str_repeat(' ', $level * 4);
self::$_output .= "$className#$id\n" . $spaces . '(';
foreach ($members as $key => $value) {
$keyDisplay = strtr(trim($key), array("\0" => ':'));
self::$_output .= "\n" . $spaces . " [$keyDisplay] => ";
self::dumpInternal($value, $level + 1);
}
self::$_output .= "\n" . $spaces . ')';
}
break;
}
}
}

2
framework/util/mimeTypes.php → framework/helpers/mimeTypes.php

@ -6,7 +6,7 @@
* according to file extension names. * according to file extension names.
* *
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
* @since 2.0 * @since 2.0
*/ */

108
framework/i18n/I18N.php

@ -4,91 +4,93 @@ namespace yii\i18n;
use Yii; use Yii;
use yii\base\Component; use yii\base\Component;
use yii\base\InvalidConfigException;
class I18N extends Component class I18N extends Component
{ {
/**
* @var array list of [[MessageSource]] configurations or objects. The array keys are message
* categories, and the array values are the corresponding [[MessageSource]] objects or the configurations
* for creating the [[MessageSource]] objects. The message categories can contain the wildcard '*' at the end
* to match multiple categories with the same prefix. For example, 'app\*' matches both 'app\cat1' and 'app\cat2'.
*/
public $translations;
public function init()
{
if (!isset($this->translations['yii'])) {
$this->translations['yii'] = array(
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en_US',
'basePath' => '@yii/messages',
);
}
if (!isset($this->translations['app'])) {
$this->translations['app'] = array(
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en_US',
'basePath' => '@app/messages',
);
}
}
public function translate($message, $params = array(), $language = null) public function translate($message, $params = array(), $language = null)
{ {
if ($language === null) { if ($language === null) {
$language = Yii::$app->language; $language = Yii::$app->language;
} }
if (strpos($message, '|') !== false && preg_match('/^([\w\-\.]+)\|(.*)/', $message, $matches)) { // allow chars for category: word chars, ".", "-", "/","\"
if (strpos($message, '|') !== false && preg_match('/^([\w\-\\/\.\\\\]+)\|(.*)/', $message, $matches)) {
$category = $matches[1]; $category = $matches[1];
$message = $matches[2]; $message = $matches[2];
} else { } else {
$category = 'app'; $category = 'app';
} }
// $message = $this->getMessageSource($category)->translate($category, $message, $language); $message = $this->getMessageSource($category)->translate($category, $message, $language);
//
// if (!is_array($params)) {
// $params = array($params);
// }
//
// if (isset($params[0])) {
// $message = $this->getPluralFormat($message, $params[0], $language);
// if (!isset($params['{n}'])) {
// $params['{n}'] = $params[0];
// }
// unset($params[0]);
// }
return $params === array() ? $message : strtr($message, $params); if (!is_array($params)) {
$params = array($params);
} }
public function getLocale($language) if (isset($params[0])) {
{ $message = $this->getPluralForm($message, $params[0], $language);
if (!isset($params['{n}'])) {
$params['{n}'] = $params[0];
} }
unset($params[0]);
public function getMessageSource($category)
{
return $category === 'yii' ? $this->getMessages() : $this->getCoreMessages();
} }
private $_coreMessages; return $params === array() ? $message : strtr($message, $params);
private $_messages; }
public function getCoreMessages() public function getMessageSource($category)
{ {
if (is_object($this->_coreMessages)) { if (isset($this->translations[$category])) {
return $this->_coreMessages; $source = $this->translations[$category];
} elseif ($this->_coreMessages === null) {
return $this->_coreMessages = new PhpMessageSource(array(
'sourceLanguage' => 'en_US',
'basePath' => '@yii/messages',
));
} else { } else {
return $this->_coreMessages = Yii::createObject($this->_coreMessages); // try wildcard matching
foreach ($this->translations as $pattern => $config) {
if (substr($pattern, -1) === '*' && strpos($category, rtrim($pattern, '*')) === 0) {
$source = $config;
break;
} }
} }
public function setCoreMessages($config)
{
$this->_coreMessages = $config;
} }
if (isset($source)) {
public function getMessages() return $source instanceof MessageSource ? $source : Yii::createObject($source);
{
if (is_object($this->_messages)) {
return $this->_messages;
} elseif ($this->_messages === null) {
return $this->_messages = new PhpMessageSource(array(
'sourceLanguage' => 'en_US',
'basePath' => '@app/messages',
));
} else { } else {
return $this->_messages = Yii::createObject($this->_messages); throw new InvalidConfigException("Unable to locate message source for category '$category'.");
} }
} }
public function setMessages($config) public function getLocale($language)
{ {
$this->_messages = $config;
} }
protected function getPluralFormat($message, $number, $language) protected function getPluralForm($message, $number, $language)
{ {
if (strpos($message, '|') === false) { if (strpos($message, '|') === false) {
return $message; return $message;
@ -96,7 +98,7 @@ class I18N extends Component
$chunks = explode('|', $message); $chunks = explode('|', $message);
$rules = $this->getLocale($language)->getPluralRules(); $rules = $this->getLocale($language)->getPluralRules();
foreach ($rules as $i => $rule) { foreach ($rules as $i => $rule) {
if (isset($chunks[$i]) && self::evaluate($rule, $number)) { if (isset($chunks[$i]) && $this->evaluate($rule, $number)) {
return $chunks[$i]; return $chunks[$i];
} }
} }
@ -110,7 +112,7 @@ class I18N extends Component
* @param mixed $n the number value * @param mixed $n the number value
* @return boolean the expression result * @return boolean the expression result
*/ */
protected static function evaluate($expression, $n) protected function evaluate($expression, $n)
{ {
return @eval("return $expression;"); return @eval("return $expression;");
} }

4
framework/i18n/MessageSource.php

@ -1,9 +1,7 @@
<?php <?php
/** /**
* MessageSource class file.
*
* @link http://www.yiiframework.com/ * @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC * @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */

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

Loading…
Cancel
Save