Commit 8f9cfa2c by Qiang Xue

draft of script/asset management is done.

parent 5b412b8f
...@@ -86,11 +86,14 @@ class FileHelper ...@@ -86,11 +86,14 @@ class FileHelper
{ {
if (function_exists('finfo_open')) { if (function_exists('finfo_open')) {
$info = finfo_open(FILEINFO_MIME_TYPE, $magicFile); $info = finfo_open(FILEINFO_MIME_TYPE, $magicFile);
if ($info && ($result = finfo_file($info, $file)) !== false) { if ($info) {
$result = finfo_file($info, $file);
finfo_close($info); finfo_close($info);
if ($result !== false) {
return $result; return $result;
} }
} }
}
return $checkExtension ? self::getMimeTypeByExtension($file) : null; return $checkExtension ? self::getMimeTypeByExtension($file) : null;
} }
...@@ -122,4 +125,49 @@ class FileHelper ...@@ -122,4 +125,49 @@ class FileHelper
} }
/**
* Copies a whole directory as another one.
* The files and sub-directories will also be copied over.
* @param string $src the source directory
* @param string $dst the destination directory
* @param array $options options for directory copy. Valid options are:
*
* - dirMode: integer, the permission to be set for newly copied directories. Defaults to 0777.
* - fileMode: integer, the permission to be set for newly copied files. Defaults to the current environment setting.
* - filter: callback, a PHP callback that is called for every sub-directory and file to
* determine if it should be copied. The signature of the callback should be:
*
* ~~~
* // $path is the file/directory path to be copied
* function ($path) {
* // return a boolean indicating if $path should be copied
* }
* ~~~
*/
public static function copyDirectory($src, $dst, $options = array())
{
if (!is_dir($dst)) {
mkdir($dst, isset($options['dirMode']) ? $options['dirMode'] : 0777, true);
}
$handle = opendir($src);
while (($file = readdir($handle)) !== false) {
if ($file === '.' || $file === '..') {
continue;
}
$srcPath = $src . DIRECTORY_SEPARATOR . $file;
if (!isset($options['filter']) || call_user_func($options['filter'], $srcPath)) {
$dstPath = $dst . DIRECTORY_SEPARATOR . $file;
if (is_file($srcPath)) {
copy($srcPath, $dstPath);
if (isset($options['fileMode'])) {
chmod($dstPath, $options['fileMode']);
}
} else {
static::copyDirectory($srcPath, $dstPath, $options);
}
}
}
closedir($handle);
}
} }
\ No newline at end of file
...@@ -11,27 +11,6 @@ use Yii; ...@@ -11,27 +11,6 @@ use Yii;
use yii\base\Object; use yii\base\Object;
/** /**
* Each asset bundle should be declared with the following structure:
*
* ~~~
* array(
* 'basePath' => '...',
* 'baseUrl' => '...', // if missing, the bundle will be published to the "www/assets" folder
* 'js' => array(
* 'js/main.js',
* 'js/menu.js',
* 'js/base.js' => self::POS_HEAD,
* 'css' => array(
* 'css/main.css',
* 'css/menu.css',
* ),
* 'depends' => array(
* 'jquery',
* 'yii',
* 'yii/treeview',
* ),
* )
* ~~~
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
...@@ -61,9 +40,26 @@ class AssetBundle extends Object ...@@ -61,9 +40,26 @@ class AssetBundle extends Object
* *
* Note that you should not use backward slashes "\" to specify JavaScript files. * Note that you should not use backward slashes "\" to specify JavaScript files.
* *
* A JavaScript file can be associated with the options: // todo * Each JavaScript file may be associated with options. In this case, the array key
* should be the JavaScript file path, while the corresponding array value should
* be the option array. The options will be passed to [[ViewContent::registerJsFile()]].
*/ */
public $js = array(); public $js = array();
/**
* @var array list of CSS files that this bundle contains. Each CSS file can
* be specified in one of the three formats:
*
* - a relative path: a path relative to [[basePath]] if [[basePath]] is set,
* or a URL relative to [[baseUrl]] if [[basePath]] is not set;
* - an absolute URL;
* - a path alias that can be resolved into a relative path or an absolute URL.
*
* Note that you should not use backward slashes "\" to specify CSS files.
*
* Each CSS file may be associated with options. In this case, the array key
* should be the CSS file path, while the corresponding array value should
* be the option array. The options will be passed to [[ViewContent::registerCssFile()]].
*/
public $css = array(); public $css = array();
/** /**
* @var array list of the bundle names that this bundle depends on * @var array list of the bundle names that this bundle depends on
......
...@@ -11,6 +11,7 @@ use Yii; ...@@ -11,6 +11,7 @@ use Yii;
use yii\base\Component; use yii\base\Component;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
use yii\base\InvalidParamException; use yii\base\InvalidParamException;
use yii\helpers\FileHelper;
/** /**
* *
...@@ -58,24 +59,22 @@ class AssetManager extends Component ...@@ -58,24 +59,22 @@ class AssetManager extends Component
**/ **/
public $excludeFiles = array('.svn', '.gitignore'); public $excludeFiles = array('.svn', '.gitignore');
/** /**
* @var integer the permission to be set for newly generated asset files. * @var integer the permission to be set for newly published asset files.
* This value will be used by PHP chmod function. * This value will be used by PHP chmod() function.
* Defaults to 0666, meaning the file is read-writable by all users. * If not set, the permission will be determined by the current environment.
* @since 1.1.8
*/ */
public $newFileMode = 0666; public $fileMode;
/** /**
* @var integer the permission to be set for newly generated asset directories. * @var integer the permission to be set for newly generated asset directories.
* This value will be used by PHP chmod function. * This value will be used by PHP chmod() function.
* Defaults to 0777, meaning the directory can be read, written and executed by all users. * Defaults to 0777, meaning the directory can be read, written and executed by all users.
* @since 1.1.8
*/ */
public $newDirMode = 0777; public $dirMode = 0777;
/** /**
* @var array published assets * Initializes the component.
* @throws InvalidConfigException if [[basePath]] is invalid
*/ */
private $_published = array();
public function init() public function init()
{ {
parent::init(); parent::init();
...@@ -136,16 +135,22 @@ class AssetManager extends Component ...@@ -136,16 +135,22 @@ class AssetManager extends Component
} }
/** /**
* @var array published assets
*/
private $_published = array();
/**
* Publishes a file or a directory. * 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. * This method will copy the specified file or directory to [[basePath]] so that
* <ul> * it can be accessed via the Web server.
* <li>If the asset is a file, its file modification time will be checked *
* to avoid unnecessary file copying;</li> * If the asset is a file, its file modification time will be checked to avoid
* <li>If the asset is a directory, all files and subdirectories under it will * unnecessary file copying.
* be published recursively. Note, in case $forceCopy is false the method only checks the *
* existence of the target directory to avoid repetitive copying.</li> * If the asset is a directory, all files and subdirectories under it will be published recursively.
* </ul> * Note, in case $forceCopy is false the method only checks the existence of the target
* directory to avoid repetitive copying (which is very expensive).
* *
* Note: On rare scenario, a race condition can develop that will lead to a * 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 * one-time-manifestation of a non-critical problem in the creation of the directory
...@@ -155,23 +160,14 @@ class AssetManager extends Component ...@@ -155,23 +160,14 @@ class AssetManager extends Component
* discussion: http://code.google.com/p/yii/issues/detail?id=2579 * discussion: http://code.google.com/p/yii/issues/detail?id=2579
* *
* @param string $path the asset (file or directory) to be published * @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. * @param boolean $forceCopy whether the asset should ALWAYS be copied even if it is found
* If false, the name will be the hash taken from dirname of the path being published and path mtime. * in the target directory. This parameter is mainly useful during the development stage
* Defaults to false. Set true if the path being published is shared among * when the original assets are being constantly changed. The consequence is that the performance
* 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. * 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 * @return string an absolute URL to the published asset
* @throws CException if the asset to be published does not exist. * @throws InvalidParamException if the asset to be published does not exist.
*/ */
public function publish($path, $hashByName = false, $level = -1, $forceCopy = false) public function publish($path, $forceCopy = false)
{ {
if (isset($this->_published[$path])) { if (isset($this->_published[$path])) {
return $this->_published[$path]; return $this->_published[$path];
...@@ -183,13 +179,13 @@ class AssetManager extends Component ...@@ -183,13 +179,13 @@ class AssetManager extends Component
} }
if (is_file($src)) { if (is_file($src)) {
$dir = $this->hash($hashByName ? basename($src) : dirname($src) . filemtime($src)); $dir = $this->hash(dirname($src) . filemtime($src));
$fileName = basename($src); $fileName = basename($src);
$dstDir = $this->basePath . DIRECTORY_SEPARATOR . $dir; $dstDir = $this->basePath . DIRECTORY_SEPARATOR . $dir;
$dstFile = $dstDir . DIRECTORY_SEPARATOR . $fileName; $dstFile = $dstDir . DIRECTORY_SEPARATOR . $fileName;
if (!is_dir($dstDir)) { if (!is_dir($dstDir)) {
@mkdir($dstDir, $this->newDirMode, true); @mkdir($dstDir, $this->dirMode, true);
} }
...@@ -197,30 +193,27 @@ class AssetManager extends Component ...@@ -197,30 +193,27 @@ class AssetManager extends Component
if (!is_file($dstFile)) { if (!is_file($dstFile)) {
symlink($src, $dstFile); symlink($src, $dstFile);
} }
} elseif (@filemtime($dstFile) < @filemtime($src)) { } elseif (@filemtime($dstFile) < @filemtime($src) || $forceCopy) {
copy($src, $dstFile); copy($src, $dstFile);
@chmod($dstFile, $this->newFileMode); if ($this->fileMode !== null) {
@chmod($dstFile, $this->fileMode);
}
} }
$url = $this->baseUrl . "/$dir/$fileName"; $url = $this->baseUrl . "/$dir/$fileName";
} else { } else {
$dir = $this->hash($hashByName ? basename($src) : $src . filemtime($src)); $dir = $this->hash($src . filemtime($src));
$dstDir = $this->basePath . DIRECTORY_SEPARATOR . $dir; $dstDir = $this->basePath . DIRECTORY_SEPARATOR . $dir;
if ($this->linkAssets) { if ($this->linkAssets) {
if (!is_dir($dstDir)) { if (!is_dir($dstDir)) {
symlink($src, $dstDir); symlink($src, $dstDir);
} }
} else { } elseif (!is_dir($dstDir) || $forceCopy) {
if (!is_dir($dstDir) || $forceCopy) {
FileHelper::copyDirectory($src, $dstDir, array( FileHelper::copyDirectory($src, $dstDir, array(
'exclude' => $this->excludeFiles, 'dirMode' => $this->dirMode,
'level' => $level, 'fileMode' => $this->fileMode,
'newDirMode' => $this->newDirMode,
'newFileMode' => $this->newFileMode,
)); ));
} }
}
$url = $this->baseUrl . '/' . $dir; $url = $this->baseUrl . '/' . $dir;
} }
...@@ -232,20 +225,16 @@ class AssetManager extends Component ...@@ -232,20 +225,16 @@ class AssetManager extends Component
* This method does not perform any publishing. It merely tells you * This method does not perform any publishing. It merely tells you
* if the file or directory is published, where it will go. * if the file or directory is published, where it will go.
* @param string $path directory or file path being published * @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 * @return string the published file path. False if the file or directory does not exist
*/ */
public function getPublishedPath($path, $hashByName = false) public function getPublishedPath($path)
{ {
if (($path = realpath($path)) !== false) { if (($path = realpath($path)) !== false) {
$base = $this->basePath . DIRECTORY_SEPARATOR; $base = $this->basePath . DIRECTORY_SEPARATOR;
if (is_file($path)) { if (is_file($path)) {
return $base . $this->hash($hashByName ? basename($path) : dirname($path) . filemtime($path)) . DIRECTORY_SEPARATOR . basename($path); return $base . $this->hash(dirname($path) . filemtime($path)) . DIRECTORY_SEPARATOR . basename($path);
} else { } else {
return $base . $this->hash($hashByName ? basename($path) : $path . filemtime($path)); return $base . $this->hash($path . filemtime($path));
} }
} else { } else {
return false; return false;
...@@ -257,22 +246,18 @@ class AssetManager extends Component ...@@ -257,22 +246,18 @@ class AssetManager extends Component
* This method does not perform any publishing. It merely tells you * 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. * if the file path is published, what the URL will be to access it.
* @param string $path directory or file path being published * @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. * @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) public function getPublishedUrl($path)
{ {
if (isset($this->_published[$path])) { if (isset($this->_published[$path])) {
return $this->_published[$path]; return $this->_published[$path];
} }
if (($path = realpath($path)) !== false) { if (($path = realpath($path)) !== false) {
if (is_file($path)) { if (is_file($path)) {
return $this->baseUrl . '/' . $this->hash($hashByName ? basename($path) : dirname($path) . filemtime($path)) . '/' . basename($path); return $this->baseUrl . '/' . $this->hash(dirname($path) . filemtime($path)) . '/' . basename($path);
} else { } else {
return $this->baseUrl . '/' . $this->hash($hashByName ? basename($path) : $path . filemtime($path)); return $this->baseUrl . '/' . $this->hash($path . filemtime($path));
} }
} else { } else {
return false; 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