Commit e7a5e8ab by Qiang Xue

...

parent 6f528bac
......@@ -218,6 +218,7 @@ class YiiBase
* - a URL (e.g. `http://www.yiiframework.com`)
* - a path alias (e.g. `@yii/base`). In this case, the path alias will be converted into the
* actual path first by calling [[getAlias]].
* @throws \yii\base\Exception if $path is an invalid alias
* @see getAlias
*/
public static function setAlias($alias, $path)
......
......@@ -118,7 +118,9 @@ class Application extends Module
\Yii::$application = $this;
$this->id = $id;
$this->setBasePath($basePath);
\Yii::setAlias('application', $this->getBasePath());
\Yii::$aliases['@application'] = $this->getBasePath();
\Yii::$aliases['@entry'] = dirname($_SERVER['SCRIPT_FILENAME']);
\Yii::$aliases['@www'] = '';
$this->registerCoreComponents();
}
......
<?php
/**
* Theme class file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2012 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* Theme represents an application theme.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Theme extends ApplicationComponent
{
private $_name;
private $_basePath;
private $_baseUrl;
/**
* Constructor.
* @param string $name name of the theme
* @param string $basePath base theme path
* @param string $baseUrl base theme URL
*/
public function __construct($name, $basePath, $baseUrl)
{
$this->_name = $name;
$this->_baseUrl = $baseUrl;
$this->_basePath = $basePath;
}
/**
* @return string theme name
*/
public function getName()
{
return $this->_name;
}
/**
* @return string the relative URL to the theme folder (without ending slash)
*/
public function getBaseUrl()
{
return $this->_baseUrl;
}
/**
* @return string the file path to the theme folder
*/
public function getBasePath()
{
return $this->_basePath;
}
/**
* @return string the path for controller views. Defaults to 'ThemeRoot/views'.
*/
public function getViewPath()
{
return $this->_basePath . DIRECTORY_SEPARATOR . 'views';
}
/**
* Finds the view file for the specified controller's view.
* @param CController $controller the controller
* @param string $viewName the view name
* @return string the view file path. False if the file does not exist.
*/
public function getViewFile($controller, $viewName)
{
$moduleViewPath = $this->getViewPath();
if (($module = $controller->getModule()) !== null)
{
$moduleViewPath .= '/' . $module->getId();
}
return $controller->resolveViewFile($viewName, $this->getViewPath() . '/' . $controller->getUniqueId(), $this->getViewPath(), $moduleViewPath);
}
/**
* Finds the layout file for the specified controller's layout.
* @param CController $controller the controller
* @param string $layoutName the layout name
* @return string the layout file path. False if the file does not exist.
*/
public function getLayoutFile($controller, $layoutName)
{
$moduleViewPath = $basePath = $this->getViewPath();
$module = $controller->getModule();
if (empty($layoutName)) {
while ($module !== null) {
if ($module->layout === false)
return false;
if (!empty($module->layout))
break;
$module = $module->getParentModule();
}
if ($module === null)
$layoutName = Yii::app()->layout;
else {
$layoutName = $module->layout;
$moduleViewPath .= '/' . $module->getId();
}
}
else if ($module !== null)
$moduleViewPath .= '/' . $module->getId();
return $controller->resolveViewFile($layoutName, $moduleViewPath . '/layouts', $basePath, $moduleViewPath);
}
}
<?php
/**
* ThemeManager class file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2012 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* ThemeManager manages the themes for the Web application.
*
* A theme is a collection of view/layout files and resource files
* (e.g. css, image, js files). When a theme is active, {@link CController}
* will look for the specified view/layout under the theme folder first.
* The corresponding view/layout files will be used if the theme provides them.
* Otherwise, the default view/layout files will be used.
*
* By default, each theme is organized as a directory whose name is the theme name.
* All themes are located under the "WebRootPath/themes" directory.
*
* To activate a theme, set the {@link CWebApplication::setTheme theme} property
* to be the name of that theme.
*
* Since a self-contained theme often contains resource files that are made
* Web accessible, please make sure the view/layout files are protected from Web access.
*
* @property array $themeNames List of available theme names.
* @property string $basePath The base path for all themes. Defaults to "WebRootPath/themes".
* @property string $baseUrl The base URL for all themes. Defaults to "/WebRoot/themes".
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class ThemeManager extends ApplicationComponent
{
/**
* default themes base path
*/
const DEFAULT_BASEPATH = 'themes';
/**
* @var string the name of the theme class for representing a theme.
* Defaults to {@link Theme}. This can also be a class name in dot syntax.
*/
public $themeClass = 'Theme';
/**
* @var string the base path containing all themes. Defaults to '@entry/themes'.
*/
public $basePath = '@entry/themes';
/**
* @var string the base URL for all themes. Defaults to "@www/themes".
*/
public $baseUrl = '@www/themes';
/**
* @param string $name name of the theme to be retrieved
* @return Theme the theme retrieved. Null if the theme does not exist.
*/
public function getTheme($name)
{
$themePath = $this->getBasePath() . DIRECTORY_SEPARATOR . $name;
if (is_dir($themePath)) {
$class = Yii::import($this->themeClass, true);
return new $class($name, $themePath, $this->getBaseUrl() . '/' . $name);
} else {
return null;
}
}
/**
* @return array list of available theme names
*/
public function getThemeNames()
{
static $themes;
if ($themes === null) {
$themes = array();
$basePath = $this->getBasePath();
$folder = @opendir($basePath);
while (($file = @readdir($folder)) !== false) {
if ($file !== '.' && $file !== '..' && $file !== '.svn' && $file !== '.gitignore' && is_dir($basePath . DIRECTORY_SEPARATOR . $file)) {
$themes[] = $file;
}
}
closedir($folder);
sort($themes);
}
return $themes;
}
}
......@@ -10,7 +10,6 @@
namespace yii\base;
use yii\util\FileHelper;
use yii\util\ArrayHelper;
/**
* @author Qiang Xue <qiang.xue@gmail.com>
......@@ -47,7 +46,7 @@ class View extends Component
/**
* Renders a view.
*
* The method first identifies the actual view file corresponding to the specified view.
* The method first finds the actual view file corresponding to the specified view.
* 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.
*
......@@ -58,36 +57,57 @@ class View extends Component
* 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 a path alias or a path relative to [[basePath]].
* @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
* @throws Exception if the view file cannot be found
*/
public function render($view, $params = array())
{
$file = $this->findViewFile($view);
if ($file !== false) {
$this->renderFile($file, $params);
return $this->renderFile($file, $params);
} else {
throw new Exception("Unable to find the view file for view '$view'.");
}
}
/**
* Renders a view file.
* @param string $file the view file path
* @param array $params the parameters to be extracted and made available in the view file
* @return string the rendering result
*/
public function renderFile($file, $params = array())
{
$this->renderFileInternal($file, $params);
return $this->renderFileInternal($file, $params);
}
public function createWidget($class, $properties = array())
{
$properties['class'] = $class;
return \Yii::createObject($properties, $this->owner);
}
public function widget($class, $properties = array())
{
$widget = $this->createWidget($class, $properties);
$widget->run();
echo $widget->run();
return $widget;
}
/**
* @var Widget[] the widgets that are currently not ended
*/
private $_widgetStack = array();
/**
* Begins a widget.
* @param string $class the widget class
* @param array $properties the initial property values of the widget
* @return Widget the widget instance
*/
public function beginWidget($class, $properties = array())
{
$widget = $this->createWidget($class, $properties);
......@@ -95,43 +115,34 @@ class View extends Component
return $widget;
}
/**
* Ends a widget.
* Note that the rendering result of the widget is directly echoed out.
* If you want to capture the rendering result of a widget, you may use
* [[createWidget()]] and [[Widget::run()]].
* @return Widget the widget instance
* @throws Exception if [[beginWidget()]] and [[endWidget()]] calls are not properly nested
*/
public function endWidget()
{
if (($widget = array_pop($this->_widgetStack)) !== null) {
$widget->run();
echo $widget->run();
return $widget;
} else {
throw new Exception("Unmatched beginWidget() and endWidget() calls.");
}
}
public function createWidget($class, $properties = array())
{
$properties['class'] = $class;
// todo: widget skin should be something global, similar to theme
if ($this->enableSkin) {
if ($this->skinnableWidgets === null || in_array($class, $this->skinnableWidgets)) {
$skinName = isset($properties['skin']) ? $properties['skin'] : 'default';
if ($skinName !== false && ($skin = $this->getSkin($class, $skinName)) !== array()) {
$properties = $properties === array() ? $skin : ArrayHelper::merge($skin, $properties);
}
}
}
return \Yii::createObject($properties, $this->owner);
}
/**
* Begins recording a clip.
* This method is a shortcut to beginning [[yii\web\widgets\ClipWidget]]
* This method is a shortcut to beginning [[yii\widgets\Clip]]
* @param string $id the clip ID.
* @param array $properties initial property values for [[yii\web\widgets\ClipWidget]]
* @param array $properties initial property values for [[yii\widgets\Clip]]
*/
public function beginClip($id, $properties = array())
{
$properties['id'] = $id;
$this->beginWidget('yii\web\widgets\ClipWidget', $properties);
$this->beginWidget('yii\widgets\Clip', $properties);
}
/**
......@@ -158,14 +169,14 @@ class View extends Component
* ~~~
*
* @param string $id a unique ID identifying the fragment to be cached.
* @param array $properties initial property values for [[yii\web\widgets\OutputCache]]
* @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\web\widgets\OutputCache', $properties);
$cache = $this->beginWidget('yii\widgets\OutputCache', $properties);
if ($cache->getIsContentCached()) {
$this->endCache();
return false;
......@@ -195,11 +206,11 @@ class View extends Component
* {@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\web\widgets\ContentDecorator
* @see yii\widgets\ContentDecorator
*/
public function beginContent($view = null, $params = array())
public function beginContent($view, $params = array())
{
$this->beginWidget('yii\web\widgets\ContentDecorator', array(
$this->beginWidget('yii\widgets\ContentDecorator', array(
'view' => $view,
'params' => $params,
));
......@@ -214,17 +225,32 @@ class View extends Component
$this->endWidget();
}
/**
* Renders a view file.
* This method will extract the given parameters and include the view file.
* It captures the output of the included view file and returns it as a string.
* @param string $_file_ the view file.
* @param array $_params_ the parameters (name-value pairs) that will be extracted and made available in the view file.
* @return string the rendering result
*/
protected function renderFileInternal($_file_, $_params_ = array())
{
ob_start();
ob_implicit_flush(false);
extract($_params_, EXTR_OVERWRITE);
require($_file_);
return ob_get_clean();
}
/**
* Finds the view file based on the given view name.
* @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|boolean the view file if it exists. False if the view file cannot be found.
*/
public function findViewFile($view)
{
if ($view[0] === '/') {
throw new Exception('The view name "$view" should not start with a slash "/".');
}
$view = ltrim($view, '/');
if (($extension = FileHelper::getExtension($view)) === '') {
$view .= '.php';
......
<?php
/**
* Widget class file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2012 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* Widget is the base class for widgets.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Widget extends Component implements Initable
{
/**
* @var mixed the name of the skin to be used by this widget. Defaults to 'default'.
* If this is set as false, no skin will be applied to this widget.
*/
public $skin = 'default';
/**
* @var Widget|Controller the owner/creator of this widget. It could be either a widget or a controller.
*/
public $owner;
/**
* @var string id of the widget.
*/
private $_id;
/**
* @var integer a counter used to generate IDs for widgets.
*/
private static $_counter = 0;
/**
* Constructor.
* @param Widget|Controller $owner owner/creator of this widget.
*/
public function __construct($owner)
{
$this->owner = $owner;
}
/**
* Returns the ID of the widget.
* @param boolean $autoGenerate whether to generate an ID if it is not set previously
* @return string ID of the widget.
*/
public function getId($autoGenerate = true)
{
if ($autoGenerate && $this->_id === null) {
$this->_id = 'yw' . self::$_counter++;
}
return $this->_id;
}
/**
* Sets the ID of the widget.
* @param string $value id of the widget.
*/
public function setId($value)
{
$this->_id = $value;
}
/**
* Initializes the widget.
*/
public function init()
{
}
/**
* Executes the widget.
* @return string the rendering result of the widget
*/
public function run()
{
}
/**
* Renders a view.
*
* The method first finds the actual view file corresponding to the specified view.
* 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.
*
* 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())
{
return $this->createView()->render($view, $params);
}
/**
* @return View
*/
public function createView()
{
$view = new View;
if (($theme = \Yii::$application->getTheme()) !== null) {
$view->basePath[] = $theme->getViewPath() . DIRECTORY_SEPARATOR . str_replace('\\', '_', get_class($this));
}
$class = new \ReflectionClass($this);
$view->basePath[] = dirname($class->getFileName()) . DIRECTORY_SEPARATOR . 'views';
return $view;
}
}
\ No newline at end of file
......@@ -103,6 +103,7 @@ class Command extends \yii\base\Component
* this may improve performance.
* For SQL statement with binding parameters, this method is invoked
* automatically.
* @throws Exception if there is any DB error
*/
public function prepare()
{
......@@ -221,7 +222,7 @@ class Command extends \yii\base\Component
}
\Yii::trace("Executing SQL: {$sql}{$paramLog}", __CLASS__);
//echo $sql . "\n\n";
try {
if ($this->connection->enableProfiling) {
\Yii::beginProfile(__METHOD__ . "($sql)", __CLASS__);
......
<?php
/**
* Application class file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2012 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
/**
* Application is the base class for all application classes.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Application extends \yii\base\Application
{
/**
* Processes the request.
* @return integer the exit status of the controller action (0 means normal, non-zero values mean abnormal)
*/
public function processRequest()
{
$route = $this->resolveRequest();
return $this->runController($route, null);
}
protected function resolveRequest()
{
return array();
}
}
<?php
/**
* CAssetManager class file.
*
* @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/
*/
/**
* CAssetManager is a Web application component that manages private files (called assets) and makes them accessible by Web clients.
*
* It achieves this goal by copying assets to a Web-accessible directory
* and returns the corresponding URL for accessing them.
*
* To publish an asset, simply call {@link publish()}.
*
* The Web-accessible directory holding the published files is specified
* by {@link setBasePath basePath}, which defaults to the "assets" directory
* under the directory containing the application entry script file.
* The property {@link setBaseUrl baseUrl} refers to the URL for accessing
* the {@link setBasePath basePath}.
*
* @property string $basePath The root directory storing the published asset files. Defaults to 'WebRoot/assets'.
* @property string $baseUrl The base url that the published asset files can be accessed.
* Note, the ending slashes are stripped off. Defaults to '/AppBaseUrl/assets'.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id$
* @package system.web
* @since 1.0
*/
class CAssetManager extends CApplicationComponent
{
/**
* Default web accessible base path for storing private files
*/
const DEFAULT_BASEPATH='assets';
/**
* @var boolean whether to use symbolic link to publish asset files. Defaults to false, meaning
* asset files are copied to public folders. Using symbolic links has the benefit that the published
* assets will always be consistent with the source assets. This is especially useful during development.
*
* However, there are special requirements for hosting environments in order to use symbolic links.
* In particular, symbolic links are supported only on Linux/Unix, and Windows Vista/2008 or greater.
* The latter requires PHP 5.3 or greater.
*
* Moreover, some Web servers need to be properly configured so that the linked assets are accessible
* to Web users. For example, for Apache Web server, the following configuration directive should be added
* for the Web folder:
* <pre>
* Options FollowSymLinks
* </pre>
*
* @since 1.1.5
*/
public $linkAssets=false;
/**
* @var array list of directories and files which should be excluded from the publishing process.
* Defaults to exclude '.svn' and '.gitignore' files only. This option has no effect if {@link linkAssets} is enabled.
* @since 1.1.6
**/
public $excludeFiles=array('.svn','.gitignore');
/**
* @var integer the permission to be set for newly generated asset files.
* This value will be used by PHP chmod function.
* Defaults to 0666, meaning the file is read-writable by all users.
* @since 1.1.8
*/
public $newFileMode=0666;
/**
* @var integer the permission to be set for newly generated asset directories.
* This value will be used by PHP chmod function.
* Defaults to 0777, meaning the directory can be read, written and executed by all users.
* @since 1.1.8
*/
public $newDirMode=0777;
/**
* @var string base web accessible path for storing private files
*/
private $_basePath;
/**
* @var string base URL for accessing the publishing directory.
*/
private $_baseUrl;
/**
* @var array published assets
*/
private $_published=array();
/**
* @return string the root directory storing the published asset files. Defaults to 'WebRoot/assets'.
*/
public function getBasePath()
{
if($this->_basePath===null)
{
$request=Yii::app()->getRequest();
$this->setBasePath(dirname($request->getScriptFile()).DIRECTORY_SEPARATOR.self::DEFAULT_BASEPATH);
}
return $this->_basePath;
}
/**
* Sets the root directory storing published asset files.
* @param string $value the root directory storing published asset files
* @throws CException if the base path is invalid
*/
public function setBasePath($value)
{
if(($basePath=realpath($value))!==false && is_dir($basePath) && is_writable($basePath))
$this->_basePath=$basePath;
else
throw new CException(Yii::t('yii','CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.',
array('{path}'=>$value)));
}
/**
* @return string the base url that the published asset files can be accessed.
* Note, the ending slashes are stripped off. Defaults to '/AppBaseUrl/assets'.
*/
public function getBaseUrl()
{
if($this->_baseUrl===null)
{
$request=Yii::app()->getRequest();
$this->setBaseUrl($request->getBaseUrl().'/'.self::DEFAULT_BASEPATH);
}
return $this->_baseUrl;
}
/**
* @param string $value the base url that the published asset files can be accessed
*/
public function setBaseUrl($value)
{
$this->_baseUrl=rtrim($value,'/');
}
/**
* Publishes a file or a directory.
* This method will copy the specified asset to a web accessible directory
* and return the URL for accessing the published asset.
* <ul>
* <li>If the asset is a file, its file modification time will be checked
* to avoid unnecessary file copying;</li>
* <li>If the asset is a directory, all files and subdirectories under it will
* be published recursively. Note, in case $forceCopy is false the method only checks the
* existence of the target directory to avoid repetitive copying.</li>
* </ul>
*
* Note: On rare scenario, a race condition can develop that will lead to a
* one-time-manifestation of a non-critical problem in the creation of the directory
* that holds the published assets. This problem can be avoided altogether by 'requesting'
* in advance all the resources that are supposed to trigger a 'publish()' call, and doing
* that in the application deployment phase, before system goes live. See more in the following
* discussion: http://code.google.com/p/yii/issues/detail?id=2579
*
* @param string $path the asset (file or directory) to be published
* @param boolean $hashByName whether the published directory should be named as the hashed basename.
* If false, the name will be the hash taken from dirname of the path being published and path mtime.
* Defaults to false. Set true if the path being published is shared among
* different extensions.
* @param integer $level level of recursive copying when the asset is a directory.
* Level -1 means publishing all subdirectories and files;
* Level 0 means publishing only the files DIRECTLY under the directory;
* level N means copying those directories that are within N levels.
* @param boolean $forceCopy whether we should copy the asset file or directory even if it is already published before.
* This parameter is set true mainly during development stage when the original
* assets are being constantly changed. The consequence is that the performance
* is degraded, which is not a concern during development, however.
* This parameter has been available since version 1.1.2.
* @return string an absolute URL to the published asset
* @throws CException if the asset to be published does not exist.
*/
public function publish($path,$hashByName=false,$level=-1,$forceCopy=false)
{
if(isset($this->_published[$path]))
return $this->_published[$path];
else if(($src=realpath($path))!==false)
{
if(is_file($src))
{
$dir=$this->hash($hashByName ? basename($src) : dirname($src).filemtime($src));
$fileName=basename($src);
$dstDir=$this->getBasePath().DIRECTORY_SEPARATOR.$dir;
$dstFile=$dstDir.DIRECTORY_SEPARATOR.$fileName;
if($this->linkAssets)
{
if(!is_file($dstFile))
{
if(!is_dir($dstDir))
{
mkdir($dstDir);
@chmod($dstDir, $this->newDirMode);
}
symlink($src,$dstFile);
}
}
else if(@filemtime($dstFile)<@filemtime($src))
{
if(!is_dir($dstDir))
{
mkdir($dstDir);
@chmod($dstDir, $this->newDirMode);
}
copy($src,$dstFile);
@chmod($dstFile, $this->newFileMode);
}
return $this->_published[$path]=$this->getBaseUrl()."/$dir/$fileName";
}
else if(is_dir($src))
{
$dir=$this->hash($hashByName ? basename($src) : $src.filemtime($src));
$dstDir=$this->getBasePath().DIRECTORY_SEPARATOR.$dir;
if($this->linkAssets)
{
if(!is_dir($dstDir))
symlink($src,$dstDir);
}
else if(!is_dir($dstDir) || $forceCopy)
{
CFileHelper::copyDirectory($src,$dstDir,array(
'exclude'=>$this->excludeFiles,
'level'=>$level,
'newDirMode'=>$this->newDirMode,
'newFileMode'=>$this->newFileMode,
));
}
return $this->_published[$path]=$this->getBaseUrl().'/'.$dir;
}
}
throw new CException(Yii::t('yii','The asset "{asset}" to be published does not exist.',
array('{asset}'=>$path)));
}
/**
* Returns the published path of a file path.
* This method does not perform any publishing. It merely tells you
* if the file or directory is published, where it will go.
* @param string $path directory or file path being published
* @param boolean $hashByName whether the published directory should be named as the hashed basename.
* If false, the name will be the hash taken from dirname of the path being published and path mtime.
* Defaults to false. Set true if the path being published is shared among
* different extensions.
* @return string the published file path. False if the file or directory does not exist
*/
public function getPublishedPath($path,$hashByName=false)
{
if(($path=realpath($path))!==false)
{
$base=$this->getBasePath().DIRECTORY_SEPARATOR;
if(is_file($path))
return $base . $this->hash($hashByName ? basename($path) : dirname($path).filemtime($path)) . DIRECTORY_SEPARATOR . basename($path);
else
return $base . $this->hash($hashByName ? basename($path) : $path.filemtime($path));
}
else
return false;
}
/**
* Returns the URL of a published file path.
* This method does not perform any publishing. It merely tells you
* if the file path is published, what the URL will be to access it.
* @param string $path directory or file path being published
* @param boolean $hashByName whether the published directory should be named as the hashed basename.
* If false, the name will be the hash taken from dirname of the path being published and path mtime.
* Defaults to false. Set true if the path being published is shared among
* different extensions.
* @return string the published URL for the file or directory. False if the file or directory does not exist.
*/
public function getPublishedUrl($path,$hashByName=false)
{
if(isset($this->_published[$path]))
return $this->_published[$path];
if(($path=realpath($path))!==false)
{
if(is_file($path))
return $this->getBaseUrl().'/'.$this->hash($hashByName ? basename($path) : dirname($path).filemtime($path)).'/'.basename($path);
else
return $this->getBaseUrl().'/'.$this->hash($hashByName ? basename($path) : $path.filemtime($path));
}
else
return false;
}
/**
* Generate a CRC32 hash for the directory path. Collisions are higher
* than MD5 but generates a much smaller hash string.
* @param string $path string to be hashed.
* @return string hashed string.
*/
protected function hash($path)
{
return sprintf('%x',crc32($path.Yii::getVersion()));
}
}
<?php
/**
* Controller class file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2012 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
use yii\base\Action;
use yii\base\Exception;
use yii\base\HttpException;
/**
* Controller is the base class of Web controllers.
*
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Controller extends \yii\base\Controller
{
/**
* Returns the request parameters that will be used for action parameter binding.
* Default implementation simply returns an empty array.
* Child classes may override this method to customize the parameters to be provided
* for action parameter binding (e.g. `$_GET`).
* @return array the request parameters (name-value pairs) to be used for action parameter binding
*/
public function getActionParams()
{
return $_GET;
}
/**
* This method is invoked when the request parameters do not satisfy the requirement of the specified action.
* The default implementation will throw an exception.
* @param Action $action the action being executed
* @param Exception $exception the exception about the invalid parameters
* @throws HttpException $exception a 400 HTTP exception
*/
public function invalidActionParams($action, $exception)
{
throw new HttpException(400, \Yii::t('yii', 'Your request is invalid.'));
}
}
\ No newline at end of file
<?php
/**
* CHttpCookie class file.
*
* @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/
*/
/**
* A CHttpCookie instance stores a single cookie, including the cookie name, value, domain, path, expire, and secure.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id$
* @package system.web
* @since 1.0
*/
class CHttpCookie extends CComponent
{
/**
* @var string name of the cookie
*/
public $name;
/**
* @var string value of the cookie
*/
public $value='';
/**
* @var string domain of the cookie
*/
public $domain='';
/**
* @var integer the timestamp at which the cookie expires. This is the server timestamp. Defaults to 0, meaning "until the browser is closed".
*/
public $expire=0;
/**
* @var string the path on the server in which the cookie will be available on. The default is '/'.
*/
public $path='/';
/**
* @var boolean whether cookie should be sent via secure connection
*/
public $secure=false;
/**
* @var boolean whether the cookie should be accessible only through the HTTP protocol.
* By setting this property to true, the cookie will not be accessible by scripting languages,
* such as JavaScript, which can effectly help to reduce identity theft through XSS attacks.
* Note, this property is only effective for PHP 5.2.0 or above.
*/
public $httpOnly=false;
/**
* Constructor.
* @param string $name name of this cookie
* @param string $value value of this cookie
*/
public function __construct($name,$value)
{
$this->name=$name;
$this->value=$value;
}
}
<?php
/**
* CPagination class file.
*
* @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/
*/
/**
* CPagination represents information relevant to pagination.
*
* When data needs to be rendered in multiple pages, we can use CPagination to
* represent information such as {@link getItemCount total item count},
* {@link getPageSize page size}, {@link getCurrentPage current page}, etc.
* These information can be passed to {@link CBasePager pagers} to render
* pagination buttons or links.
*
* Example:
*
* Controller action:
* <pre>
* function actionIndex(){
* $criteria=new CDbCriteria();
* $count=Article::model()->count($criteria);
* $pages=new CPagination($count);
*
* // results per page
* $pages->pageSize=10;
* $pages->applyLimit($criteria);
* $models=Article::model()->findAll($criteria);
*
* $this->render('index', array(
* 'models' => $models,
* 'pages' => $pages
* ));
* }
* </pre>
*
* View:
* <pre>
* <?php foreach($models as $model): ?>
* // display a model
* <?php endforeach; ?>
*
* // display pagination
* <?php $this->widget('CLinkPager', array(
* 'pages' => $pages,
* )) ?>
* </pre>
*
* @property integer $pageSize Number of items in each page. Defaults to 10.
* @property integer $itemCount Total number of items. Defaults to 0.
* @property integer $pageCount Number of pages.
* @property integer $currentPage The zero-based index of the current page. Defaults to 0.
* @property integer $offset The offset of the data. This may be used to set the
* OFFSET value for a SQL statement for fetching the current page of data.
* @property integer $limit The limit of the data. This may be used to set the
* LIMIT value for a SQL statement for fetching the current page of data.
* This returns the same value as {@link pageSize}.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id$
* @package system.web
* @since 1.0
*/
class CPagination extends CComponent
{
/**
* The default page size.
*/
const DEFAULT_PAGE_SIZE=10;
/**
* @var string name of the GET variable storing the current page index. Defaults to 'page'.
*/
public $pageVar='page';
/**
* @var string the route (controller ID and action ID) for displaying the paged contents.
* Defaults to empty string, meaning using the current route.
*/
public $route='';
/**
* @var array of parameters (name=>value) that should be used instead of GET when generating pagination URLs.
* Defaults to null, meaning using the currently available GET parameters.
*/
public $params;
/**
* @var boolean whether to ensure {@link currentPage} is returning a valid page number.
* When this property is true, the value returned by {@link currentPage} will always be between
* 0 and ({@link pageCount}-1). Because {@link pageCount} relies on the correct value of {@link itemCount},
* it means you must have knowledge about the total number of data items when you want to access {@link currentPage}.
* This is fine for SQL-based queries, but may not be feasible for other kinds of queries (e.g. MongoDB).
* In those cases, you may set this property to be false to skip the validation (you may need to validate yourself then).
* Defaults to true.
* @since 1.1.4
*/
public $validateCurrentPage=true;
private $_pageSize=self::DEFAULT_PAGE_SIZE;
private $_itemCount=0;
private $_currentPage;
/**
* Constructor.
* @param integer $itemCount total number of items.
*/
public function __construct($itemCount=0)
{
$this->setItemCount($itemCount);
}
/**
* @return integer number of items in each page. Defaults to 10.
*/
public function getPageSize()
{
return $this->_pageSize;
}
/**
* @param integer $value number of items in each page
*/
public function setPageSize($value)
{
if(($this->_pageSize=$value)<=0)
$this->_pageSize=self::DEFAULT_PAGE_SIZE;
}
/**
* @return integer total number of items. Defaults to 0.
*/
public function getItemCount()
{
return $this->_itemCount;
}
/**
* @param integer $value total number of items.
*/
public function setItemCount($value)
{
if(($this->_itemCount=$value)<0)
$this->_itemCount=0;
}
/**
* @return integer number of pages
*/
public function getPageCount()
{
return (int)(($this->_itemCount+$this->_pageSize-1)/$this->_pageSize);
}
/**
* @param boolean $recalculate whether to recalculate the current page based on the page size and item count.
* @return integer the zero-based index of the current page. Defaults to 0.
*/
public function getCurrentPage($recalculate=true)
{
if($this->_currentPage===null || $recalculate)
{
if(isset($_GET[$this->pageVar]))
{
$this->_currentPage=(int)$_GET[$this->pageVar]-1;
if($this->validateCurrentPage)
{
$pageCount=$this->getPageCount();
if($this->_currentPage>=$pageCount)
$this->_currentPage=$pageCount-1;
}
if($this->_currentPage<0)
$this->_currentPage=0;
}
else
$this->_currentPage=0;
}
return $this->_currentPage;
}
/**
* @param integer $value the zero-based index of the current page.
*/
public function setCurrentPage($value)
{
$this->_currentPage=$value;
$_GET[$this->pageVar]=$value+1;
}
/**
* Creates the URL suitable for pagination.
* This method is mainly called by pagers when creating URLs used to
* perform pagination. The default implementation is to call
* the controller's createUrl method with the page information.
* You may override this method if your URL scheme is not the same as
* the one supported by the controller's createUrl method.
* @param CController $controller the controller that will create the actual URL
* @param integer $page the page that the URL should point to. This is a zero-based index.
* @return string the created URL
*/
public function createPageUrl($controller,$page)
{
$params=$this->params===null ? $_GET : $this->params;
if($page>0) // page 0 is the default
$params[$this->pageVar]=$page+1;
else
unset($params[$this->pageVar]);
return $controller->createUrl($this->route,$params);
}
/**
* Applies LIMIT and OFFSET to the specified query criteria.
* @param CDbCriteria $criteria the query criteria that should be applied with the limit
*/
public function applyLimit($criteria)
{
$criteria->limit=$this->getLimit();
$criteria->offset=$this->getOffset();
}
/**
* @return integer the offset of the data. This may be used to set the
* OFFSET value for a SQL statement for fetching the current page of data.
* @since 1.1.0
*/
public function getOffset()
{
return $this->getCurrentPage()*$this->getPageSize();
}
/**
* @return integer the limit of the data. This may be used to set the
* LIMIT value for a SQL statement for fetching the current page of data.
* This returns the same value as {@link pageSize}.
* @since 1.1.0
*/
public function getLimit()
{
return $this->getPageSize();
}
}
\ No newline at end of file
<?php
/**
* CHttpRequest and CCookieCollection class file.
*
* @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/
*/
/**
* CHttpRequest encapsulates the $_SERVER variable and resolves its inconsistency among different Web servers.
*
* CHttpRequest also manages the cookies sent from and sent to the user.
* By setting {@link enableCookieValidation} to true,
* cookies sent from the user will be validated to see if they are tampered.
* The property {@link getCookies cookies} returns the collection of cookies.
* For more details, see {@link CCookieCollection}.
*
* CHttpRequest is a default application component loaded by {@link CWebApplication}. It can be
* accessed via {@link CWebApplication::getRequest()}.
*
* @property string $url Part of the request URL after the host info.
* @property string $hostInfo Schema and hostname part (with port number if needed) of the request URL (e.g. http://www.yiiframework.com).
* @property string $baseUrl The relative URL for the application.
* @property string $scriptUrl The relative URL of the entry script.
* @property string $pathInfo Part of the request URL that is after the entry script and before the question mark.
* Note, the returned pathinfo is decoded starting from 1.1.4.
* Prior to 1.1.4, whether it is decoded or not depends on the server configuration
* (in most cases it is not decoded).
* @property string $requestUri The request URI portion for the currently requested URL.
* @property string $queryString Part of the request URL that is after the question mark.
* @property boolean $isSecureConnection If the request is sent via secure channel (https).
* @property string $requestType Request type, such as GET, POST, HEAD, PUT, DELETE.
* @property boolean $isPostRequest Whether this is a POST request.
* @property boolean $isDeleteRequest Whether this is a DELETE request.
* @property boolean $isPutRequest Whether this is a PUT request.
* @property boolean $isAjaxRequest Whether this is an AJAX (XMLHttpRequest) request.
* @property boolean $isFlashRequest Whether this is an Adobe Flash or Adobe Flex request.
* @property string $serverName Server name.
* @property integer $serverPort Server port number.
* @property string $urlReferrer URL referrer, null if not present.
* @property string $userAgent User agent, null if not present.
* @property string $userHostAddress User IP address.
* @property string $userHost User host name, null if cannot be determined.
* @property string $scriptFile Entry script file path (processed w/ realpath()).
* @property array $browser User browser capabilities.
* @property string $acceptTypes User browser accept types, null if not present.
* @property integer $port Port number for insecure requests.
* @property integer $securePort Port number for secure requests.
* @property CCookieCollection $cookies The cookie collection.
* @property string $preferredLanguage The user preferred language.
* @property string $csrfToken The random token for CSRF validation.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id$
* @package system.web
* @since 1.0
*/
class CHttpRequest extends CApplicationComponent
{
/**
* @var boolean whether cookies should be validated to ensure they are not tampered. Defaults to false.
*/
public $enableCookieValidation=false;
/**
* @var boolean whether to enable CSRF (Cross-Site Request Forgery) validation. Defaults to false.
* By setting this property to true, forms submitted to an Yii Web application must be originated
* from the same application. If not, a 400 HTTP exception will be raised.
* Note, this feature requires that the user client accepts cookie.
* You also need to use {@link CHtml::form} or {@link CHtml::statefulForm} to generate
* the needed HTML forms in your pages.
* @see http://seclab.stanford.edu/websec/csrf/csrf.pdf
*/
public $enableCsrfValidation=false;
/**
* @var string the name of the token used to prevent CSRF. Defaults to 'YII_CSRF_TOKEN'.
* This property is effectively only when {@link enableCsrfValidation} is true.
*/
public $csrfTokenName='YII_CSRF_TOKEN';
/**
* @var array the property values (in name-value pairs) used to initialize the CSRF cookie.
* Any property of {@link CHttpCookie} may be initialized.
* This property is effective only when {@link enableCsrfValidation} is true.
*/
public $csrfCookie;
private $_requestUri;
private $_pathInfo;
private $_scriptFile;
private $_scriptUrl;
private $_hostInfo;
private $_baseUrl;
private $_cookies;
private $_preferredLanguage;
private $_csrfToken;
private $_deleteParams;
private $_putParams;
/**
* Initializes the application component.
* This method overrides the parent implementation by preprocessing
* the user request data.
*/
public function init()
{
parent::init();
$this->normalizeRequest();
}
/**
* Normalizes the request data.
* This method strips off slashes in request data if get_magic_quotes_gpc() returns true.
* It also performs CSRF validation if {@link enableCsrfValidation} is true.
*/
protected function normalizeRequest()
{
// normalize request
if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc())
{
if(isset($_GET))
$_GET=$this->stripSlashes($_GET);
if(isset($_POST))
$_POST=$this->stripSlashes($_POST);
if(isset($_REQUEST))
$_REQUEST=$this->stripSlashes($_REQUEST);
if(isset($_COOKIE))
$_COOKIE=$this->stripSlashes($_COOKIE);
}
if($this->enableCsrfValidation)
Yii::app()->attachEventHandler('onBeginRequest',array($this,'validateCsrfToken'));
}
/**
* Strips slashes from input data.
* This method is applied when magic quotes is enabled.
* @param mixed $data input data to be processed
* @return mixed processed data
*/
public function stripSlashes(&$data)
{
return is_array($data)?array_map(array($this,'stripSlashes'),$data):stripslashes($data);
}
/**
* Returns the named GET or POST parameter value.
* If the GET or POST parameter does not exist, the second parameter to this method will be returned.
* If both GET and POST contains such a named parameter, the GET parameter takes precedence.
* @param string $name the GET parameter name
* @param mixed $defaultValue the default parameter value if the GET parameter does not exist.
* @return mixed the GET parameter value
* @see getQuery
* @see getPost
*/
public function getParam($name,$defaultValue=null)
{
return isset($_GET[$name]) ? $_GET[$name] : (isset($_POST[$name]) ? $_POST[$name] : $defaultValue);
}
/**
* Returns the named GET parameter value.
* If the GET parameter does not exist, the second parameter to this method will be returned.
* @param string $name the GET parameter name
* @param mixed $defaultValue the default parameter value if the GET parameter does not exist.
* @return mixed the GET parameter value
* @see getPost
* @see getParam
*/
public function getQuery($name,$defaultValue=null)
{
return isset($_GET[$name]) ? $_GET[$name] : $defaultValue;
}
/**
* Returns the named POST parameter value.
* If the POST parameter does not exist, the second parameter to this method will be returned.
* @param string $name the POST parameter name
* @param mixed $defaultValue the default parameter value if the POST parameter does not exist.
* @return mixed the POST parameter value
* @see getParam
* @see getQuery
*/
public function getPost($name,$defaultValue=null)
{
return isset($_POST[$name]) ? $_POST[$name] : $defaultValue;
}
/**
* Returns the named DELETE parameter value.
* If the DELETE parameter does not exist or if the current request is not a DELETE request,
* the second parameter to this method will be returned.
* If the DELETE request was tunneled through POST via _method parameter, the POST parameter
* will be returned instead (available since version 1.1.11).
* @param string $name the DELETE parameter name
* @param mixed $defaultValue the default parameter value if the DELETE parameter does not exist.
* @return mixed the DELETE parameter value
* @since 1.1.7
*/
public function getDelete($name,$defaultValue=null)
{
if($this->getIsDeleteViaPostRequest())
return $this->getPost($name, $defaultValue);
if($this->_deleteParams===null)
$this->_deleteParams=$this->getIsDeleteRequest() ? $this->getRestParams() : array();
return isset($this->_deleteParams[$name]) ? $this->_deleteParams[$name] : $defaultValue;
}
/**
* Returns the named PUT parameter value.
* If the PUT parameter does not exist or if the current request is not a PUT request,
* the second parameter to this method will be returned.
* If the PUT request was tunneled through POST via _method parameter, the POST parameter
* will be returned instead (available since version 1.1.11).
* @param string $name the PUT parameter name
* @param mixed $defaultValue the default parameter value if the PUT parameter does not exist.
* @return mixed the PUT parameter value
* @since 1.1.7
*/
public function getPut($name,$defaultValue=null)
{
if($this->getIsPutViaPostReqest())
return $this->getPost($name, $defaultValue);
if($this->_putParams===null)
$this->_putParams=$this->getIsPutRequest() ? $this->getRestParams() : array();
return isset($this->_putParams[$name]) ? $this->_putParams[$name] : $defaultValue;
}
/**
* Returns the PUT or DELETE request parameters.
* @return array the request parameters
* @since 1.1.7
*/
protected function getRestParams()
{
$result=array();
if(function_exists('mb_parse_str'))
mb_parse_str(file_get_contents('php://input'), $result);
else
parse_str(file_get_contents('php://input'), $result);
return $result;
}
/**
* Returns the currently requested URL.
* This is the same as {@link getRequestUri}.
* @return string part of the request URL after the host info.
*/
public function getUrl()
{
return $this->getRequestUri();
}
/**
* Returns the schema and host part of the application URL.
* The returned URL does not have an ending slash.
* By default this is determined based on the user request information.
* You may explicitly specify it by setting the {@link setHostInfo hostInfo} property.
* @param string $schema schema to use (e.g. http, https). If empty, the schema used for the current request will be used.
* @return string schema and hostname part (with port number if needed) of the request URL (e.g. http://www.yiiframework.com)
* @see setHostInfo
*/
public function getHostInfo($schema='')
{
if($this->_hostInfo===null)
{
if($secure=$this->getIsSecureConnection())
$http='https';
else
$http='http';
if(isset($_SERVER['HTTP_HOST']))
$this->_hostInfo=$http.'://'.$_SERVER['HTTP_HOST'];
else
{
$this->_hostInfo=$http.'://'.$_SERVER['SERVER_NAME'];
$port=$secure ? $this->getSecurePort() : $this->getPort();
if(($port!==80 && !$secure) || ($port!==443 && $secure))
$this->_hostInfo.=':'.$port;
}
}
if($schema!=='')
{
$secure=$this->getIsSecureConnection();
if($secure && $schema==='https' || !$secure && $schema==='http')
return $this->_hostInfo;
$port=$schema==='https' ? $this->getSecurePort() : $this->getPort();
if($port!==80 && $schema==='http' || $port!==443 && $schema==='https')
$port=':'.$port;
else
$port='';
$pos=strpos($this->_hostInfo,':');
return $schema.substr($this->_hostInfo,$pos,strcspn($this->_hostInfo,':',$pos+1)+1).$port;
}
else
return $this->_hostInfo;
}
/**
* Sets the schema and host part of the application URL.
* This setter is provided in case the schema and hostname cannot be determined
* on certain Web servers.
* @param string $value the schema and host part of the application URL.
*/
public function setHostInfo($value)
{
$this->_hostInfo=rtrim($value,'/');
}
/**
* Returns the relative URL for the application.
* This is similar to {@link getScriptUrl scriptUrl} except that
* it does not have the script file name, and the ending slashes are stripped off.
* @param boolean $absolute whether to return an absolute URL. Defaults to false, meaning returning a relative one.
* @return string the relative URL for the application
* @see setScriptUrl
*/
public function getBaseUrl($absolute=false)
{
if($this->_baseUrl===null)
$this->_baseUrl=rtrim(dirname($this->getScriptUrl()),'\\/');
return $absolute ? $this->getHostInfo() . $this->_baseUrl : $this->_baseUrl;
}
/**
* Sets the relative URL for the application.
* By default the URL is determined based on the entry script URL.
* This setter is provided in case you want to change this behavior.
* @param string $value the relative URL for the application
*/
public function setBaseUrl($value)
{
$this->_baseUrl=$value;
}
/**
* Returns the relative URL of the entry script.
* The implementation of this method referenced Zend_Controller_Request_Http in Zend Framework.
* @return string the relative URL of the entry script.
*/
public function getScriptUrl()
{
if($this->_scriptUrl===null)
{
$scriptName=basename($_SERVER['SCRIPT_FILENAME']);
if(basename($_SERVER['SCRIPT_NAME'])===$scriptName)
$this->_scriptUrl=$_SERVER['SCRIPT_NAME'];
else if(basename($_SERVER['PHP_SELF'])===$scriptName)
$this->_scriptUrl=$_SERVER['PHP_SELF'];
else if(isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME'])===$scriptName)
$this->_scriptUrl=$_SERVER['ORIG_SCRIPT_NAME'];
else if(($pos=strpos($_SERVER['PHP_SELF'],'/'.$scriptName))!==false)
$this->_scriptUrl=substr($_SERVER['SCRIPT_NAME'],0,$pos).'/'.$scriptName;
else if(isset($_SERVER['DOCUMENT_ROOT']) && strpos($_SERVER['SCRIPT_FILENAME'],$_SERVER['DOCUMENT_ROOT'])===0)
$this->_scriptUrl=str_replace('\\','/',str_replace($_SERVER['DOCUMENT_ROOT'],'',$_SERVER['SCRIPT_FILENAME']));
else
throw new CException(Yii::t('yii','CHttpRequest is unable to determine the entry script URL.'));
}
return $this->_scriptUrl;
}
/**
* Sets the relative URL for the application entry script.
* This setter is provided in case the entry script URL cannot be determined
* on certain Web servers.
* @param string $value the relative URL for the application entry script.
*/
public function setScriptUrl($value)
{
$this->_scriptUrl='/'.trim($value,'/');
}
/**
* Returns the path info of the currently requested URL.
* This refers to the part that is after the entry script and before the question mark.
* The starting and ending slashes are stripped off.
* @return string part of the request URL that is after the entry script and before the question mark.
* Note, the returned pathinfo is decoded starting from 1.1.4.
* Prior to 1.1.4, whether it is decoded or not depends on the server configuration
* (in most cases it is not decoded).
* @throws CException if the request URI cannot be determined due to improper server configuration
*/
public function getPathInfo()
{
if($this->_pathInfo===null)
{
$pathInfo=$this->getRequestUri();
if(($pos=strpos($pathInfo,'?'))!==false)
$pathInfo=substr($pathInfo,0,$pos);
$pathInfo=$this->decodePathInfo($pathInfo);
$scriptUrl=$this->getScriptUrl();
$baseUrl=$this->getBaseUrl();
if(strpos($pathInfo,$scriptUrl)===0)
$pathInfo=substr($pathInfo,strlen($scriptUrl));
else if($baseUrl==='' || strpos($pathInfo,$baseUrl)===0)
$pathInfo=substr($pathInfo,strlen($baseUrl));
else if(strpos($_SERVER['PHP_SELF'],$scriptUrl)===0)
$pathInfo=substr($_SERVER['PHP_SELF'],strlen($scriptUrl));
else
throw new CException(Yii::t('yii','CHttpRequest is unable to determine the path info of the request.'));
$this->_pathInfo=trim($pathInfo,'/');
}
return $this->_pathInfo;
}
/**
* Decodes the path info.
* This method is an improved variant of the native urldecode() function and used in {@link getPathInfo getPathInfo()} to
* decode the path part of the request URI. You may override this method to change the way the path info is being decoded.
* @param string $pathInfo encoded path info
* @return string decoded path info
* @since 1.1.10
*/
protected function decodePathInfo($pathInfo)
{
$pathInfo = urldecode($pathInfo);
// is it UTF-8?
// http://w3.org/International/questions/qa-forms-utf-8.html
if(preg_match('%^(?:
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)*$%xs', $pathInfo))
{
return $pathInfo;
}
else
{
return utf8_encode($pathInfo);
}
}
/**
* Returns the request URI portion for the currently requested URL.
* This refers to the portion that is after the {@link hostInfo host info} part.
* It includes the {@link queryString query string} part if any.
* The implementation of this method referenced Zend_Controller_Request_Http in Zend Framework.
* @return string the request URI portion for the currently requested URL.
* @throws CException if the request URI cannot be determined due to improper server configuration
*/
public function getRequestUri()
{
if($this->_requestUri===null)
{
if(isset($_SERVER['HTTP_X_REWRITE_URL'])) // IIS
$this->_requestUri=$_SERVER['HTTP_X_REWRITE_URL'];
else if(isset($_SERVER['REQUEST_URI']))
{
$this->_requestUri=$_SERVER['REQUEST_URI'];
if(!empty($_SERVER['HTTP_HOST']))
{
if(strpos($this->_requestUri,$_SERVER['HTTP_HOST'])!==false)
$this->_requestUri=preg_replace('/^\w+:\/\/[^\/]+/','',$this->_requestUri);
}
else
$this->_requestUri=preg_replace('/^(http|https):\/\/[^\/]+/i','',$this->_requestUri);
}
else if(isset($_SERVER['ORIG_PATH_INFO'])) // IIS 5.0 CGI
{
$this->_requestUri=$_SERVER['ORIG_PATH_INFO'];
if(!empty($_SERVER['QUERY_STRING']))
$this->_requestUri.='?'.$_SERVER['QUERY_STRING'];
}
else
throw new CException(Yii::t('yii','CHttpRequest is unable to determine the request URI.'));
}
return $this->_requestUri;
}
/**
* Returns part of the request URL that is after the question mark.
* @return string part of the request URL that is after the question mark
*/
public function getQueryString()
{
return isset($_SERVER['QUERY_STRING'])?$_SERVER['QUERY_STRING']:'';
}
/**
* Return if the request is sent via secure channel (https).
* @return boolean if the request is sent via secure channel (https)
*/
public function getIsSecureConnection()
{
return isset($_SERVER['HTTPS']) && !strcasecmp($_SERVER['HTTPS'],'on');
}
/**
* Returns the request type, such as GET, POST, HEAD, PUT, DELETE.
* Request type can be manually set in POST requests with a parameter named _method. Useful
* for RESTful request from older browsers which do not support PUT or DELETE
* natively (available since version 1.1.11).
* @return string request type, such as GET, POST, HEAD, PUT, DELETE.
*/
public function getRequestType()
{
if(isset($_POST['_method']))
return strtoupper($_POST['_method']);
return strtoupper(isset($_SERVER['REQUEST_METHOD'])?$_SERVER['REQUEST_METHOD']:'GET');
}
/**
* Returns whether this is a POST request.
* @return boolean whether this is a POST request.
*/
public function getIsPostRequest()
{
return isset($_SERVER['REQUEST_METHOD']) && !strcasecmp($_SERVER['REQUEST_METHOD'],'POST');
}
/**
* Returns whether this is a DELETE request.
* @return boolean whether this is a DELETE request.
* @since 1.1.7
*/
public function getIsDeleteRequest()
{
return (isset($_SERVER['REQUEST_METHOD']) && !strcasecmp($_SERVER['REQUEST_METHOD'],'DELETE')) || $this->getIsDeleteViaPostRequest();
}
/**
* Returns whether this is a DELETE request which was tunneled through POST.
* @return boolean whether this is a DELETE request tunneled through POST.
* @since 1.1.11
*/
protected function getIsDeleteViaPostRequest()
{
return isset($_POST['_method']) && !strcasecmp($_POST['_method'],'DELETE');
}
/**
* Returns whether this is a PUT request.
* @return boolean whether this is a PUT request.
* @since 1.1.7
*/
public function getIsPutRequest()
{
return (isset($_SERVER['REQUEST_METHOD']) && !strcasecmp($_SERVER['REQUEST_METHOD'],'PUT')) || $this->getIsPutViaPostReqest();
}
/**
* Returns whether this is a PUT request which was tunneled through POST.
* @return boolean whether this is a PUT request tunneled through POST.
* @since 1.1.11
*/
protected function getIsPutViaPostReqest()
{
return isset($_POST['_method']) && !strcasecmp($_POST['_method'],'PUT');
}
/**
* Returns whether this is an AJAX (XMLHttpRequest) request.
* @return boolean whether this is an AJAX (XMLHttpRequest) request.
*/
public function getIsAjaxRequest()
{
return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH']==='XMLHttpRequest';
}
/**
* Returns whether this is an Adobe Flash or Adobe Flex request.
* @return boolean whether this is an Adobe Flash or Adobe Flex request.
* @since 1.1.11
*/
public function getIsFlashRequest()
{
return isset($_SERVER['HTTP_USER_AGENT']) && (stripos($_SERVER['HTTP_USER_AGENT'],'Shockwave')!==false || stripos($_SERVER['HTTP_USER_AGENT'],'Flash')!==false);
}
/**
* Returns the server name.
* @return string server name
*/
public function getServerName()
{
return $_SERVER['SERVER_NAME'];
}
/**
* Returns the server port number.
* @return integer server port number
*/
public function getServerPort()
{
return $_SERVER['SERVER_PORT'];
}
/**
* Returns the URL referrer, null if not present
* @return string URL referrer, null if not present
*/
public function getUrlReferrer()
{
return isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:null;
}
/**
* Returns the user agent, null if not present.
* @return string user agent, null if not present
*/
public function getUserAgent()
{
return isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:null;
}
/**
* Returns the user IP address.
* @return string user IP address
*/
public function getUserHostAddress()
{
return isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:'127.0.0.1';
}
/**
* Returns the user host name, null if it cannot be determined.
* @return string user host name, null if cannot be determined
*/
public function getUserHost()
{
return isset($_SERVER['REMOTE_HOST'])?$_SERVER['REMOTE_HOST']:null;
}
/**
* Returns entry script file path.
* @return string entry script file path (processed w/ realpath())
*/
public function getScriptFile()
{
if($this->_scriptFile!==null)
return $this->_scriptFile;
else
return $this->_scriptFile=realpath($_SERVER['SCRIPT_FILENAME']);
}
/**
* Returns information about the capabilities of user browser.
* @param string $userAgent the user agent to be analyzed. Defaults to null, meaning using the
* current User-Agent HTTP header information.
* @return array user browser capabilities.
* @see http://www.php.net/manual/en/function.get-browser.php
*/
public function getBrowser($userAgent=null)
{
return get_browser($userAgent,true);
}
/**
* Returns user browser accept types, null if not present.
* @return string user browser accept types, null if not present
*/
public function getAcceptTypes()
{
return isset($_SERVER['HTTP_ACCEPT'])?$_SERVER['HTTP_ACCEPT']:null;
}
private $_port;
/**
* Returns the port to use for insecure requests.
* Defaults to 80, or the port specified by the server if the current
* request is insecure.
* You may explicitly specify it by setting the {@link setPort port} property.
* @return integer port number for insecure requests.
* @see setPort
* @since 1.1.3
*/
public function getPort()
{
if($this->_port===null)
$this->_port=!$this->getIsSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int)$_SERVER['SERVER_PORT'] : 80;
return $this->_port;
}
/**
* Sets the port to use for insecure requests.
* This setter is provided in case a custom port is necessary for certain
* server configurations.
* @param integer $value port number.
* @since 1.1.3
*/
public function setPort($value)
{
$this->_port=(int)$value;
$this->_hostInfo=null;
}
private $_securePort;
/**
* Returns the port to use for secure requests.
* Defaults to 443, or the port specified by the server if the current
* request is secure.
* You may explicitly specify it by setting the {@link setSecurePort securePort} property.
* @return integer port number for secure requests.
* @see setSecurePort
* @since 1.1.3
*/
public function getSecurePort()
{
if($this->_securePort===null)
$this->_securePort=$this->getIsSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int)$_SERVER['SERVER_PORT'] : 443;
return $this->_securePort;
}
/**
* Sets the port to use for secure requests.
* This setter is provided in case a custom port is necessary for certain
* server configurations.
* @param integer $value port number.
* @since 1.1.3
*/
public function setSecurePort($value)
{
$this->_securePort=(int)$value;
$this->_hostInfo=null;
}
/**
* Returns the cookie collection.
* The result can be used like an associative array. Adding {@link CHttpCookie} objects
* to the collection will send the cookies to the client; and removing the objects
* from the collection will delete those cookies on the client.
* @return CCookieCollection the cookie collection.
*/
public function getCookies()
{
if($this->_cookies!==null)
return $this->_cookies;
else
return $this->_cookies=new CCookieCollection($this);
}
/**
* Redirects the browser to the specified URL.
* @param string $url URL to be redirected to. If the URL is a relative one, the base URL of
* the application will be inserted at the beginning.
* @param boolean $terminate whether to terminate the current application
* @param integer $statusCode the HTTP status code. Defaults to 302. See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html}
* for details about HTTP status code.
*/
public function redirect($url,$terminate=true,$statusCode=302)
{
if(strpos($url,'/')===0)
$url=$this->getHostInfo().$url;
header('Location: '.$url, true, $statusCode);
if($terminate)
Yii::app()->end();
}
/**
* Returns the user preferred language.
* The returned language ID will be canonicalized using {@link CLocale::getCanonicalID}.
* This method returns false if the user does not have language preference.
* @return string the user preferred language.
*/
public function getPreferredLanguage()
{
if($this->_preferredLanguage===null)
{
if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) && ($n=preg_match_all('/([\w\-_]+)\s*(;\s*q\s*=\s*(\d*\.\d*))?/',$_SERVER['HTTP_ACCEPT_LANGUAGE'],$matches))>0)
{
$languages=array();
for($i=0;$i<$n;++$i)
$languages[$matches[1][$i]]=empty($matches[3][$i]) ? 1.0 : floatval($matches[3][$i]);
arsort($languages);
foreach($languages as $language=>$pref)
return $this->_preferredLanguage=CLocale::getCanonicalID($language);
}
return $this->_preferredLanguage=false;
}
return $this->_preferredLanguage;
}
/**
* Sends a file to user.
* @param string $fileName file name
* @param string $content content to be set.
* @param string $mimeType mime type of the content. If null, it will be guessed automatically based on the given file name.
* @param boolean $terminate whether to terminate the current application after calling this method
*/
public function sendFile($fileName,$content,$mimeType=null,$terminate=true)
{
if($mimeType===null)
{
if(($mimeType=CFileHelper::getMimeTypeByExtension($fileName))===null)
$mimeType='text/plain';
}
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header("Content-type: $mimeType");
if(ob_get_length()===false)
header('Content-Length: '.(function_exists('mb_strlen') ? mb_strlen($content,'8bit') : strlen($content)));
header("Content-Disposition: attachment; filename=\"$fileName\"");
header('Content-Transfer-Encoding: binary');
if($terminate)
{
// clean up the application first because the file downloading could take long time
// which may cause timeout of some resources (such as DB connection)
Yii::app()->end(0,false);
echo $content;
exit(0);
}
else
echo $content;
}
/**
* Sends existing file to a browser as a download using x-sendfile.
*
* X-Sendfile is a feature allowing a web application to redirect the request for a file to the webserver
* that in turn processes the request, this way eliminating the need to perform tasks like reading the file
* and sending it to the user. When dealing with a lot of files (or very big files) this can lead to a great
* increase in performance as the web application is allowed to terminate earlier while the webserver is
* handling the request.
*
* The request is sent to the server through a special non-standard HTTP-header.
* When the web server encounters the presence of such header it will discard all output and send the file
* specified by that header using web server internals including all optimizations like caching-headers.
*
* As this header directive is non-standard different directives exists for different web servers applications:
* <ul>
* <li>Apache: {@link http://tn123.org/mod_xsendfile X-Sendfile}</li>
* <li>Lighttpd v1.4: {@link http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file X-LIGHTTPD-send-file}</li>
* <li>Lighttpd v1.5: {@link http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file X-Sendfile}</li>
* <li>Nginx: {@link http://wiki.nginx.org/XSendfile X-Accel-Redirect}</li>
* <li>Cherokee: {@link http://www.cherokee-project.com/doc/other_goodies.html#x-sendfile X-Sendfile and X-Accel-Redirect}</li>
* </ul>
* So for this method to work the X-SENDFILE option/module should be enabled by the web server and
* a proper xHeader should be sent.
*
* <b>Note:</b>
* This option allows to download files that are not under web folders, and even files that are otherwise protected (deny from all) like .htaccess
*
* <b>Side effects</b>:
* If this option is disabled by the web server, when this method is called a download configuration dialog
* will open but the downloaded file will have 0 bytes.
*
* <b>Example</b>:
* <pre>
* <?php
* Yii::app()->request->xSendFile('/home/user/Pictures/picture1.jpg',array(
* 'saveName'=>'image1.jpg',
* 'mimeType'=>'image/jpeg',
* 'terminate'=>false,
* ));
* ?>
* </pre>
* @param string $filePath file name with full path
* @param array $options additional options:
* <ul>
* <li>saveName: file name shown to the user, if not set real file name will be used</li>
* <li>mimeType: mime type of the file, if not set it will be guessed automatically based on the file name, if set to null no content-type header will be sent.</li>
* <li>xHeader: appropriate x-sendfile header, defaults to "X-Sendfile"</li>
* <li>terminate: whether to terminate the current application after calling this method, defaults to true</li>
* <li>forceDownload: specifies whether the file will be downloaded or shown inline, defaults to true. (Since version 1.1.9.)</li>
* <li>addHeaders: an array of additional http headers in header-value pairs (available since version 1.1.10)</li>
* </ul>
*/
public function xSendFile($filePath, $options=array())
{
if(!isset($options['forceDownload']) || $options['forceDownload'])
$disposition='attachment';
else
$disposition='inline';
if(!isset($options['saveName']))
$options['saveName']=basename($filePath);
if(!isset($options['mimeType']))
{
if(($options['mimeType']=CFileHelper::getMimeTypeByExtension($filePath))===null)
$options['mimeType']='text/plain';
}
if(!isset($options['xHeader']))
$options['xHeader']='X-Sendfile';
if($options['mimeType'] !== null)
header('Content-type: '.$options['mimeType']);
header('Content-Disposition: '.$disposition.'; filename="'.$options['saveName'].'"');
if(isset($options['addHeaders']))
{
foreach($options['addHeaders'] as $header=>$value)
header($header.': '.$value);
}
header(trim($options['xHeader']).': '.$filePath);
if(!isset($options['terminate']) || $options['terminate'])
Yii::app()->end();
}
/**
* Returns the random token used to perform CSRF validation.
* The token will be read from cookie first. If not found, a new token
* will be generated.
* @return string the random token for CSRF validation.
* @see enableCsrfValidation
*/
public function getCsrfToken()
{
if($this->_csrfToken===null)
{
$cookie=$this->getCookies()->itemAt($this->csrfTokenName);
if(!$cookie || ($this->_csrfToken=$cookie->value)==null)
{
$cookie=$this->createCsrfCookie();
$this->_csrfToken=$cookie->value;
$this->getCookies()->add($cookie->name,$cookie);
}
}
return $this->_csrfToken;
}
/**
* Creates a cookie with a randomly generated CSRF token.
* Initial values specified in {@link csrfCookie} will be applied
* to the generated cookie.
* @return CHttpCookie the generated cookie
* @see enableCsrfValidation
*/
protected function createCsrfCookie()
{
$cookie=new CHttpCookie($this->csrfTokenName,sha1(uniqid(mt_rand(),true)));
if(is_array($this->csrfCookie))
{
foreach($this->csrfCookie as $name=>$value)
$cookie->$name=$value;
}
return $cookie;
}
/**
* Performs the CSRF validation.
* This is the event handler responding to {@link CApplication::onBeginRequest}.
* The default implementation will compare the CSRF token obtained
* from a cookie and from a POST field. If they are different, a CSRF attack is detected.
* @param CEvent $event event parameter
* @throws CHttpException if the validation fails
*/
public function validateCsrfToken($event)
{
if($this->getIsPostRequest())
{
// only validate POST requests
$cookies=$this->getCookies();
if($cookies->contains($this->csrfTokenName) && isset($_POST[$this->csrfTokenName]))
{
$tokenFromCookie=$cookies->itemAt($this->csrfTokenName)->value;
$tokenFromPost=$_POST[$this->csrfTokenName];
$valid=$tokenFromCookie===$tokenFromPost;
}
else
$valid=false;
if(!$valid)
throw new CHttpException(400,Yii::t('yii','The CSRF token could not be verified.'));
}
}
}
/**
* CCookieCollection implements a collection class to store cookies.
*
* You normally access it via {@link CHttpRequest::getCookies()}.
*
* Since CCookieCollection extends from {@link CMap}, it can be used
* like an associative array as follows:
* <pre>
* $cookies[$name]=new CHttpCookie($name,$value); // sends a cookie
* $value=$cookies[$name]->value; // reads a cookie value
* unset($cookies[$name]); // removes a cookie
* </pre>
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id$
* @package system.web
* @since 1.0
*/
class CCookieCollection extends CMap
{
private $_request;
private $_initialized=false;
/**
* Constructor.
* @param CHttpRequest $request owner of this collection.
*/
public function __construct(CHttpRequest $request)
{
$this->_request=$request;
$this->copyfrom($this->getCookies());
$this->_initialized=true;
}
/**
* @return CHttpRequest the request instance
*/
public function getRequest()
{
return $this->_request;
}
/**
* @return array list of validated cookies
*/
protected function getCookies()
{
$cookies=array();
if($this->_request->enableCookieValidation)
{
$sm=Yii::app()->getSecurityManager();
foreach($_COOKIE as $name=>$value)
{
if(is_string($value) && ($value=$sm->validateData($value))!==false)
$cookies[$name]=new CHttpCookie($name,@unserialize($value));
}
}
else
{
foreach($_COOKIE as $name=>$value)
$cookies[$name]=new CHttpCookie($name,$value);
}
return $cookies;
}
/**
* Adds a cookie with the specified name.
* This overrides the parent implementation by performing additional
* operations for each newly added CHttpCookie object.
* @param mixed $name Cookie name.
* @param CHttpCookie $cookie Cookie object.
* @throws CException if the item to be inserted is not a CHttpCookie object.
*/
public function add($name,$cookie)
{
if($cookie instanceof CHttpCookie)
{
$this->remove($name);
parent::add($name,$cookie);
if($this->_initialized)
$this->addCookie($cookie);
}
else
throw new CException(Yii::t('yii','CHttpCookieCollection can only hold CHttpCookie objects.'));
}
/**
* Removes a cookie with the specified name.
* This overrides the parent implementation by performing additional
* cleanup work when removing a CHttpCookie object.
* @param mixed $name Cookie name.
* @return CHttpCookie The removed cookie object.
*/
public function remove($name)
{
if(($cookie=parent::remove($name))!==null)
{
if($this->_initialized)
$this->removeCookie($cookie);
}
return $cookie;
}
/**
* Sends a cookie.
* @param CHttpCookie $cookie cookie to be sent
*/
protected function addCookie($cookie)
{
$value=$cookie->value;
if($this->_request->enableCookieValidation)
$value=Yii::app()->getSecurityManager()->hashData(serialize($value));
if(version_compare(PHP_VERSION,'5.2.0','>='))
setcookie($cookie->name,$value,$cookie->expire,$cookie->path,$cookie->domain,$cookie->secure,$cookie->httpOnly);
else
setcookie($cookie->name,$value,$cookie->expire,$cookie->path,$cookie->domain,$cookie->secure);
}
/**
* Deletes a cookie.
* @param CHttpCookie $cookie cookie to be deleted
*/
protected function removeCookie($cookie)
{
if(version_compare(PHP_VERSION,'5.2.0','>='))
setcookie($cookie->name,null,0,$cookie->path,$cookie->domain,$cookie->secure,$cookie->httpOnly);
else
setcookie($cookie->name,null,0,$cookie->path,$cookie->domain,$cookie->secure);
}
}
<?php
/**
* CHttpSession class file.
*
* @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/
*/
/**
* CHttpSession provides session-level data management and the related configurations.
*
* To start the session, call {@link open()}; To complete and send out session data, call {@link close()};
* To destroy the session, call {@link destroy()}.
*
* If {@link autoStart} is set true, the session will be started automatically
* when the application component is initialized by the application.
*
* CHttpSession can be used like an array to set and get session data. For example,
* <pre>
* $session=new CHttpSession;
* $session->open();
* $value1=$session['name1']; // get session variable 'name1'
* $value2=$session['name2']; // get session variable 'name2'
* foreach($session as $name=>$value) // traverse all session variables
* $session['name3']=$value3; // set session variable 'name3'
* </pre>
*
* The following configurations are available for session:
* <ul>
* <li>{@link setSessionID sessionID};</li>
* <li>{@link setSessionName sessionName};</li>
* <li>{@link autoStart};</li>
* <li>{@link setSavePath savePath};</li>
* <li>{@link setCookieParams cookieParams};</li>
* <li>{@link setGCProbability gcProbability};</li>
* <li>{@link setCookieMode cookieMode};</li>
* <li>{@link setUseTransparentSessionID useTransparentSessionID};</li>
* <li>{@link setTimeout timeout}.</li>
* </ul>
* See the corresponding setter and getter documentation for more information.
* Note, these properties must be set before the session is started.
*
* CHttpSession can be extended to support customized session storage.
* Override {@link openSession}, {@link closeSession}, {@link readSession},
* {@link writeSession}, {@link destroySession} and {@link gcSession}
* and set {@link useCustomStorage} to true.
* Then, the session data will be stored and retrieved using the above methods.
*
* CHttpSession is a Web application component that can be accessed via
* {@link CWebApplication::getSession()}.
*
* @property boolean $useCustomStorage Whether to use custom storage.
* @property boolean $isStarted Whether the session has started.
* @property string $sessionID The current session ID.
* @property string $sessionName The current session name.
* @property string $savePath The current session save path, defaults to '/tmp'.
* @property array $cookieParams The session cookie parameters.
* @property string $cookieMode How to use cookie to store session ID. Defaults to 'Allow'.
* @property integer $gCProbability The probability (percentage) that the gc (garbage collection) process is started on every session initialization, defaults to 1 meaning 1% chance.
* @property boolean $useTransparentSessionID Whether transparent sid support is enabled or not, defaults to false.
* @property integer $timeout The number of seconds after which data will be seen as 'garbage' and cleaned up, defaults to 1440 seconds.
* @property CHttpSessionIterator $iterator An iterator for traversing the session variables.
* @property integer $count The number of session variables.
* @property array $keys The list of session variable names.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id$
* @package system.web
* @since 1.0
*/
class CHttpSession extends CApplicationComponent implements IteratorAggregate,ArrayAccess,Countable
{
/**
* @var boolean whether the session should be automatically started when the session application component is initialized, defaults to true.
*/
public $autoStart=true;
/**
* Initializes the application component.
* This method is required by IApplicationComponent and is invoked by application.
*/
public function init()
{
parent::init();
if($this->autoStart)
$this->open();
register_shutdown_function(array($this,'close'));
}
/**
* Returns a value indicating whether to use custom session storage.
* This method should be overriden to return true if custom session storage handler should be used.
* If returning true, make sure the methods {@link openSession}, {@link closeSession}, {@link readSession},
* {@link writeSession}, {@link destroySession}, and {@link gcSession} are overridden in child
* class, because they will be used as the callback handlers.
* The default implementation always return false.
* @return boolean whether to use custom storage.
*/
public function getUseCustomStorage()
{
return false;
}
/**
* Starts the session if it has not started yet.
*/
public function open()
{
if($this->getUseCustomStorage())
@session_set_save_handler(array($this,'openSession'),array($this,'closeSession'),array($this,'readSession'),array($this,'writeSession'),array($this,'destroySession'),array($this,'gcSession'));
@session_start();
if(YII_DEBUG && session_id()=='')
{
$message=Yii::t('yii','Failed to start session.');
if(function_exists('error_get_last'))
{
$error=error_get_last();
if(isset($error['message']))
$message=$error['message'];
}
Yii::log($message, CLogger::LEVEL_WARNING, 'system.web.CHttpSession');
}
}
/**
* Ends the current session and store session data.
*/
public function close()
{
if(session_id()!=='')
@session_write_close();
}
/**
* Frees all session variables and destroys all data registered to a session.
*/
public function destroy()
{
if(session_id()!=='')
{
@session_unset();
@session_destroy();
}
}
/**
* @return boolean whether the session has started
*/
public function getIsStarted()
{
return session_id()!=='';
}
/**
* @return string the current session ID
*/
public function getSessionID()
{
return session_id();
}
/**
* @param string $value the session ID for the current session
*/
public function setSessionID($value)
{
session_id($value);
}
/**
* Updates the current session id with a newly generated one .
* Please refer to {@link http://php.net/session_regenerate_id} for more details.
* @param boolean $deleteOldSession Whether to delete the old associated session file or not.
* @since 1.1.8
*/
public function regenerateID($deleteOldSession=false)
{
session_regenerate_id($deleteOldSession);
}
/**
* @return string the current session name
*/
public function getSessionName()
{
return session_name();
}
/**
* @param string $value the session name for the current session, must be an alphanumeric string, defaults to PHPSESSID
*/
public function setSessionName($value)
{
session_name($value);
}
/**
* @return string the current session save path, defaults to '/tmp'.
*/
public function getSavePath()
{
return session_save_path();
}
/**
* @param string $value the current session save path
* @throws CException if the path is not a valid directory
*/
public function setSavePath($value)
{
if(is_dir($value))
session_save_path($value);
else
throw new CException(Yii::t('yii','CHttpSession.savePath "{path}" is not a valid directory.',
array('{path}'=>$value)));
}
/**
* @return array the session cookie parameters.
* @see http://us2.php.net/manual/en/function.session-get-cookie-params.php
*/
public function getCookieParams()
{
return session_get_cookie_params();
}
/**
* Sets the session cookie parameters.
* The effect of this method only lasts for the duration of the script.
* Call this method before the session starts.
* @param array $value cookie parameters, valid keys include: lifetime, path, domain, secure.
* @see http://us2.php.net/manual/en/function.session-set-cookie-params.php
*/
public function setCookieParams($value)
{
$data=session_get_cookie_params();
extract($data);
extract($value);
if(isset($httponly))
session_set_cookie_params($lifetime,$path,$domain,$secure,$httponly);
else
session_set_cookie_params($lifetime,$path,$domain,$secure);
}
/**
* @return string how to use cookie to store session ID. Defaults to 'Allow'.
*/
public function getCookieMode()
{
if(ini_get('session.use_cookies')==='0')
return 'none';
else if(ini_get('session.use_only_cookies')==='0')
return 'allow';
else
return 'only';
}
/**
* @param string $value how to use cookie to store session ID. Valid values include 'none', 'allow' and 'only'.
*/
public function setCookieMode($value)
{
if($value==='none')
{
ini_set('session.use_cookies','0');
ini_set('session.use_only_cookies','0');
}
else if($value==='allow')
{
ini_set('session.use_cookies','1');
ini_set('session.use_only_cookies','0');
}
else if($value==='only')
{
ini_set('session.use_cookies','1');
ini_set('session.use_only_cookies','1');
}
else
throw new CException(Yii::t('yii','CHttpSession.cookieMode can only be "none", "allow" or "only".'));
}
/**
* @return integer the probability (percentage) that the gc (garbage collection) process is started on every session initialization, defaults to 1 meaning 1% chance.
*/
public function getGCProbability()
{
return (int)ini_get('session.gc_probability');
}
/**
* @param integer $value the probability (percentage) that the gc (garbage collection) process is started on every session initialization.
* @throws CException if the value is beyond [0,100]
*/
public function setGCProbability($value)
{
$value=(int)$value;
if($value>=0 && $value<=100)
{
ini_set('session.gc_probability',$value);
ini_set('session.gc_divisor','100');
}
else
throw new CException(Yii::t('yii','CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.',
array('{value}'=>$value)));
}
/**
* @return boolean whether transparent sid support is enabled or not, defaults to false.
*/
public function getUseTransparentSessionID()
{
return ini_get('session.use_trans_sid')==1;
}
/**
* @param boolean $value whether transparent sid support is enabled or not.
*/
public function setUseTransparentSessionID($value)
{
ini_set('session.use_trans_sid',$value?'1':'0');
}
/**
* @return integer the number of seconds after which data will be seen as 'garbage' and cleaned up, defaults to 1440 seconds.
*/
public function getTimeout()
{
return (int)ini_get('session.gc_maxlifetime');
}
/**
* @param integer $value the number of seconds after which data will be seen as 'garbage' and cleaned up
*/
public function setTimeout($value)
{
ini_set('session.gc_maxlifetime',$value);
}
/**
* Session open handler.
* This method should be overridden if {@link useCustomStorage} is set true.
* Do not call this method directly.
* @param string $savePath session save path
* @param string $sessionName session name
* @return boolean whether session is opened successfully
*/
public function openSession($savePath,$sessionName)
{
return true;
}
/**
* Session close handler.
* This method should be overridden if {@link useCustomStorage} is set true.
* Do not call this method directly.
* @return boolean whether session is closed successfully
*/
public function closeSession()
{
return true;
}
/**
* Session read handler.
* This method should be overridden if {@link useCustomStorage} is set true.
* Do not call this method directly.
* @param string $id session ID
* @return string the session data
*/
public function readSession($id)
{
return '';
}
/**
* Session write handler.
* This method should be overridden if {@link useCustomStorage} is set true.
* Do not call this method directly.
* @param string $id session ID
* @param string $data session data
* @return boolean whether session write is successful
*/
public function writeSession($id,$data)
{
return true;
}
/**
* Session destroy handler.
* This method should be overridden if {@link useCustomStorage} is set true.
* Do not call this method directly.
* @param string $id session ID
* @return boolean whether session is destroyed successfully
*/
public function destroySession($id)
{
return true;
}
/**
* Session GC (garbage collection) handler.
* This method should be overridden if {@link useCustomStorage} is set true.
* Do not call this method directly.
* @param integer $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.
* @return boolean whether session is GCed successfully
*/
public function gcSession($maxLifetime)
{
return true;
}
//------ The following methods enable CHttpSession to be CMap-like -----
/**
* Returns an iterator for traversing the session variables.
* This method is required by the interface IteratorAggregate.
* @return CHttpSessionIterator an iterator for traversing the session variables.
*/
public function getIterator()
{
return new CHttpSessionIterator;
}
/**
* Returns the number of items in the session.
* @return integer the number of session variables
*/
public function getCount()
{
return count($_SESSION);
}
/**
* Returns the number of items in the session.
* This method is required by Countable interface.
* @return integer number of items in the session.
*/
public function count()
{
return $this->getCount();
}
/**
* @return array the list of session variable names
*/
public function getKeys()
{
return array_keys($_SESSION);
}
/**
* Returns the session variable value with the session variable name.
* This method is very similar to {@link itemAt} and {@link offsetGet},
* except that it will return $defaultValue if the session variable does not exist.
* @param mixed $key the session variable name
* @param mixed $defaultValue the default value to be returned when the session variable does not exist.
* @return mixed the session variable value, or $defaultValue if the session variable does not exist.
* @since 1.1.2
*/
public function get($key,$defaultValue=null)
{
return isset($_SESSION[$key]) ? $_SESSION[$key] : $defaultValue;
}
/**
* Returns the session variable value with the session variable name.
* This method is exactly the same as {@link offsetGet}.
* @param mixed $key the session variable name
* @return mixed the session variable value, null if no such variable exists
*/
public function itemAt($key)
{
return isset($_SESSION[$key]) ? $_SESSION[$key] : null;
}
/**
* Adds a session variable.
* Note, if the specified name already exists, the old value will be removed first.
* @param mixed $key session variable name
* @param mixed $value session variable value
*/
public function add($key,$value)
{
$_SESSION[$key]=$value;
}
/**
* Removes a session variable.
* @param mixed $key the name of the session variable to be removed
* @return mixed the removed value, null if no such session variable.
*/
public function remove($key)
{
if(isset($_SESSION[$key]))
{
$value=$_SESSION[$key];
unset($_SESSION[$key]);
return $value;
}
else
return null;
}
/**
* Removes all session variables
*/
public function clear()
{
foreach(array_keys($_SESSION) as $key)
unset($_SESSION[$key]);
}
/**
* @param mixed $key session variable name
* @return boolean whether there is the named session variable
*/
public function contains($key)
{
return isset($_SESSION[$key]);
}
/**
* @return array the list of all session variables in array
*/
public function toArray()
{
return $_SESSION;
}
/**
* This method is required by the interface ArrayAccess.
* @param mixed $offset the offset to check on
* @return boolean
*/
public function offsetExists($offset)
{
return isset($_SESSION[$offset]);
}
/**
* This method is required by the interface ArrayAccess.
* @param integer $offset the offset to retrieve element.
* @return mixed the element at the offset, null if no element is found at the offset
*/
public function offsetGet($offset)
{
return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null;
}
/**
* This method is required by the interface ArrayAccess.
* @param integer $offset the offset to set element
* @param mixed $item the element value
*/
public function offsetSet($offset,$item)
{
$_SESSION[$offset]=$item;
}
/**
* This method is required by the interface ArrayAccess.
* @param mixed $offset the offset to unset element
*/
public function offsetUnset($offset)
{
unset($_SESSION[$offset]);
}
}
<?php
/**
* CSort class file.
*
* @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/
*/
/**
* CSort represents information relevant to sorting.
*
* When data needs to be sorted according to one or several attributes,
* we can use CSort to represent the sorting information and generate
* appropriate hyperlinks that can lead to sort actions.
*
* CSort is designed to be used together with {@link CActiveRecord}.
* When creating a CSort instance, you need to specify {@link modelClass}.
* You can use CSort to generate hyperlinks by calling {@link link}.
* You can also use CSort to modify a {@link CDbCriteria} instance by calling {@link applyOrder} so that
* it can cause the query results to be sorted according to the specified
* attributes.
*
* In order to prevent SQL injection attacks, CSort ensures that only valid model attributes
* can be sorted. This is determined based on {@link modelClass} and {@link attributes}.
* When {@link attributes} is not set, all attributes belonging to {@link modelClass}
* can be sorted. When {@link attributes} is set, only those attributes declared in the property
* can be sorted.
*
* By configuring {@link attributes}, one can perform more complex sorts that may
* consist of things like compound attributes (e.g. sort based on the combination of
* first name and last name of users).
*
* The property {@link attributes} should be an array of key-value pairs, where the keys
* represent the attribute names, while the values represent the virtual attribute definitions.
* For more details, please check the documentation about {@link attributes}.
*
* @property string $orderBy The order-by columns represented by this sort object.
* This can be put in the ORDER BY clause of a SQL statement.
* @property array $directions Sort directions indexed by attribute names.
* The sort direction. Can be either CSort::SORT_ASC for ascending order or
* CSort::SORT_DESC for descending order.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id$
* @package system.web
*/
class CSort extends CComponent
{
/**
* Sort ascending
* @since 1.1.10
*/
const SORT_ASC = false;
/**
* Sort descending
* @since 1.1.10
*/
const SORT_DESC = true;
/**
* @var boolean whether the sorting can be applied to multiple attributes simultaneously.
* Defaults to false, which means each time the data can only be sorted by one attribute.
*/
public $multiSort=false;
/**
* @var string the name of the model class whose attributes can be sorted.
* The model class must be a child class of {@link CActiveRecord}.
*/
public $modelClass;
/**
* @var array list of attributes that are allowed to be sorted.
* For example, array('user_id','create_time') would specify that only 'user_id'
* and 'create_time' of the model {@link modelClass} can be sorted.
* By default, this property is an empty array, which means all attributes in
* {@link modelClass} are allowed to be sorted.
*
* This property can also be used to specify complex sorting. To do so,
* a virtual attribute can be declared in terms of a key-value pair in the array.
* The key refers to the name of the virtual attribute that may appear in the sort request,
* while the value specifies the definition of the virtual attribute.
*
* In the simple case, a key-value pair can be like <code>'user'=>'user_id'</code>
* where 'user' is the name of the virtual attribute while 'user_id' means the virtual
* attribute is the 'user_id' attribute in the {@link modelClass}.
*
* A more flexible way is to specify the key-value pair as
* <pre>
* 'user'=>array(
* 'asc'=>'first_name, last_name',
* 'desc'=>'first_name DESC, last_name DESC',
* 'label'=>'Name'
* )
* </pre>
* where 'user' is the name of the virtual attribute that specifies the full name of user
* (a compound attribute consisting of first name and last name of user). In this case,
* we have to use an array to define the virtual attribute with three elements: 'asc',
* 'desc' and 'label'.
*
* The above approach can also be used to declare virtual attributes that consist of relational
* attributes. For example,
* <pre>
* 'price'=>array(
* 'asc'=>'item.price',
* 'desc'=>'item.price DESC',
* 'label'=>'Item Price'
* )
* </pre>
*
* Note, the attribute name should not contain '-' or '.' characters because
* they are used as {@link separators}.
*
* Starting from version 1.1.3, an additional option named 'default' can be used in the virtual attribute
* declaration. This option specifies whether an attribute should be sorted in ascending or descending
* order upon user clicking the corresponding sort hyperlink if it is not currently sorted. The valid
* option values include 'asc' (default) and 'desc'. For example,
* <pre>
* 'price'=>array(
* 'asc'=>'item.price',
* 'desc'=>'item.price DESC',
* 'label'=>'Item Price',
* 'default'=>'desc',
* )
* </pre>
*
* Also starting from version 1.1.3, you can include a star ('*') element in this property so that
* all model attributes are available for sorting, in addition to those virtual attributes. For example,
* <pre>
* 'attributes'=>array(
* 'price'=>array(
* 'asc'=>'item.price',
* 'desc'=>'item.price DESC',
* 'label'=>'Item Price',
* 'default'=>'desc',
* ),
* '*',
* )
* </pre>
* Note that when a name appears as both a model attribute and a virtual attribute, the position of
* the star element in the array determines which one takes precedence. In particular, if the star
* element is the first element in the array, the model attribute takes precedence; and if the star
* element is the last one, the virtual attribute takes precedence.
*/
public $attributes=array();
/**
* @var string the name of the GET parameter that specifies which attributes to be sorted
* in which direction. Defaults to 'sort'.
*/
public $sortVar='sort';
/**
* @var string the tag appeared in the GET parameter that indicates the attribute should be sorted
* in descending order. Defaults to 'desc'.
*/
public $descTag='desc';
/**
* @var mixed the default order that should be applied to the query criteria when
* the current request does not specify any sort. For example, 'name, create_time DESC' or
* 'UPPER(name)'.
*
* Starting from version 1.1.3, you can also specify the default order using an array.
* The array keys could be attribute names or virtual attribute names as declared in {@link attributes},
* and the array values indicate whether the sorting of the corresponding attributes should
* be in descending order. For example,
* <pre>
* 'defaultOrder'=>array(
* 'price'=>CSort::SORT_DESC,
* )
* </pre>
* `SORT_DESC` and `SORT_ASC` are available since 1.1.10. In earlier Yii versions you should use
* `true` and `false` respectively.
*
* Please note when using array to specify the default order, the corresponding attributes
* will be put into {@link directions} and thus affect how the sort links are rendered
* (e.g. an arrow may be displayed next to the currently active sort link).
*/
public $defaultOrder;
/**
* @var string the route (controller ID and action ID) for generating the sorted contents.
* Defaults to empty string, meaning using the currently requested route.
*/
public $route='';
/**
* @var array separators used in the generated URL. This must be an array consisting of
* two elements. The first element specifies the character separating different
* attributes, while the second element specifies the character separating attribute name
* and the corresponding sort direction. Defaults to array('-','.').
*/
public $separators=array('-','.');
/**
* @var array the additional GET parameters (name=>value) that should be used when generating sort URLs.
* Defaults to null, meaning using the currently available GET parameters.
*/
public $params;
private $_directions;
/**
* Constructor.
* @param string $modelClass the class name of data models that need to be sorted.
* This should be a child class of {@link CActiveRecord}.
*/
public function __construct($modelClass=null)
{
$this->modelClass=$modelClass;
}
/**
* Modifies the query criteria by changing its {@link CDbCriteria::order} property.
* This method will use {@link directions} to determine which columns need to be sorted.
* They will be put in the ORDER BY clause. If the criteria already has non-empty {@link CDbCriteria::order} value,
* the new value will be appended to it.
* @param CDbCriteria $criteria the query criteria
*/
public function applyOrder($criteria)
{
$order=$this->getOrderBy();
if(!empty($order))
{
if(!empty($criteria->order))
$criteria->order.=', ';
$criteria->order.=$order;
}
}
/**
* @return string the order-by columns represented by this sort object.
* This can be put in the ORDER BY clause of a SQL statement.
* @since 1.1.0
*/
public function getOrderBy()
{
$directions=$this->getDirections();
if(empty($directions))
return is_string($this->defaultOrder) ? $this->defaultOrder : '';
else
{
if($this->modelClass!==null)
$schema=CActiveRecord::model($this->modelClass)->getDbConnection()->getSchema();
$orders=array();
foreach($directions as $attribute=>$descending)
{
$definition=$this->resolveAttribute($attribute);
if(is_array($definition))
{
if($descending)
$orders[]=isset($definition['desc']) ? $definition['desc'] : $attribute.' DESC';
else
$orders[]=isset($definition['asc']) ? $definition['asc'] : $attribute;
}
else if($definition!==false)
{
$attribute=$definition;
if(isset($schema))
{
if(($pos=strpos($attribute,'.'))!==false)
$attribute=$schema->quoteTableName(substr($attribute,0,$pos)).'.'.$schema->quoteColumnName(substr($attribute,$pos+1));
else
$attribute=CActiveRecord::model($this->modelClass)->getTableAlias(true).'.'.$schema->quoteColumnName($attribute);
}
$orders[]=$descending?$attribute.' DESC':$attribute;
}
}
return implode(', ',$orders);
}
}
/**
* Generates a hyperlink that can be clicked to cause sorting.
* @param string $attribute the attribute name. This must be the actual attribute name, not alias.
* If it is an attribute of a related AR object, the name should be prefixed with
* the relation name (e.g. 'author.name', where 'author' is the relation name).
* @param string $label the link label. If null, the label will be determined according
* to the attribute (see {@link resolveLabel}).
* @param array $htmlOptions additional HTML attributes for the hyperlink tag
* @return string the generated hyperlink
*/
public function link($attribute,$label=null,$htmlOptions=array())
{
if($label===null)
$label=$this->resolveLabel($attribute);
if(($definition=$this->resolveAttribute($attribute))===false)
return $label;
$directions=$this->getDirections();
if(isset($directions[$attribute]))
{
$class=$directions[$attribute] ? 'desc' : 'asc';
if(isset($htmlOptions['class']))
$htmlOptions['class'].=' '.$class;
else
$htmlOptions['class']=$class;
$descending=!$directions[$attribute];
unset($directions[$attribute]);
}
else if(is_array($definition) && isset($definition['default']))
$descending=$definition['default']==='desc';
else
$descending=false;
if($this->multiSort)
$directions=array_merge(array($attribute=>$descending),$directions);
else
$directions=array($attribute=>$descending);
$url=$this->createUrl(Yii::app()->getController(),$directions);
return $this->createLink($attribute,$label,$url,$htmlOptions);
}
/**
* Resolves the attribute label for the specified attribute.
* This will invoke {@link CActiveRecord::getAttributeLabel} to determine what label to use.
* If the attribute refers to a virtual attribute declared in {@link attributes},
* then the label given in the {@link attributes} will be returned instead.
* @param string $attribute the attribute name.
* @return string the attribute label
*/
public function resolveLabel($attribute)
{
$definition=$this->resolveAttribute($attribute);
if(is_array($definition))
{
if(isset($definition['label']))
return $definition['label'];
}
else if(is_string($definition))
$attribute=$definition;
if($this->modelClass!==null)
return CActiveRecord::model($this->modelClass)->getAttributeLabel($attribute);
else
return $attribute;
}
/**
* Returns the currently requested sort information.
* @return array sort directions indexed by attribute names.
* Sort direction can be either CSort::SORT_ASC for ascending order or
* CSort::SORT_DESC for descending order.
*/
public function getDirections()
{
if($this->_directions===null)
{
$this->_directions=array();
if(isset($_GET[$this->sortVar]) && is_string($_GET[$this->sortVar]))
{
$attributes=explode($this->separators[0],$_GET[$this->sortVar]);
foreach($attributes as $attribute)
{
if(($pos=strrpos($attribute,$this->separators[1]))!==false)
{
$descending=substr($attribute,$pos+1)===$this->descTag;
if($descending)
$attribute=substr($attribute,0,$pos);
}
else
$descending=false;
if(($this->resolveAttribute($attribute))!==false)
{
$this->_directions[$attribute]=$descending;
if(!$this->multiSort)
return $this->_directions;
}
}
}
if($this->_directions===array() && is_array($this->defaultOrder))
$this->_directions=$this->defaultOrder;
}
return $this->_directions;
}
/**
* Returns the sort direction of the specified attribute in the current request.
* @param string $attribute the attribute name
* @return mixed Sort direction of the attribute. Can be either CSort::SORT_ASC
* for ascending order or CSort::SORT_DESC for descending order. Value is null
* if the attribute doesn't need to be sorted.
*/
public function getDirection($attribute)
{
$this->getDirections();
return isset($this->_directions[$attribute]) ? $this->_directions[$attribute] : null;
}
/**
* Creates a URL that can lead to generating sorted data.
* @param CController $controller the controller that will be used to create the URL.
* @param array $directions the sort directions indexed by attribute names.
* The sort direction can be either CSort::SORT_ASC for ascending order or
* CSort::SORT_DESC for descending order.
* @return string the URL for sorting
*/
public function createUrl($controller,$directions)
{
$sorts=array();
foreach($directions as $attribute=>$descending)
$sorts[]=$descending ? $attribute.$this->separators[1].$this->descTag : $attribute;
$params=$this->params===null ? $_GET : $this->params;
$params[$this->sortVar]=implode($this->separators[0],$sorts);
return $controller->createUrl($this->route,$params);
}
/**
* Returns the real definition of an attribute given its name.
*
* The resolution is based on {@link attributes} and {@link CActiveRecord::attributeNames}.
* <ul>
* <li>When {@link attributes} is an empty array, if the name refers to an attribute of {@link modelClass},
* then the name is returned back.</li>
* <li>When {@link attributes} is not empty, if the name refers to an attribute declared in {@link attributes},
* then the corresponding virtual attribute definition is returned. Starting from version 1.1.3, if {@link attributes}
* contains a star ('*') element, the name will also be used to match against all model attributes.</li>
* <li>In all other cases, false is returned, meaning the name does not refer to a valid attribute.</li>
* </ul>
* @param string $attribute the attribute name that the user requests to sort on
* @return mixed the attribute name or the virtual attribute definition. False if the attribute cannot be sorted.
*/
public function resolveAttribute($attribute)
{
if($this->attributes!==array())
$attributes=$this->attributes;
else if($this->modelClass!==null)
$attributes=CActiveRecord::model($this->modelClass)->attributeNames();
else
return false;
foreach($attributes as $name=>$definition)
{
if(is_string($name))
{
if($name===$attribute)
return $definition;
}
else if($definition==='*')
{
if($this->modelClass!==null && CActiveRecord::model($this->modelClass)->hasAttribute($attribute))
return $attribute;
}
else if($definition===$attribute)
return $attribute;
}
return false;
}
/**
* Creates a hyperlink based on the given label and URL.
* You may override this method to customize the link generation.
* @param string $attribute the name of the attribute that this link is for
* @param string $label the label of the hyperlink
* @param string $url the URL
* @param array $htmlOptions additional HTML options
* @return string the generated hyperlink
*/
protected function createLink($attribute,$label,$url,$htmlOptions)
{
return CHtml::link($label,$url,$htmlOptions);
}
}
\ No newline at end of file
<?php
/**
* CTheme class file.
*
* @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/
*/
/**
* CTheme represents an application theme.
*
* @property string $name Theme name.
* @property string $baseUrl The relative URL to the theme folder (without ending slash).
* @property string $basePath The file path to the theme folder.
* @property string $viewPath The path for controller views. Defaults to 'ThemeRoot/views'.
* @property string $systemViewPath The path for system views. Defaults to 'ThemeRoot/views/system'.
* @property string $skinPath The path for widget skins. Defaults to 'ThemeRoot/views/skins'.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id$
* @package system.web
* @since 1.0
*/
class CTheme extends CComponent
{
private $_name;
private $_basePath;
private $_baseUrl;
/**
* Constructor.
* @param string $name name of the theme
* @param string $basePath base theme path
* @param string $baseUrl base theme URL
*/
public function __construct($name,$basePath,$baseUrl)
{
$this->_name=$name;
$this->_baseUrl=$baseUrl;
$this->_basePath=$basePath;
}
/**
* @return string theme name
*/
public function getName()
{
return $this->_name;
}
/**
* @return string the relative URL to the theme folder (without ending slash)
*/
public function getBaseUrl()
{
return $this->_baseUrl;
}
/**
* @return string the file path to the theme folder
*/
public function getBasePath()
{
return $this->_basePath;
}
/**
* @return string the path for controller views. Defaults to 'ThemeRoot/views'.
*/
public function getViewPath()
{
return $this->_basePath.DIRECTORY_SEPARATOR.'views';
}
/**
* @return string the path for system views. Defaults to 'ThemeRoot/views/system'.
*/
public function getSystemViewPath()
{
return $this->getViewPath().DIRECTORY_SEPARATOR.'system';
}
/**
* @return string the path for widget skins. Defaults to 'ThemeRoot/views/skins'.
* @since 1.1
*/
public function getSkinPath()
{
return $this->getViewPath().DIRECTORY_SEPARATOR.'skins';
}
/**
* Finds the view file for the specified controller's view.
* @param CController $controller the controller
* @param string $viewName the view name
* @return string the view file path. False if the file does not exist.
*/
public function getViewFile($controller,$viewName)
{
$moduleViewPath=$this->getViewPath();
if(($module=$controller->getModule())!==null)
$moduleViewPath.='/'.$module->getId();
return $controller->resolveViewFile($viewName,$this->getViewPath().'/'.$controller->getUniqueId(),$this->getViewPath(),$moduleViewPath);
}
/**
* Finds the layout file for the specified controller's layout.
* @param CController $controller the controller
* @param string $layoutName the layout name
* @return string the layout file path. False if the file does not exist.
*/
public function getLayoutFile($controller,$layoutName)
{
$moduleViewPath=$basePath=$this->getViewPath();
$module=$controller->getModule();
if(empty($layoutName))
{
while($module!==null)
{
if($module->layout===false)
return false;
if(!empty($module->layout))
break;
$module=$module->getParentModule();
}
if($module===null)
$layoutName=Yii::app()->layout;
else
{
$layoutName=$module->layout;
$moduleViewPath.='/'.$module->getId();
}
}
else if($module!==null)
$moduleViewPath.='/'.$module->getId();
return $controller->resolveViewFile($layoutName,$moduleViewPath.'/layouts',$basePath,$moduleViewPath);
}
}
<?php
/**
* CThemeManager class file.
*
* @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/
*/
/**
* CThemeManager manages the themes for the Web application.
*
* A theme is a collection of view/layout files and resource files
* (e.g. css, image, js files). When a theme is active, {@link CController}
* will look for the specified view/layout under the theme folder first.
* The corresponding view/layout files will be used if the theme provides them.
* Otherwise, the default view/layout files will be used.
*
* By default, each theme is organized as a directory whose name is the theme name.
* All themes are located under the "WebRootPath/themes" directory.
*
* To activate a theme, set the {@link CWebApplication::setTheme theme} property
* to be the name of that theme.
*
* Since a self-contained theme often contains resource files that are made
* Web accessible, please make sure the view/layout files are protected from Web access.
*
* @property array $themeNames List of available theme names.
* @property string $basePath The base path for all themes. Defaults to "WebRootPath/themes".
* @property string $baseUrl The base URL for all themes. Defaults to "/WebRoot/themes".
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id$
* @package system.web
* @since 1.0
*/
class CThemeManager extends CApplicationComponent
{
/**
* default themes base path
*/
const DEFAULT_BASEPATH='themes';
/**
* @var string the name of the theme class for representing a theme.
* Defaults to {@link CTheme}. This can also be a class name in dot syntax.
*/
public $themeClass='CTheme';
private $_basePath=null;
private $_baseUrl=null;
/**
* @param string $name name of the theme to be retrieved
* @return CTheme the theme retrieved. Null if the theme does not exist.
*/
public function getTheme($name)
{
$themePath=$this->getBasePath().DIRECTORY_SEPARATOR.$name;
if(is_dir($themePath))
{
$class=Yii::import($this->themeClass, true);
return new $class($name,$themePath,$this->getBaseUrl().'/'.$name);
}
else
return null;
}
/**
* @return array list of available theme names
*/
public function getThemeNames()
{
static $themes;
if($themes===null)
{
$themes=array();
$basePath=$this->getBasePath();
$folder=@opendir($basePath);
while(($file=@readdir($folder))!==false)
{
if($file!=='.' && $file!=='..' && $file!=='.svn' && $file!=='.gitignore' && is_dir($basePath.DIRECTORY_SEPARATOR.$file))
$themes[]=$file;
}
closedir($folder);
sort($themes);
}
return $themes;
}
/**
* @return string the base path for all themes. Defaults to "WebRootPath/themes".
*/
public function getBasePath()
{
if($this->_basePath===null)
$this->setBasePath(dirname(Yii::app()->getRequest()->getScriptFile()).DIRECTORY_SEPARATOR.self::DEFAULT_BASEPATH);
return $this->_basePath;
}
/**
* @param string $value the base path for all themes.
* @throws CException if the base path does not exist
*/
public function setBasePath($value)
{
$this->_basePath=realpath($value);
if($this->_basePath===false || !is_dir($this->_basePath))
throw new CException(Yii::t('yii','Theme directory "{directory}" does not exist.',array('{directory}'=>$value)));
}
/**
* @return string the base URL for all themes. Defaults to "/WebRoot/themes".
*/
public function getBaseUrl()
{
if($this->_baseUrl===null)
$this->_baseUrl=Yii::app()->getBaseUrl().'/'.self::DEFAULT_BASEPATH;
return $this->_baseUrl;
}
/**
* @param string $value the base URL for all themes.
*/
public function setBaseUrl($value)
{
$this->_baseUrl=rtrim($value,'/');
}
}
<?php
/**
* CUrlManager class file
*
* @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/
*/
/**
* CUrlManager manages the URLs of Yii Web applications.
*
* It provides URL construction ({@link createUrl()}) as well as parsing ({@link parseUrl()}) functionality.
*
* URLs managed via CUrlManager can be in one of the following two formats,
* by setting {@link setUrlFormat urlFormat} property:
* <ul>
* <li>'path' format: /path/to/EntryScript.php/name1/value1/name2/value2...</li>
* <li>'get' format: /path/to/EntryScript.php?name1=value1&name2=value2...</li>
* </ul>
*
* When using 'path' format, CUrlManager uses a set of {@link setRules rules} to:
* <ul>
* <li>parse the requested URL into a route ('ControllerID/ActionID') and GET parameters;</li>
* <li>create URLs based on the given route and GET parameters.</li>
* </ul>
*
* A rule consists of a route and a pattern. The latter is used by CUrlManager to determine
* which rule is used for parsing/creating URLs. A pattern is meant to match the path info
* part of a URL. It may contain named parameters using the syntax '&lt;ParamName:RegExp&gt;'.
*
* When parsing a URL, a matching rule will extract the named parameters from the path info
* and put them into the $_GET variable; when creating a URL, a matching rule will extract
* the named parameters from $_GET and put them into the path info part of the created URL.
*
* If a pattern ends with '/*', it means additional GET parameters may be appended to the path
* info part of the URL; otherwise, the GET parameters can only appear in the query string part.
*
* To specify URL rules, set the {@link setRules rules} property as an array of rules (pattern=>route).
* For example,
* <pre>
* array(
* 'articles'=>'article/list',
* 'article/<id:\d+>/*'=>'article/read',
* )
* </pre>
* Two rules are specified in the above:
* <ul>
* <li>The first rule says that if the user requests the URL '/path/to/index.php/articles',
* it should be treated as '/path/to/index.php/article/list'; and vice versa applies
* when constructing such a URL.</li>
* <li>The second rule contains a named parameter 'id' which is specified using
* the &lt;ParamName:RegExp&gt; syntax. It says that if the user requests the URL
* '/path/to/index.php/article/13', it should be treated as '/path/to/index.php/article/read?id=13';
* and vice versa applies when constructing such a URL.</li>
* </ul>
*
* The route part may contain references to named parameters defined in the pattern part.
* This allows a rule to be applied to different routes based on matching criteria.
* For example,
* <pre>
* array(
* '<_c:(post|comment)>/<id:\d+>/<_a:(create|update|delete)>'=>'<_c>/<_a>',
* '<_c:(post|comment)>/<id:\d+>'=>'<_c>/view',
* '<_c:(post|comment)>s/*'=>'<_c>/list',
* )
* </pre>
* In the above, we use two named parameters '<_c>' and '<_a>' in the route part. The '<_c>'
* parameter matches either 'post' or 'comment', while the '<_a>' parameter matches an action ID.
*
* Like normal rules, these rules can be used for both parsing and creating URLs.
* For example, using the rules above, the URL '/index.php/post/123/create'
* would be parsed as the route 'post/create' with GET parameter 'id' being 123.
* And given the route 'post/list' and GET parameter 'page' being 2, we should get a URL
* '/index.php/posts/page/2'.
*
* It is also possible to include hostname into the rules for parsing and creating URLs.
* One may extract part of the hostname to be a GET parameter.
* For example, the URL <code>http://admin.example.com/en/profile</code> may be parsed into GET parameters
* <code>user=admin</code> and <code>lang=en</code>. On the other hand, rules with hostname may also be used to
* create URLs with parameterized hostnames.
*
* In order to use parameterized hostnames, simply declare URL rules with host info, e.g.:
* <pre>
* array(
* 'http://<user:\w+>.example.com/<lang:\w+>/profile' => 'user/profile',
* )
* </pre>
*
* Starting from version 1.1.8, one can write custom URL rule classes and use them for one or several URL rules.
* For example,
* <pre>
* array(
* // a standard rule
* '<action:(login|logout)>' => 'site/<action>',
* // a custom rule using data in DB
* array(
* 'class' => 'application.components.MyUrlRule',
* 'connectionID' => 'db',
* ),
* )
* </pre>
* Please note that the custom URL rule class should extend from {@link CBaseUrlRule} and
* implement the following two methods,
* <ul>
* <li>{@link CBaseUrlRule::createUrl()}</li>
* <li>{@link CBaseUrlRule::parseUrl()}</li>
* </ul>
*
* CUrlManager is a default application component that may be accessed via
* {@link CWebApplication::getUrlManager()}.
*
* @property string $baseUrl The base URL of the application (the part after host name and before query string).
* If {@link showScriptName} is true, it will include the script name part.
* Otherwise, it will not, and the ending slashes are stripped off.
* @property string $urlFormat The URL format. Defaults to 'path'. Valid values include 'path' and 'get'.
* Please refer to the guide for more details about the difference between these two formats.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id$
* @package system.web
* @since 1.0
*/
class CUrlManager extends CApplicationComponent
{
const CACHE_KEY='Yii.CUrlManager.rules';
const GET_FORMAT='get';
const PATH_FORMAT='path';
/**
* @var array the URL rules (pattern=>route).
*/
public $rules=array();
/**
* @var string the URL suffix used when in 'path' format.
* For example, ".html" can be used so that the URL looks like pointing to a static HTML page. Defaults to empty.
*/
public $urlSuffix='';
/**
* @var boolean whether to show entry script name in the constructed URL. Defaults to true.
*/
public $showScriptName=true;
/**
* @var boolean whether to append GET parameters to the path info part. Defaults to true.
* This property is only effective when {@link urlFormat} is 'path' and is mainly used when
* creating URLs. When it is true, GET parameters will be appended to the path info and
* separate from each other using slashes. If this is false, GET parameters will be in query part.
*/
public $appendParams=true;
/**
* @var string the GET variable name for route. Defaults to 'r'.
*/
public $routeVar='r';
/**
* @var boolean whether routes are case-sensitive. Defaults to true. By setting this to false,
* the route in the incoming request will be turned to lower case first before further processing.
* As a result, you should follow the convention that you use lower case when specifying
* controller mapping ({@link CWebApplication::controllerMap}) and action mapping
* ({@link CController::actions}). Also, the directory names for organizing controllers should
* be in lower case.
*/
public $caseSensitive=true;
/**
* @var boolean whether the GET parameter values should match the corresponding
* sub-patterns in a rule before using it to create a URL. Defaults to false, meaning
* a rule will be used for creating a URL only if its route and parameter names match the given ones.
* If this property is set true, then the given parameter values must also match the corresponding
* parameter sub-patterns. Note that setting this property to true will degrade performance.
* @since 1.1.0
*/
public $matchValue=false;
/**
* @var string the ID of the cache application component that is used to cache the parsed URL rules.
* Defaults to 'cache' which refers to the primary cache application component.
* Set this property to false if you want to disable caching URL rules.
*/
public $cacheID='cache';
/**
* @var boolean whether to enable strict URL parsing.
* This property is only effective when {@link urlFormat} is 'path'.
* If it is set true, then an incoming URL must match one of the {@link rules URL rules}.
* Otherwise, it will be treated as an invalid request and trigger a 404 HTTP exception.
* Defaults to false.
*/
public $useStrictParsing=false;
/**
* @var string the class name or path alias for the URL rule instances. Defaults to 'CUrlRule'.
* If you change this to something else, please make sure that the new class must extend from
* {@link CBaseUrlRule} and have the same constructor signature as {@link CUrlRule}.
* It must also be serializable and autoloadable.
* @since 1.1.8
*/
public $urlRuleClass='CUrlRule';
private $_urlFormat=self::GET_FORMAT;
private $_rules=array();
private $_baseUrl;
/**
* Initializes the application component.
*/
public function init()
{
parent::init();
$this->processRules();
}
/**
* Processes the URL rules.
*/
protected function processRules()
{
if(empty($this->rules) || $this->getUrlFormat()===self::GET_FORMAT)
return;
if($this->cacheID!==false && ($cache=Yii::app()->getComponent($this->cacheID))!==null)
{
$hash=md5(serialize($this->rules));
if(($data=$cache->get(self::CACHE_KEY))!==false && isset($data[1]) && $data[1]===$hash)
{
$this->_rules=$data[0];
return;
}
}
foreach($this->rules as $pattern=>$route)
$this->_rules[]=$this->createUrlRule($route,$pattern);
if(isset($cache))
$cache->set(self::CACHE_KEY,array($this->_rules,$hash));
}
/**
* Adds new URL rules.
* In order to make the new rules effective, this method must be called BEFORE
* {@link CWebApplication::processRequest}.
* @param array $rules new URL rules (pattern=>route).
* @param boolean $append whether the new URL rules should be appended to the existing ones. If false,
* they will be inserted at the beginning.
* @since 1.1.4
*/
public function addRules($rules, $append=true)
{
if ($append)
{
foreach($rules as $pattern=>$route)
$this->_rules[]=$this->createUrlRule($route,$pattern);
}
else
{
foreach($rules as $pattern=>$route)
array_unshift($this->_rules, $this->createUrlRule($route,$pattern));
}
}
/**
* Creates a URL rule instance.
* The default implementation returns a CUrlRule object.
* @param mixed $route the route part of the rule. This could be a string or an array
* @param string $pattern the pattern part of the rule
* @return CUrlRule the URL rule instance
* @since 1.1.0
*/
protected function createUrlRule($route,$pattern)
{
if(is_array($route) && isset($route['class']))
return $route;
else
return new $this->urlRuleClass($route,$pattern);
}
/**
* Constructs a URL.
* @param string $route the controller and the action (e.g. article/read)
* @param array $params list of GET parameters (name=>value). Both the name and value will be URL-encoded.
* If the name is '#', the corresponding value will be treated as an anchor
* and will be appended at the end of the URL.
* @param string $ampersand the token separating name-value pairs in the URL. Defaults to '&'.
* @return string the constructed URL
*/
public function createUrl($route,$params=array(),$ampersand='&')
{
unset($params[$this->routeVar]);
foreach($params as $i=>$param)
if($param===null)
$params[$i]='';
if(isset($params['#']))
{
$anchor='#'.$params['#'];
unset($params['#']);
}
else
$anchor='';
$route=trim($route,'/');
foreach($this->_rules as $i=>$rule)
{
if(is_array($rule))
$this->_rules[$i]=$rule=Yii::createComponent($rule);
if(($url=$rule->createUrl($this,$route,$params,$ampersand))!==false)
{
if($rule->hasHostInfo)
return $url==='' ? '/'.$anchor : $url.$anchor;
else
return $this->getBaseUrl().'/'.$url.$anchor;
}
}
return $this->createUrlDefault($route,$params,$ampersand).$anchor;
}
/**
* Creates a URL based on default settings.
* @param string $route the controller and the action (e.g. article/read)
* @param array $params list of GET parameters
* @param string $ampersand the token separating name-value pairs in the URL.
* @return string the constructed URL
*/
protected function createUrlDefault($route,$params,$ampersand)
{
if($this->getUrlFormat()===self::PATH_FORMAT)
{
$url=rtrim($this->getBaseUrl().'/'.$route,'/');
if($this->appendParams)
{
$url=rtrim($url.'/'.$this->createPathInfo($params,'/','/'),'/');
return $route==='' ? $url : $url.$this->urlSuffix;
}
else
{
if($route!=='')
$url.=$this->urlSuffix;
$query=$this->createPathInfo($params,'=',$ampersand);
return $query==='' ? $url : $url.'?'.$query;
}
}
else
{
$url=$this->getBaseUrl();
if(!$this->showScriptName)
$url.='/';
if($route!=='')
{
$url.='?'.$this->routeVar.'='.$route;
if(($query=$this->createPathInfo($params,'=',$ampersand))!=='')
$url.=$ampersand.$query;
}
else if(($query=$this->createPathInfo($params,'=',$ampersand))!=='')
$url.='?'.$query;
return $url;
}
}
/**
* Parses the user request.
* @param CHttpRequest $request the request application component
* @return string the route (controllerID/actionID) and perhaps GET parameters in path format.
*/
public function parseUrl($request)
{
if($this->getUrlFormat()===self::PATH_FORMAT)
{
$rawPathInfo=$request->getPathInfo();
$pathInfo=$this->removeUrlSuffix($rawPathInfo,$this->urlSuffix);
foreach($this->_rules as $i=>$rule)
{
if(is_array($rule))
$this->_rules[$i]=$rule=Yii::createComponent($rule);
if(($r=$rule->parseUrl($this,$request,$pathInfo,$rawPathInfo))!==false)
return isset($_GET[$this->routeVar]) ? $_GET[$this->routeVar] : $r;
}
if($this->useStrictParsing)
throw new CHttpException(404,Yii::t('yii','Unable to resolve the request "{route}".',
array('{route}'=>$pathInfo)));
else
return $pathInfo;
}
else if(isset($_GET[$this->routeVar]))
return $_GET[$this->routeVar];
else if(isset($_POST[$this->routeVar]))
return $_POST[$this->routeVar];
else
return '';
}
/**
* Parses a path info into URL segments and saves them to $_GET and $_REQUEST.
* @param string $pathInfo path info
*/
public function parsePathInfo($pathInfo)
{
if($pathInfo==='')
return;
$segs=explode('/',$pathInfo.'/');
$n=count($segs);
for($i=0;$i<$n-1;$i+=2)
{
$key=$segs[$i];
if($key==='') continue;
$value=$segs[$i+1];
if(($pos=strpos($key,'['))!==false && ($m=preg_match_all('/\[(.*?)\]/',$key,$matches))>0)
{
$name=substr($key,0,$pos);
for($j=$m-1;$j>=0;--$j)
{
if($matches[1][$j]==='')
$value=array($value);
else
$value=array($matches[1][$j]=>$value);
}
if(isset($_GET[$name]) && is_array($_GET[$name]))
$value=CMap::mergeArray($_GET[$name],$value);
$_REQUEST[$name]=$_GET[$name]=$value;
}
else
$_REQUEST[$key]=$_GET[$key]=$value;
}
}
/**
* Creates a path info based on the given parameters.
* @param array $params list of GET parameters
* @param string $equal the separator between name and value
* @param string $ampersand the separator between name-value pairs
* @param string $key this is used internally.
* @return string the created path info
*/
public function createPathInfo($params,$equal,$ampersand, $key=null)
{
$pairs = array();
foreach($params as $k => $v)
{
if ($key!==null)
$k = $key.'['.$k.']';
if (is_array($v))
$pairs[]=$this->createPathInfo($v,$equal,$ampersand, $k);
else
$pairs[]=urlencode($k).$equal.urlencode($v);
}
return implode($ampersand,$pairs);
}
/**
* Removes the URL suffix from path info.
* @param string $pathInfo path info part in the URL
* @param string $urlSuffix the URL suffix to be removed
* @return string path info with URL suffix removed.
*/
public function removeUrlSuffix($pathInfo,$urlSuffix)
{
if($urlSuffix!=='' && substr($pathInfo,-strlen($urlSuffix))===$urlSuffix)
return substr($pathInfo,0,-strlen($urlSuffix));
else
return $pathInfo;
}
/**
* Returns the base URL of the application.
* @return string the base URL of the application (the part after host name and before query string).
* If {@link showScriptName} is true, it will include the script name part.
* Otherwise, it will not, and the ending slashes are stripped off.
*/
public function getBaseUrl()
{
if($this->_baseUrl!==null)
return $this->_baseUrl;
else
{
if($this->showScriptName)
$this->_baseUrl=Yii::app()->getRequest()->getScriptUrl();
else
$this->_baseUrl=Yii::app()->getRequest()->getBaseUrl();
return $this->_baseUrl;
}
}
/**
* Sets the base URL of the application (the part after host name and before query string).
* This method is provided in case the {@link baseUrl} cannot be determined automatically.
* The ending slashes should be stripped off. And you are also responsible to remove the script name
* if you set {@link showScriptName} to be false.
* @param string $value the base URL of the application
* @since 1.1.1
*/
public function setBaseUrl($value)
{
$this->_baseUrl=$value;
}
/**
* Returns the URL format.
* @return string the URL format. Defaults to 'path'. Valid values include 'path' and 'get'.
* Please refer to the guide for more details about the difference between these two formats.
*/
public function getUrlFormat()
{
return $this->_urlFormat;
}
/**
* Sets the URL format.
* @param string $value the URL format. It must be either 'path' or 'get'.
*/
public function setUrlFormat($value)
{
if($value===self::PATH_FORMAT || $value===self::GET_FORMAT)
$this->_urlFormat=$value;
else
throw new CException(Yii::t('yii','CUrlManager.UrlFormat must be either "path" or "get".'));
}
}
/**
* CBaseUrlRule is the base class for a URL rule class.
*
* Custom URL rule classes should extend from this class and implement two methods:
* {@link createUrl} and {@link parseUrl}.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id$
* @package system.web
* @since 1.1.8
*/
abstract class CBaseUrlRule extends CComponent
{
/**
* @var boolean whether this rule will also parse the host info part. Defaults to false.
*/
public $hasHostInfo=false;
/**
* Creates a URL based on this rule.
* @param CUrlManager $manager the manager
* @param string $route the route
* @param array $params list of parameters (name=>value) associated with the route
* @param string $ampersand the token separating name-value pairs in the URL.
* @return mixed the constructed URL. False if this rule does not apply.
*/
abstract public function createUrl($manager,$route,$params,$ampersand);
/**
* Parses a URL based on this rule.
* @param CUrlManager $manager the URL manager
* @param CHttpRequest $request the request object
* @param string $pathInfo path info part of the URL (URL suffix is already removed based on {@link CUrlManager::urlSuffix})
* @param string $rawPathInfo path info that contains the potential URL suffix
* @return mixed the route that consists of the controller ID and action ID. False if this rule does not apply.
*/
abstract public function parseUrl($manager,$request,$pathInfo,$rawPathInfo);
}
/**
* CUrlRule represents a URL formatting/parsing rule.
*
* It mainly consists of two parts: route and pattern. The former classifies
* the rule so that it only applies to specific controller-action route.
* The latter performs the actual formatting and parsing role. The pattern
* may have a set of named parameters.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id$
* @package system.web
* @since 1.0
*/
class CUrlRule extends CBaseUrlRule
{
/**
* @var string the URL suffix used for this rule.
* For example, ".html" can be used so that the URL looks like pointing to a static HTML page.
* Defaults to null, meaning using the value of {@link CUrlManager::urlSuffix}.
*/
public $urlSuffix;
/**
* @var boolean whether the rule is case sensitive. Defaults to null, meaning
* using the value of {@link CUrlManager::caseSensitive}.
*/
public $caseSensitive;
/**
* @var array the default GET parameters (name=>value) that this rule provides.
* When this rule is used to parse the incoming request, the values declared in this property
* will be injected into $_GET.
*/
public $defaultParams=array();
/**
* @var boolean whether the GET parameter values should match the corresponding
* sub-patterns in the rule when creating a URL. Defaults to null, meaning using the value
* of {@link CUrlManager::matchValue}. When this property is false, it means
* a rule will be used for creating a URL if its route and parameter names match the given ones.
* If this property is set true, then the given parameter values must also match the corresponding
* parameter sub-patterns. Note that setting this property to true will degrade performance.
* @since 1.1.0
*/
public $matchValue;
/**
* @var string the HTTP verb (e.g. GET, POST, DELETE) that this rule should match.
* If this rule can match multiple verbs, please separate them with commas.
* If this property is not set, the rule can match any verb.
* Note that this property is only used when parsing a request. It is ignored for URL creation.
* @since 1.1.7
*/
public $verb;
/**
* @var boolean whether this rule is only used for request parsing.
* Defaults to false, meaning the rule is used for both URL parsing and creation.
* @since 1.1.7
*/
public $parsingOnly=false;
/**
* @var string the controller/action pair
*/
public $route;
/**
* @var array the mapping from route param name to token name (e.g. _r1=><1>)
*/
public $references=array();
/**
* @var string the pattern used to match route
*/
public $routePattern;
/**
* @var string regular expression used to parse a URL
*/
public $pattern;
/**
* @var string template used to construct a URL
*/
public $template;
/**
* @var array list of parameters (name=>regular expression)
*/
public $params=array();
/**
* @var boolean whether the URL allows additional parameters at the end of the path info.
*/
public $append;
/**
* @var boolean whether host info should be considered for this rule
*/
public $hasHostInfo;
/**
* Constructor.
* @param string $route the route of the URL (controller/action)
* @param string $pattern the pattern for matching the URL
*/
public function __construct($route,$pattern)
{
if(is_array($route))
{
foreach(array('urlSuffix', 'caseSensitive', 'defaultParams', 'matchValue', 'verb', 'parsingOnly') as $name)
{
if(isset($route[$name]))
$this->$name=$route[$name];
}
if(isset($route['pattern']))
$pattern=$route['pattern'];
$route=$route[0];
}
$this->route=trim($route,'/');
$tr2['/']=$tr['/']='\\/';
if(strpos($route,'<')!==false && preg_match_all('/<(\w+)>/',$route,$matches2))
{
foreach($matches2[1] as $name)
$this->references[$name]="<$name>";
}
$this->hasHostInfo=!strncasecmp($pattern,'http://',7) || !strncasecmp($pattern,'https://',8);
if($this->verb!==null)
$this->verb=preg_split('/[\s,]+/',strtoupper($this->verb),-1,PREG_SPLIT_NO_EMPTY);
if(preg_match_all('/<(\w+):?(.*?)?>/',$pattern,$matches))
{
$tokens=array_combine($matches[1],$matches[2]);
foreach($tokens as $name=>$value)
{
if($value==='')
$value='[^\/]+';
$tr["<$name>"]="(?P<$name>$value)";
if(isset($this->references[$name]))
$tr2["<$name>"]=$tr["<$name>"];
else
$this->params[$name]=$value;
}
}
$p=rtrim($pattern,'*');
$this->append=$p!==$pattern;
$p=trim($p,'/');
$this->template=preg_replace('/<(\w+):?.*?>/','<$1>',$p);
$this->pattern='/^'.strtr($this->template,$tr).'\/';
if($this->append)
$this->pattern.='/u';
else
$this->pattern.='$/u';
if($this->references!==array())
$this->routePattern='/^'.strtr($this->route,$tr2).'$/u';
if(YII_DEBUG && @preg_match($this->pattern,'test')===false)
throw new CException(Yii::t('yii','The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.',
array('{route}'=>$route,'{pattern}'=>$pattern)));
}
/**
* Creates a URL based on this rule.
* @param CUrlManager $manager the manager
* @param string $route the route
* @param array $params list of parameters
* @param string $ampersand the token separating name-value pairs in the URL.
* @return mixed the constructed URL or false on error
*/
public function createUrl($manager,$route,$params,$ampersand)
{
if($this->parsingOnly)
return false;
if($manager->caseSensitive && $this->caseSensitive===null || $this->caseSensitive)
$case='';
else
$case='i';
$tr=array();
if($route!==$this->route)
{
if($this->routePattern!==null && preg_match($this->routePattern.$case,$route,$matches))
{
foreach($this->references as $key=>$name)
$tr[$name]=$matches[$key];
}
else
return false;
}
foreach($this->defaultParams as $key=>$value)
{
if(isset($params[$key]))
{
if($params[$key]==$value)
unset($params[$key]);
else
return false;
}
}
foreach($this->params as $key=>$value)
if(!isset($params[$key]))
return false;
if($manager->matchValue && $this->matchValue===null || $this->matchValue)
{
foreach($this->params as $key=>$value)
{
if(!preg_match('/\A'.$value.'\z/u'.$case,$params[$key]))
return false;
}
}
foreach($this->params as $key=>$value)
{
$tr["<$key>"]=urlencode($params[$key]);
unset($params[$key]);
}
$suffix=$this->urlSuffix===null ? $manager->urlSuffix : $this->urlSuffix;
$url=strtr($this->template,$tr);
if($this->hasHostInfo)
{
$hostInfo=Yii::app()->getRequest()->getHostInfo();
if(stripos($url,$hostInfo)===0)
$url=substr($url,strlen($hostInfo));
}
if(empty($params))
return $url!=='' ? $url.$suffix : $url;
if($this->append)
$url.='/'.$manager->createPathInfo($params,'/','/').$suffix;
else
{
if($url!=='')
$url.=$suffix;
$url.='?'.$manager->createPathInfo($params,'=',$ampersand);
}
return $url;
}
/**
* Parses a URL based on this rule.
* @param CUrlManager $manager the URL manager
* @param CHttpRequest $request the request object
* @param string $pathInfo path info part of the URL
* @param string $rawPathInfo path info that contains the potential URL suffix
* @return mixed the route that consists of the controller ID and action ID or false on error
*/
public function parseUrl($manager,$request,$pathInfo,$rawPathInfo)
{
if($this->verb!==null && !in_array($request->getRequestType(), $this->verb, true))
return false;
if($manager->caseSensitive && $this->caseSensitive===null || $this->caseSensitive)
$case='';
else
$case='i';
if($this->urlSuffix!==null)
$pathInfo=$manager->removeUrlSuffix($rawPathInfo,$this->urlSuffix);
// URL suffix required, but not found in the requested URL
if($manager->useStrictParsing && $pathInfo===$rawPathInfo)
{
$urlSuffix=$this->urlSuffix===null ? $manager->urlSuffix : $this->urlSuffix;
if($urlSuffix!='' && $urlSuffix!=='/')
return false;
}
if($this->hasHostInfo)
$pathInfo=strtolower($request->getHostInfo()).rtrim('/'.$pathInfo,'/');
$pathInfo.='/';
if(preg_match($this->pattern.$case,$pathInfo,$matches))
{
foreach($this->defaultParams as $name=>$value)
{
if(!isset($_GET[$name]))
$_REQUEST[$name]=$_GET[$name]=$value;
}
$tr=array();
foreach($matches as $key=>$value)
{
if(isset($this->references[$key]))
$tr[$this->references[$key]]=$value;
else if(isset($this->params[$key]))
$_REQUEST[$key]=$_GET[$key]=$value;
}
if($pathInfo!==$matches[0]) // there're additional GET params
$manager->parsePathInfo(ltrim(substr($pathInfo,strlen($matches[0])),'/'));
if($this->routePattern!==null)
return strtr($this->route,$tr);
else
return $this->route;
}
else
return false;
}
}
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