Commit 1d48d01e by Qiang Xue

refactored View.

parent 9a4f4f85
...@@ -4,9 +4,9 @@ ...@@ -4,9 +4,9 @@
* @copyright Copyright (c) 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;
/** /**
...@@ -94,7 +94,7 @@ class YiiBase ...@@ -94,7 +94,7 @@ class YiiBase
*/ */
public static $objectConfig = array(); public static $objectConfig = array();
private static $_imported = array(); // alias => class name or directory private static $_imported = array(); // alias => class name or directory
private static $_logger; private static $_logger;
/** /**
...@@ -159,9 +159,7 @@ class YiiBase ...@@ -159,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) {
...@@ -191,24 +189,30 @@ class YiiBase ...@@ -191,24 +189,30 @@ 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 || ($pos = strpos($alias, '\\')) !== false) {
} elseif (($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); }
} }
} }
return false; if ($throwException) {
throw new InvalidParamException("Invalid path alias: $alias");
} else {
return false;
}
} }
/** /**
...@@ -236,10 +240,8 @@ class YiiBase ...@@ -236,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);
} }
} }
...@@ -273,14 +275,14 @@ class YiiBase ...@@ -273,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';
} }
} }
......
...@@ -160,28 +160,28 @@ class Application extends Module ...@@ -160,28 +160,28 @@ class Application extends Module
*/ */
public function handleFatalError() public function handleFatalError()
{ {
if(YII_ENABLE_ERROR_HANDLER) { if (YII_ENABLE_ERROR_HANDLER) {
$error = error_get_last(); $error = error_get_last();
if(ErrorException::isFatalErorr($error)) { if (ErrorException::isFatalErorr($error)) {
unset($this->_memoryReserve); unset($this->_memoryReserve);
$exception = new ErrorException($error['message'], $error['type'], $error['type'], $error['file'], $error['line']); $exception = new ErrorException($error['message'], $error['type'], $error['type'], $error['file'], $error['line']);
if(function_exists('xdebug_get_function_stack')) { if (function_exists('xdebug_get_function_stack')) {
$trace = array_slice(array_reverse(xdebug_get_function_stack()), 4, -1); $trace = array_slice(array_reverse(xdebug_get_function_stack()), 4, -1);
foreach($trace as &$frame) { foreach ($trace as &$frame) {
if(!isset($frame['function'])) { if (!isset($frame['function'])) {
$frame['function'] = 'unknown'; $frame['function'] = 'unknown';
} }
// XDebug < 2.1.1: http://bugs.xdebug.org/view.php?id=695 // XDebug < 2.1.1: http://bugs.xdebug.org/view.php?id=695
if(!isset($frame['type'])) { if (!isset($frame['type'])) {
$frame['type'] = '::'; $frame['type'] = '::';
} }
// XDebug has a different key name // XDebug has a different key name
$frame['args'] = array(); $frame['args'] = array();
if(isset($frame['params']) && !isset($frame['args'])) { if (isset($frame['params']) && !isset($frame['args'])) {
$frame['args'] = $frame['params']; $frame['args'] = $frame['params'];
} }
} }
...@@ -214,8 +214,8 @@ class Application extends Module ...@@ -214,8 +214,8 @@ class Application extends Module
$this->beforeRequest(); $this->beforeRequest();
// Allocating twice more than required to display memory exhausted error // Allocating twice more than required to display memory exhausted error
// in case of trying to allocate last 1 byte while all memory is taken. // in case of trying to allocate last 1 byte while all memory is taken.
$this->_memoryReserve = str_repeat('x', 1024*256); $this->_memoryReserve = str_repeat('x', 1024 * 256);
register_shutdown_function(array($this,'end'),0,false); register_shutdown_function(array($this, 'end'), 0, false);
$status = $this->processRequest(); $status = $this->processRequest();
$this->afterRequest(); $this->afterRequest();
return $status; return $status;
...@@ -346,15 +346,6 @@ class Application extends Module ...@@ -346,15 +346,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.
*/ */
...@@ -373,12 +364,12 @@ class Application extends Module ...@@ -373,12 +364,12 @@ 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');
} }
/** /**
...@@ -423,6 +414,9 @@ class Application extends Module ...@@ -423,6 +414,9 @@ class Application extends Module
'urlManager' => array( 'urlManager' => array(
'class' => 'yii\web\UrlManager', 'class' => 'yii\web\UrlManager',
), ),
'view' => array(
'class' => 'yii\base\View',
),
)); ));
} }
...@@ -446,8 +440,8 @@ class Application extends Module ...@@ -446,8 +440,8 @@ class Application extends Module
// in case error appeared in __toString method we can't throw any exception // in case error appeared in __toString method we can't throw any exception
$trace = debug_backtrace(false); $trace = debug_backtrace(false);
array_shift($trace); array_shift($trace);
foreach($trace as $frame) { foreach ($trace as $frame) {
if($frame['function'] == '__toString') { if ($frame['function'] == '__toString') {
$this->handleException($exception); $this->handleException($exception);
} }
} }
...@@ -481,7 +475,7 @@ class Application extends Module ...@@ -481,7 +475,7 @@ class Application extends Module
$this->end(1); $this->end(1);
} catch(\Exception $e) { } catch (\Exception $e) {
// exception could be thrown in end() or ErrorHandler::handle() // exception could be thrown in end() or ErrorHandler::handle()
$msg = (string)$e; $msg = (string)$e;
$msg .= "\nPrevious exception:\n"; $msg .= "\nPrevious exception:\n";
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
namespace yii\base; namespace yii\base;
use Yii; use Yii;
use yii\util\FileHelper;
use yii\util\StringHelper; use yii\util\StringHelper;
/** /**
...@@ -295,34 +296,42 @@ class Controller extends Component ...@@ -295,34 +296,42 @@ 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); $viewFile = $this->findViewFile($view);
} $layoutFile = $this->findLayoutFile();
return Yii::$app->getView()->render($this, $viewFile, $params, $layoutFile);
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 $this->renderFile($this->findViewFile($view), $params);
} }
/**
* 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()->render($this, $file, $params);
}
public function createView()
{
return new View($this);
} }
/** /**
...@@ -335,4 +344,105 @@ class Controller extends Component ...@@ -335,4 +344,105 @@ class Controller extends Component
{ {
return $this->module->getViewPath() . DIRECTORY_SEPARATOR . $this->id; return $this->module->getViewPath() . DIRECTORY_SEPARATOR . $this->id;
} }
/**
* 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 [[viewPath]].
*
* If the view name does not contain a file extension, it will use the default one `.php`.
*
* @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
*/
protected function findViewFile($view)
{
if (strncmp($view, '@', 1) === 0) {
// e.g. "@app/views/common"
$file = Yii::getAlias($view);
} elseif (strncmp($view, '/', 1) !== 0) {
// e.g. "index"
$file = $this->getViewPath() . DIRECTORY_SEPARATOR . $view;
} elseif (strncmp($view, '//', 2) !== 0) {
// e.g. "/site/index"
$file = $this->module->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
} else {
// e.g. "//layouts/main"
$file = Yii::$app->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
}
if (FileHelper::getExtension($file) === '') {
$file .= '.php';
}
return $file;
}
/**
* 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()
{
/** @var $module Module */
if (is_string($this->layout)) {
$module = $this->module;
$view = $this->layout;
} elseif ($this->layout === null) {
$module = $this->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;
}
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;
}
} }
...@@ -253,14 +253,9 @@ class ErrorHandler extends Component ...@@ -253,14 +253,9 @@ 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($this, $name, array(
'exception' => $exception, 'exception' => $exception,
)); ));
} }
......
...@@ -40,7 +40,8 @@ class Theme extends Component ...@@ -40,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;
...@@ -63,7 +64,9 @@ class Theme extends Component ...@@ -63,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;
} }
...@@ -93,7 +96,7 @@ class Theme extends Component ...@@ -93,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) {
......
...@@ -8,14 +8,14 @@ ...@@ -8,14 +8,14 @@
namespace yii\base; namespace yii\base;
use Yii; use Yii;
use yii\util\FileHelper;
use yii\base\Application; use yii\base\Application;
use yii\util\FileHelper;
/** /**
* View represents a view object in the MVC pattern. * View represents a view object in the MVC pattern.
* *
* View provides a set of methods (e.g. [[render()]]) for rendering purpose. * View provides a set of methods (e.g. [[render()]]) for rendering purpose.
* *
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
...@@ -24,133 +24,112 @@ class View extends Component ...@@ -24,133 +24,112 @@ 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.
* If not set, it will use the [[Module::layout]] of the currently active module.
*/
public $layout;
/**
* @var string the language that the view should be rendered in. If not set, it will use
* the value of [[Application::language]].
*/
public $language;
/** /**
* @var string the language that the original view is in. If not set, it will use * @var mixed custom parameters that are shared among view templates.
* the value of [[Application::sourceLanguage]].
*/ */
public $sourceLanguage; public $params;
/**
* @var boolean whether to localize the view when possible. Defaults to true.
* Note that when this is true, if a localized view cannot be found, the original view will be rendered.
* No error will be reported.
*/
public $enableI18N = true;
/** /**
* @var boolean whether to theme the view when possible. Defaults to true. * @var ViewRenderer|array the view renderer object or the configuration array for
* Note that theming will be disabled if [[Application::theme]] is not set. * creating the view renderer. If not set, view files will be treated as normal PHP files.
*/ */
public $enableTheme = true; public $renderer;
/** /**
* @var mixed custom parameters that are available in the view template * @var Theme|array the theme object or the configuration array for creating the theme.
* If not set, it means theming is not enabled.
*/ */
public $params; public $theme;
/** /**
* @var Widget[] the widgets that are currently not ended * @var Widget[] the widgets that are currently not ended
*/ */
private $_widgetStack = array(); private $_widgetStack = 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.
* 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())
{
$content = $this->renderPartial($view, $params);
return $this->renderContent($content);
}
/** /**
* Renders a text content within a layout. * Initializes the view component.
* 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) public function init()
{ {
$layoutFile = $this->findLayoutFile(); parent::init();
if ($layoutFile !== false) { if (is_array($this->renderer)) {
return $this->renderFile($layoutFile, array('content' => $content)); $this->renderer = Yii::createObject($this->renderer);
} else { }
return $content; if (is_array($this->theme)) {
$this->theme = Yii::createObject($this->theme);
} }
} }
/** /**
* Renders a view. * Renders a view file under a context with an optional layout.
*
* This method is similar to [[renderFile()]] except that it will update [[context]]
* with the provided $context parameter. It will also apply layout to the rendering result
* of the view file if $layoutFile is given.
* *
* The method first finds the actual view file corresponding to the specified view. * Theming and localization will be performed for the view file and the layout file, if possible.
* It then calls [[renderFile()]] to render the view file. The rendering result is returned
* 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 object $context the context object for rendering the file. This could be a controller, a widget,
* @param array $params the parameters that should be made available in the view. The PHP function `extract()` * or any other object that serves as the rendering context of the view file. In the view file,
* will be called on this variable to extract the variables from this parameter. * it can be accessed through the [[context]] property.
* @param string $viewFile the view file. This can be 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 string|boolean $layoutFile the layout file. This can be a file path or a path alias.
* If it is false, it means no layout should be applied.
* @return string the rendering result * @return string the rendering result
* @throws InvalidParamException if the view file cannot be found * @throws InvalidParamException if the view file or the layout file does not exist.
* @see findViewFile()
*/ */
public function renderPartial($view, $params = array()) public function render($context, $viewFile, $params = array(), $layoutFile = false)
{ {
$file = $this->findViewFile($view); $oldContext = $this->context;
if ($file !== false) { $this->context = $context;
return $this->renderFile($file, $params);
} else { $content = $this->renderFile($viewFile, $params);
throw new InvalidParamException("Unable to find the view file for view '$view'.");
if ($layoutFile !== false) {
$content = $this->renderFile($layoutFile, array('content' => $content));
} }
$this->context = $oldContext;
return $content;
} }
/** /**
* Renders a view file. * Renders a view file.
* *
* If a [[ViewRenderer|view renderer]] is installed, this method will try to use the view renderer * This method renders the specified view file under the existing [[context]].
* to render the view file. Otherwise, it will simply include the view file, capture its output *
* and return it as a string. * If [[theme]] is enabled (not null), it will try to render the themed version of the view file as long
* as it is available.
* *
* @param string $file the view file. * The method will call [[FileHelper::localize()]] to localize the view file.
*
* If [[renderer]] is enabled (not null), the method will use it to render the view file.
* Otherwise, it will simply include the view file as a normal PHP file, capture its output and
* return it as a string.
*
* @param string $viewFile the view file. This can be 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.
* @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())
{ {
$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 {
throw new InvalidParamException("The view file does not exist: $viewFile");
}
if ($this->renderer !== null) {
return $this->renderer->render($this, $viewFile, $params);
} else { } else {
return $this->renderPhpFile($file, $params); return $this->renderPhpFile($viewFile, $params);
} }
} }
...@@ -161,6 +140,8 @@ class View extends Component ...@@ -161,6 +140,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
...@@ -184,7 +165,7 @@ class View extends Component ...@@ -184,7 +165,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);
} }
/** /**
...@@ -233,7 +214,7 @@ class View extends Component ...@@ -233,7 +214,7 @@ 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()
{ {
...@@ -242,251 +223,99 @@ class View extends Component ...@@ -242,251 +223,99 @@ class View extends Component
$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.
*
* 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 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)
{
if (FileHelper::getExtension($view) === '') {
$view .= '.php';
}
if (strncmp($view, '@', 1) === 0) {
// 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) {
$file = $theme->apply($file);
}
return $this->enableI18N ? FileHelper::localize($file, $this->language, $this->sourceLanguage) : $file;
} else {
throw new InvalidConfigException("View file for view '$view' does not exist: $file");
} }
} }
/** //
* Finds the layout file that can be applied to the view. // /**
* // * Begins recording a clip.
* The applicable layout is resolved according to the following rules: // * This method is a shortcut to beginning [[yii\widgets\Clip]]
* // * @param string $id the clip ID.
* - If [[layout]] is specified as a string, use it as the layout name and search for the layout file // * @param array $properties initial property values for [[yii\widgets\Clip]]
* under the layout path of the currently active module; // */
* - If [[layout]] is null and [[owner]] is a controller: // public function beginClip($id, $properties = array())
* * 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; // $properties['id'] = $id;
* * If the controller's [[Controller::layout|layout]] is null, look through its ancestor modules // $this->beginWidget('yii\widgets\Clip', $properties);
* 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. // /**
* // * Ends recording a clip.
* Like view names, a layout name can take several formats: // */
* // public function endClip()
* - path alias (e.g. "@app/views/layouts/main"); // {
* - absolute path (e.g. "/main"): the layout name starts with a slash. The actual layout file will be // $this->endWidget();
* looked for under the [[Application::layoutPath|layout path]] of the application; // }
* - relative path (e.g. "main"): the actual layout layout file will be looked for under the //
* [[Module::viewPath|view path]] of the context module determined by the above layout resolution process. // /**
* // * Begins fragment caching.
* If the layout name does not contain a file extension, it will default to `.php`. // * This method will display cached content if it is available.
* // * If not, it will start caching and would expect an [[endCache()]]
* If [[enableTheme]] is true and there is an active application them, the method will also // * call to end the cache and save the content into cache.
* attempt to use a themed version of the layout file, when available. // * A typical usage of fragment caching is as follows,
* // *
* And if [[enableI18N]] is true, the method will attempt to use a translated version of the layout file, // * ~~~
* when available. // * if($this->beginCache($id)) {
* // * // ...generate content here
* @return string|boolean the layout file path, or false if layout is not needed. // * $this->endCache();
* @throws InvalidConfigException if the layout file cannot be found // * }
*/ // * ~~~
public function findLayoutFile() // *
{ // * @param string $id a unique ID identifying the fragment to be cached.
/** @var $module Module */ // * @param array $properties initial property values for [[yii\widgets\OutputCache]]
if (is_string($this->layout)) { // * @return boolean whether we need to generate content for caching. False if cached version is available.
if (Yii::$app->controller) { // * @see endCache
$module = Yii::$app->controller->module; // */
} else { // public function beginCache($id, $properties = array())
$module = Yii::$app; // {
} // $properties['id'] = $id;
$view = $this->layout; // $cache = $this->beginWidget('yii\widgets\OutputCache', $properties);
} elseif ($this->owner instanceof Controller) { // if ($cache->getIsContentCached()) {
if (is_string($this->owner->layout)) { // $this->endCache();
$module = $this->owner->module; // return false;
$view = $this->owner->layout; // } else {
} elseif ($this->owner->layout === null) { // return true;
$module = $this->owner->module; // }
while ($module !== null && $module->layout === null) { // }
$module = $module->module; //
} // /**
if ($module !== null && is_string($module->layout)) { // * Ends fragment caching.
$view = $module->layout; // * This is an alias to [[endWidget()]]
} // * @see beginCache
} // */
} // public function endCache()
// {
if (!isset($view)) { // $this->endWidget();
return false; // }
} //
// /**
if (FileHelper::getExtension($view) === '') { // * Begins the rendering of content that is to be decorated by the specified view.
$view .= '.php'; // * @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),
if (strncmp($view, '@', 1) === 0) { // * the default layout will be used as the decorative view.
if (($file = Yii::getAlias($view)) === false) { // * Note that if the current controller does not belong to
throw new InvalidConfigException("Invalid path alias: $view"); // * 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
} elseif (strncmp($view, '/', 1) === 0) { // * {@link CWebModule::layout default layout}.
$file = Yii::$app->getLayoutPath() . DIRECTORY_SEPARATOR . $view; // * @param array $params the variables (name=>value) to be extracted and made available in the decorative view.
} else { // * @see endContent
$file = $module->getLayoutPath() . DIRECTORY_SEPARATOR . $view; // * @see yii\widgets\ContentDecorator
} // */
// public function beginContent($view, $params = array())
if (is_file($file)) { // {
if ($this->enableTheme && ($theme = Yii::$app->getTheme()) !== null) { // $this->beginWidget('yii\widgets\ContentDecorator', array(
$file = $theme->apply($file); // 'view' => $view,
} // 'params' => $params,
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 the rendering of content.
// * @see beginContent
// */
// public function endContent()
// {
// $this->endWidget();
// }
} }
\ No newline at end of file
...@@ -7,6 +7,9 @@ ...@@ -7,6 +7,9 @@
namespace yii\base; namespace yii\base;
use Yii;
use yii\util\FileHelper;
/** /**
* Widget is the base class for widgets. * Widget is the base class for widgets.
* *
...@@ -70,35 +73,27 @@ class Widget extends Component ...@@ -70,35 +73,27 @@ 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); $file = $this->findViewFile($view);
return Yii::$app->getView()->render($this, $file, $params);
} }
/** /**
* @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()->render($this, $file, $params);
} }
/** /**
...@@ -112,4 +107,44 @@ class Widget extends Component ...@@ -112,4 +107,44 @@ class Widget extends Component
$class = new \ReflectionClass($className); $class = new \ReflectionClass($className);
return dirname($class->getFileName()) . DIRECTORY_SEPARATOR . 'views'; return dirname($class->getFileName()) . DIRECTORY_SEPARATOR . 'views';
} }
/**
* Finds the view file based on the given view name.
*
* The 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 [[viewPath]].
*
* If the view name does not contain a file extension, it will use the default one `.php`.
*
* @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
*/
public function findViewFile($view)
{
if (strncmp($view, '@', 1) === 0) {
// e.g. "@app/views/common"
$file = Yii::getAlias($view);
} elseif (strncmp($view, '/', 1) !== 0) {
// e.g. "index"
$file = $this->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 (FileHelper::getExtension($file) === '') {
$file .= '.php';
}
return $file;
}
} }
\ No newline at end of file
...@@ -52,9 +52,6 @@ class FileCache extends Cache ...@@ -52,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);
} }
......
...@@ -114,7 +114,7 @@ class MigrateController extends Controller ...@@ -114,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;
......
...@@ -43,7 +43,7 @@ class FileHelper ...@@ -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);
......
<?php <?php
/** /**
* @var \Exception $exception * @var \Exception $exception
* @var \yii\base\ErrorHandler $owner * @var \yii\base\ErrorHandler $context
*/ */
$owner = $this->owner; $context = $this->context;
$title = $owner->htmlEncode($exception instanceof \yii\base\Exception || $exception instanceof \yii\base\ErrorException ? $exception->getName() : get_class($exception)); $title = $context->htmlEncode($exception instanceof \yii\base\Exception || $exception instanceof \yii\base\ErrorException ? $exception->getName() : get_class($exception));
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
...@@ -52,7 +52,7 @@ $title = $owner->htmlEncode($exception instanceof \yii\base\Exception || $except ...@@ -52,7 +52,7 @@ $title = $owner->htmlEncode($exception instanceof \yii\base\Exception || $except
<body> <body>
<h1><?php echo $title?></h1> <h1><?php echo $title?></h1>
<h2><?php echo nl2br($owner->htmlEncode($exception->getMessage()))?></h2> <h2><?php echo nl2br($context->htmlEncode($exception->getMessage()))?></h2>
<p> <p>
The above error occurred while the Web server was processing your request. The above error occurred while the Web server was processing your request.
</p> </p>
...@@ -61,7 +61,7 @@ $title = $owner->htmlEncode($exception instanceof \yii\base\Exception || $except ...@@ -61,7 +61,7 @@ $title = $owner->htmlEncode($exception instanceof \yii\base\Exception || $except
</p> </p>
<div class="version"> <div class="version">
<?php echo date('Y-m-d H:i:s', time())?> <?php echo date('Y-m-d H:i:s', time())?>
<?php echo YII_DEBUG ? $owner->versionInfo : ''?> <?php echo YII_DEBUG ? $context->versionInfo : ''?>
</div> </div>
</body> </body>
</html> </html>
\ No newline at end of file
<?php <?php
/** /**
* @var \Exception $exception * @var \Exception $exception
* @var \yii\base\ErrorHandler $owner * @var \yii\base\ErrorHandler $context
*/ */
$owner = $this->owner; $context = $this->context;
$title = $owner->htmlEncode($exception instanceof \yii\base\Exception || $exception instanceof \yii\base\ErrorException ? $exception->getName().' ('.get_class($exception).')' : get_class($exception)); $title = $context->htmlEncode($exception instanceof \yii\base\Exception || $exception instanceof \yii\base\ErrorException ? $exception->getName().' ('.get_class($exception).')' : get_class($exception));
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
...@@ -164,26 +164,26 @@ $title = $owner->htmlEncode($exception instanceof \yii\base\Exception || $except ...@@ -164,26 +164,26 @@ $title = $owner->htmlEncode($exception instanceof \yii\base\Exception || $except
<h1><?php echo $title?></h1> <h1><?php echo $title?></h1>
<p class="message"> <p class="message">
<?php echo nl2br($owner->htmlEncode($exception->getMessage()))?> <?php echo nl2br($context->htmlEncode($exception->getMessage()))?>
</p> </p>
<div class="source"> <div class="source">
<p class="file"> <p class="file">
<?php echo $owner->htmlEncode($exception->getFile()) . '(' . $exception->getLine() . ')'?> <?php echo $context->htmlEncode($exception->getFile()) . '(' . $exception->getLine() . ')'?>
</p> </p>
<?php if (YII_DEBUG) $owner->renderSourceCode($exception->getFile(), $exception->getLine(), $owner->maxSourceLines)?> <?php if (YII_DEBUG) $context->renderSourceCode($exception->getFile(), $exception->getLine(), $context->maxSourceLines)?>
</div> </div>
<?php if (YII_DEBUG):?> <?php if (YII_DEBUG):?>
<div class="traces"> <div class="traces">
<h2>Stack Trace</h2> <h2>Stack Trace</h2>
<?php $owner->renderTrace($exception->getTrace())?> <?php $context->renderTrace($exception->getTrace())?>
</div> </div>
<?php endif?> <?php endif?>
<div class="version"> <div class="version">
<?php echo date('Y-m-d H:i:s', time())?> <?php echo date('Y-m-d H:i:s', time())?>
<?php echo YII_DEBUG ? $owner->versionInfo : ''?> <?php echo YII_DEBUG ? $context->versionInfo : ''?>
</div> </div>
</div> </div>
......
...@@ -214,7 +214,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ...@@ -214,7 +214,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
public function setSavePath($value) public function setSavePath($value)
{ {
$path = Yii::getAlias($value); $path = Yii::getAlias($value);
if ($path !== false && is_dir($path)) { if (is_dir($path)) {
session_save_path($path); session_save_path($path);
} else { } else {
throw new InvalidParamException("Session save path is not a valid directory: $value"); throw new InvalidParamException("Session save path is not a valid directory: $value");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment