Qiang Xue
13 years ago
15 changed files with 1687 additions and 703 deletions
@ -1,138 +0,0 @@ |
|||||||
<?php |
|
||||||
/** |
|
||||||
* CConsoleCommandRunner class file. |
|
||||||
* |
|
||||||
* @author Qiang Xue <qiang.xue@gmail.com> |
|
||||||
* @link http://www.yiiframework.com/ |
|
||||||
* @copyright Copyright © 2008-2011 Yii Software LLC |
|
||||||
* @license http://www.yiiframework.com/license/ |
|
||||||
*/ |
|
||||||
|
|
||||||
namespace yii\console; |
|
||||||
|
|
||||||
/** |
|
||||||
* CConsoleCommandRunner manages commands and executes the requested command. |
|
||||||
* |
|
||||||
* @property string $scriptName The entry script name. |
|
||||||
* |
|
||||||
* @author Qiang Xue <qiang.xue@gmail.com> |
|
||||||
* @since 2.0 |
|
||||||
*/ |
|
||||||
class CommandRunner extends \yii\base\Component |
|
||||||
{ |
|
||||||
/** |
|
||||||
* @var array list of all available commands (command name=>command configuration). |
|
||||||
* Each command configuration can be either a string or an array. |
|
||||||
* If the former, the string should be the class name or |
|
||||||
* {@link YiiBase::getPathOfAlias class path alias} of the command. |
|
||||||
* If the latter, the array must contain a 'class' element which specifies |
|
||||||
* the command's class name or {@link YiiBase::getPathOfAlias class path alias}. |
|
||||||
* The rest name-value pairs in the array are used to initialize |
|
||||||
* the corresponding command properties. For example, |
|
||||||
* <pre> |
|
||||||
* array( |
|
||||||
* 'email'=>array( |
|
||||||
* 'class'=>'path.to.Mailer', |
|
||||||
* 'interval'=>3600, |
|
||||||
* ), |
|
||||||
* 'log'=>'path.to.LoggerCommand', |
|
||||||
* ) |
|
||||||
* </pre> |
|
||||||
*/ |
|
||||||
public $commands=array(); |
|
||||||
|
|
||||||
private $_scriptName; |
|
||||||
|
|
||||||
/** |
|
||||||
* Executes the requested command. |
|
||||||
* @param array $args list of user supplied parameters (including the entry script name and the command name). |
|
||||||
*/ |
|
||||||
public function run($args) |
|
||||||
{ |
|
||||||
$this->_scriptName=$args[0]; |
|
||||||
array_shift($args); |
|
||||||
if(isset($args[0])) |
|
||||||
{ |
|
||||||
$name=$args[0]; |
|
||||||
array_shift($args); |
|
||||||
} else |
|
||||||
$name='help'; |
|
||||||
|
|
||||||
if(($command=$this->createCommand($name))===null) |
|
||||||
$command=$this->createCommand('help'); |
|
||||||
$command->init(); |
|
||||||
$command->run($args); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @return string the entry script name |
|
||||||
*/ |
|
||||||
public function getScriptName() |
|
||||||
{ |
|
||||||
return $this->_scriptName; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Searches for commands under the specified directory. |
|
||||||
* @param string $path the directory containing the command class files. |
|
||||||
* @return array list of commands (command name=>command class file) |
|
||||||
*/ |
|
||||||
public function findCommands($path) |
|
||||||
{ |
|
||||||
if(($dir=@opendir($path))===false) |
|
||||||
return array(); |
|
||||||
$commands=array(); |
|
||||||
while(($name=readdir($dir))!==false) |
|
||||||
{ |
|
||||||
$file=$path.DIRECTORY_SEPARATOR.$name; |
|
||||||
if(!strcasecmp(substr($name,-11),'Command.php') && is_file($file)) |
|
||||||
$commands[strtolower(substr($name,0,-11))]=$file; |
|
||||||
} |
|
||||||
closedir($dir); |
|
||||||
return $commands; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Adds commands from the specified command path. |
|
||||||
* If a command already exists, the new one will be ignored. |
|
||||||
* @param string $path the alias of the directory containing the command class files. |
|
||||||
*/ |
|
||||||
public function addCommands($path) |
|
||||||
{ |
|
||||||
if(($commands=$this->findCommands($path))!==array()) |
|
||||||
{ |
|
||||||
foreach($commands as $name=>$file) |
|
||||||
{ |
|
||||||
if(!isset($this->commands[$name])) |
|
||||||
$this->commands[$name]=$file; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @param string $name command name (case-insensitive) |
|
||||||
* @return \yii\console\Command the command object. Null if the name is invalid. |
|
||||||
*/ |
|
||||||
public function createCommand($name) |
|
||||||
{ |
|
||||||
$name=strtolower($name); |
|
||||||
if(isset($this->commands[$name])) |
|
||||||
{ |
|
||||||
if(is_string($this->commands[$name])) // class file path or alias |
|
||||||
{ |
|
||||||
if(strpos($this->commands[$name],'/')!==false || strpos($this->commands[$name],'\\')!==false) |
|
||||||
{ |
|
||||||
$className=substr(basename($this->commands[$name]),0,-4); |
|
||||||
if(!class_exists($className,false)) |
|
||||||
require_once($this->commands[$name]); |
|
||||||
} else // an alias |
|
||||||
$className=\Yii::import($this->commands[$name]); |
|
||||||
return new $className($name,$this); |
|
||||||
} else // an array configuration |
|
||||||
return \Yii::create($this->commands[$name],$name,$this); |
|
||||||
} else if($name==='help') |
|
||||||
return new HelpCommand('help',$this); |
|
||||||
else |
|
||||||
return null; |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,129 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* WebAppCommand class file. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
* @version $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* WebAppCommand creates an Yii Web application at the specified location. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @version $Id$ |
||||||
|
* @package system.cli.commands |
||||||
|
* @since 1.0 |
||||||
|
*/ |
||||||
|
class WebAppCommand extends CConsoleCommand |
||||||
|
{ |
||||||
|
private $_rootPath; |
||||||
|
|
||||||
|
public function getHelp() |
||||||
|
{ |
||||||
|
return <<<EOD |
||||||
|
USAGE |
||||||
|
yiic webapp <app-path> |
||||||
|
|
||||||
|
DESCRIPTION |
||||||
|
This command generates an Yii Web Application at the specified location. |
||||||
|
|
||||||
|
PARAMETERS |
||||||
|
* app-path: required, the directory where the new application will be created. |
||||||
|
If the directory does not exist, it will be created. After the application |
||||||
|
is created, please make sure the directory can be accessed by Web users. |
||||||
|
|
||||||
|
EOD; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Execute the action. |
||||||
|
* @param array command line parameters specific for this command |
||||||
|
*/ |
||||||
|
public function run($args) |
||||||
|
{ |
||||||
|
if(!isset($args[0])) |
||||||
|
$this->usageError('the Web application location is not specified.'); |
||||||
|
$path=strtr($args[0],'/\\',DIRECTORY_SEPARATOR); |
||||||
|
if(strpos($path,DIRECTORY_SEPARATOR)===false) |
||||||
|
$path='.'.DIRECTORY_SEPARATOR.$path; |
||||||
|
$dir=rtrim(realpath(dirname($path)),'\\/'); |
||||||
|
if($dir===false || !is_dir($dir)) |
||||||
|
$this->usageError("The directory '$path' is not valid. Please make sure the parent directory exists."); |
||||||
|
if(basename($path)==='.') |
||||||
|
$this->_rootPath=$path=$dir; |
||||||
|
else |
||||||
|
$this->_rootPath=$path=$dir.DIRECTORY_SEPARATOR.basename($path); |
||||||
|
if($this->confirm("Create a Web application under '$path'?")) |
||||||
|
{ |
||||||
|
$sourceDir=realpath(dirname(__FILE__).'/../views/webapp'); |
||||||
|
if($sourceDir===false) |
||||||
|
die("\nUnable to locate the source directory.\n"); |
||||||
|
$list=$this->buildFileList($sourceDir,$path); |
||||||
|
$list['index.php']['callback']=array($this,'generateIndex'); |
||||||
|
$list['index-test.php']['callback']=array($this,'generateIndex'); |
||||||
|
$list['protected/tests/bootstrap.php']['callback']=array($this,'generateTestBoostrap'); |
||||||
|
$list['protected/yiic.php']['callback']=array($this,'generateYiic'); |
||||||
|
$this->copyFiles($list); |
||||||
|
@chmod($path.'/assets',0777); |
||||||
|
@chmod($path.'/protected/runtime',0777); |
||||||
|
@chmod($path.'/protected/data',0777); |
||||||
|
@chmod($path.'/protected/data/testdrive.db',0777); |
||||||
|
@chmod($path.'/protected/yiic',0755); |
||||||
|
echo "\nYour application has been created successfully under {$path}.\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function generateIndex($source,$params) |
||||||
|
{ |
||||||
|
$content=file_get_contents($source); |
||||||
|
$yii=realpath(dirname(__FILE__).'/../../yii.php'); |
||||||
|
$yii=$this->getRelativePath($yii,$this->_rootPath.DIRECTORY_SEPARATOR.'index.php'); |
||||||
|
$yii=str_replace('\\','\\\\',$yii); |
||||||
|
return preg_replace('/\$yii\s*=(.*?);/',"\$yii=$yii;",$content); |
||||||
|
} |
||||||
|
|
||||||
|
public function generateTestBoostrap($source,$params) |
||||||
|
{ |
||||||
|
$content=file_get_contents($source); |
||||||
|
$yii=realpath(dirname(__FILE__).'/../../yiit.php'); |
||||||
|
$yii=$this->getRelativePath($yii,$this->_rootPath.DIRECTORY_SEPARATOR.'protected'.DIRECTORY_SEPARATOR.'tests'.DIRECTORY_SEPARATOR.'bootstrap.php'); |
||||||
|
$yii=str_replace('\\','\\\\',$yii); |
||||||
|
return preg_replace('/\$yiit\s*=(.*?);/',"\$yiit=$yii;",$content); |
||||||
|
} |
||||||
|
|
||||||
|
public function generateYiic($source,$params) |
||||||
|
{ |
||||||
|
$content=file_get_contents($source); |
||||||
|
$yiic=realpath(dirname(__FILE__).'/../../yiic.php'); |
||||||
|
$yiic=$this->getRelativePath($yiic,$this->_rootPath.DIRECTORY_SEPARATOR.'protected'.DIRECTORY_SEPARATOR.'yiic.php'); |
||||||
|
$yiic=str_replace('\\','\\\\',$yiic); |
||||||
|
return preg_replace('/\$yiic\s*=(.*?);/',"\$yiic=$yiic;",$content); |
||||||
|
} |
||||||
|
|
||||||
|
protected function getRelativePath($path1,$path2) |
||||||
|
{ |
||||||
|
$segs1=explode(DIRECTORY_SEPARATOR,$path1); |
||||||
|
$segs2=explode(DIRECTORY_SEPARATOR,$path2); |
||||||
|
$n1=count($segs1); |
||||||
|
$n2=count($segs2); |
||||||
|
|
||||||
|
for($i=0;$i<$n1 && $i<$n2;++$i) |
||||||
|
{ |
||||||
|
if($segs1[$i]!==$segs2[$i]) |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if($i===0) |
||||||
|
return "'".$path1."'"; |
||||||
|
$up=''; |
||||||
|
for($j=$i;$j<$n2-1;++$j) |
||||||
|
$up.='/..'; |
||||||
|
for(;$i<$n1-1;++$i) |
||||||
|
$up.='/'.$segs1[$i]; |
||||||
|
|
||||||
|
return 'dirname(__FILE__).\''.$up.'/'.basename($path1).'\''; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,223 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* MessageCommand class file. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* MessageCommand extracts messages to be translated from source files. |
||||||
|
* The extracted messages are saved as PHP message source files |
||||||
|
* under the specified directory. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @version $Id$ |
||||||
|
* @package system.cli.commands |
||||||
|
* @since 1.0 |
||||||
|
*/ |
||||||
|
class MessageCommand extends CConsoleCommand |
||||||
|
{ |
||||||
|
public function getHelp() |
||||||
|
{ |
||||||
|
return <<<EOD |
||||||
|
USAGE |
||||||
|
yiic message <config-file> |
||||||
|
|
||||||
|
DESCRIPTION |
||||||
|
This command searches for messages to be translated in the specified |
||||||
|
source files and compiles them into PHP arrays as message source. |
||||||
|
|
||||||
|
PARAMETERS |
||||||
|
* config-file: required, the path of the configuration file. You can find |
||||||
|
an example in framework/messages/config.php. |
||||||
|
|
||||||
|
The file can be placed anywhere and must be a valid PHP script which |
||||||
|
returns an array of name-value pairs. Each name-value pair represents |
||||||
|
a configuration option. |
||||||
|
|
||||||
|
The following options are available: |
||||||
|
|
||||||
|
- sourcePath: string, root directory of all source files. |
||||||
|
- messagePath: string, root directory containing message translations. |
||||||
|
- languages: array, list of language codes that the extracted messages |
||||||
|
should be translated to. For example, array('zh_cn','en_au'). |
||||||
|
- fileTypes: array, a list of file extensions (e.g. 'php', 'xml'). |
||||||
|
Only the files whose extension name can be found in this list |
||||||
|
will be processed. If empty, all files will be processed. |
||||||
|
- exclude: array, a list of directory and file exclusions. Each |
||||||
|
exclusion can be either a name or a path. If a file or directory name |
||||||
|
or path matches the exclusion, it will not be copied. For example, |
||||||
|
an exclusion of '.svn' will exclude all files and directories whose |
||||||
|
name is '.svn'. And an exclusion of '/a/b' will exclude file or |
||||||
|
directory 'sourcePath/a/b'. |
||||||
|
- translator: the name of the function for translating messages. |
||||||
|
Defaults to 'Yii::t'. This is used as a mark to find messages to be |
||||||
|
translated. |
||||||
|
- overwrite: if message file must be overwritten with the merged messages. |
||||||
|
- removeOld: if message no longer needs translation it will be removed, |
||||||
|
instead of being enclosed between a pair of '@@' marks. |
||||||
|
- sort: sort messages by key when merging, regardless of their translation |
||||||
|
state (new, obsolete, translated.) |
||||||
|
|
||||||
|
EOD; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Execute the action. |
||||||
|
* @param array command line parameters specific for this command |
||||||
|
*/ |
||||||
|
public function run($args) |
||||||
|
{ |
||||||
|
if(!isset($args[0])) |
||||||
|
$this->usageError('the configuration file is not specified.'); |
||||||
|
if(!is_file($args[0])) |
||||||
|
$this->usageError("the configuration file {$args[0]} does not exist."); |
||||||
|
|
||||||
|
$config=require_once($args[0]); |
||||||
|
$translator='Yii::t'; |
||||||
|
extract($config); |
||||||
|
|
||||||
|
if(!isset($sourcePath,$messagePath,$languages)) |
||||||
|
$this->usageError('The configuration file must specify "sourcePath", "messagePath" and "languages".'); |
||||||
|
if(!is_dir($sourcePath)) |
||||||
|
$this->usageError("The source path $sourcePath is not a valid directory."); |
||||||
|
if(!is_dir($messagePath)) |
||||||
|
$this->usageError("The message path $messagePath is not a valid directory."); |
||||||
|
if(empty($languages)) |
||||||
|
$this->usageError("Languages cannot be empty."); |
||||||
|
|
||||||
|
if(!isset($overwrite)) |
||||||
|
$overwrite = false; |
||||||
|
|
||||||
|
if(!isset($removeOld)) |
||||||
|
$removeOld = false; |
||||||
|
|
||||||
|
if(!isset($sort)) |
||||||
|
$sort = false; |
||||||
|
|
||||||
|
$options=array(); |
||||||
|
if(isset($fileTypes)) |
||||||
|
$options['fileTypes']=$fileTypes; |
||||||
|
if(isset($exclude)) |
||||||
|
$options['exclude']=$exclude; |
||||||
|
$files=CFileHelper::findFiles(realpath($sourcePath),$options); |
||||||
|
|
||||||
|
$messages=array(); |
||||||
|
foreach($files as $file) |
||||||
|
$messages=array_merge_recursive($messages,$this->extractMessages($file,$translator)); |
||||||
|
|
||||||
|
foreach($languages as $language) |
||||||
|
{ |
||||||
|
$dir=$messagePath.DIRECTORY_SEPARATOR.$language; |
||||||
|
if(!is_dir($dir)) |
||||||
|
@mkdir($dir); |
||||||
|
foreach($messages as $category=>$msgs) |
||||||
|
{ |
||||||
|
$msgs=array_values(array_unique($msgs)); |
||||||
|
$this->generateMessageFile($msgs,$dir.DIRECTORY_SEPARATOR.$category.'.php',$overwrite,$removeOld,$sort); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected function extractMessages($fileName,$translator) |
||||||
|
{ |
||||||
|
echo "Extracting messages from $fileName...\n"; |
||||||
|
$subject=file_get_contents($fileName); |
||||||
|
$n=preg_match_all('/\b'.$translator.'\s*\(\s*(\'.*?(?<!\\\\)\'|".*?(?<!\\\\)")\s*,\s*(\'.*?(?<!\\\\)\'|".*?(?<!\\\\)")\s*[,\)]/s',$subject,$matches,PREG_SET_ORDER); |
||||||
|
$messages=array(); |
||||||
|
for($i=0;$i<$n;++$i) |
||||||
|
{ |
||||||
|
if(($pos=strpos($matches[$i][1],'.'))!==false) |
||||||
|
$category=substr($matches[$i][1],$pos+1,-1); |
||||||
|
else |
||||||
|
$category=substr($matches[$i][1],1,-1); |
||||||
|
$message=$matches[$i][2]; |
||||||
|
$messages[$category][]=eval("return $message;"); // use eval to eliminate quote escape |
||||||
|
} |
||||||
|
return $messages; |
||||||
|
} |
||||||
|
|
||||||
|
protected function generateMessageFile($messages,$fileName,$overwrite,$removeOld,$sort) |
||||||
|
{ |
||||||
|
echo "Saving messages to $fileName..."; |
||||||
|
if(is_file($fileName)) |
||||||
|
{ |
||||||
|
$translated=require($fileName); |
||||||
|
sort($messages); |
||||||
|
ksort($translated); |
||||||
|
if(array_keys($translated)==$messages) |
||||||
|
{ |
||||||
|
echo "nothing new...skipped.\n"; |
||||||
|
return; |
||||||
|
} |
||||||
|
$merged=array(); |
||||||
|
$untranslated=array(); |
||||||
|
foreach($messages as $message) |
||||||
|
{ |
||||||
|
if(!empty($translated[$message])) |
||||||
|
$merged[$message]=$translated[$message]; |
||||||
|
else |
||||||
|
$untranslated[]=$message; |
||||||
|
} |
||||||
|
ksort($merged); |
||||||
|
sort($untranslated); |
||||||
|
$todo=array(); |
||||||
|
foreach($untranslated as $message) |
||||||
|
$todo[$message]=''; |
||||||
|
ksort($translated); |
||||||
|
foreach($translated as $message=>$translation) |
||||||
|
{ |
||||||
|
if(!isset($merged[$message]) && !isset($todo[$message]) && !$removeOld) |
||||||
|
{ |
||||||
|
if(substr($translation,0,2)==='@@' && substr($translation,-2)==='@@') |
||||||
|
$todo[$message]=$translation; |
||||||
|
else |
||||||
|
$todo[$message]='@@'.$translation.'@@'; |
||||||
|
} |
||||||
|
} |
||||||
|
$merged=array_merge($todo,$merged); |
||||||
|
if($sort) |
||||||
|
ksort($merged); |
||||||
|
if($overwrite === false) |
||||||
|
$fileName.='.merged'; |
||||||
|
echo "translation merged.\n"; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
$merged=array(); |
||||||
|
foreach($messages as $message) |
||||||
|
$merged[$message]=''; |
||||||
|
ksort($merged); |
||||||
|
echo "saved.\n"; |
||||||
|
} |
||||||
|
$array=str_replace("\r",'',var_export($merged,true)); |
||||||
|
$content=<<<EOD |
||||||
|
<?php |
||||||
|
/** |
||||||
|
* Message translations. |
||||||
|
* |
||||||
|
* This file is automatically generated by 'yiic message' command. |
||||||
|
* It contains the localizable messages extracted from source code. |
||||||
|
* You may modify this file by translating the extracted messages. |
||||||
|
* |
||||||
|
* Each array element represents the translation (value) of a message (key). |
||||||
|
* If the value is empty, the message is considered as not translated. |
||||||
|
* Messages that no longer need translation will have their translations |
||||||
|
* enclosed between a pair of '@@' marks. |
||||||
|
* |
||||||
|
* Message string can be used with plural forms format. Check i18n section |
||||||
|
* of the guide for details. |
||||||
|
* |
||||||
|
* NOTE, this file must be saved in UTF-8 encoding. |
||||||
|
* |
||||||
|
* @version \$Id: \$ |
||||||
|
*/ |
||||||
|
return $array; |
||||||
|
|
||||||
|
EOD; |
||||||
|
file_put_contents($fileName, $content); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,561 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* MigrateCommand class file. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* MigrateCommand manages the database migrations. |
||||||
|
* |
||||||
|
* The implementation of this command and other supporting classes referenced |
||||||
|
* the yii-dbmigrations extension ((https://github.com/pieterclaerhout/yii-dbmigrations), |
||||||
|
* authored by Pieter Claerhout. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @version $Id$ |
||||||
|
* @package system.cli.commands |
||||||
|
* @since 1.1.6 |
||||||
|
*/ |
||||||
|
class MigrateCommand extends CConsoleCommand |
||||||
|
{ |
||||||
|
const BASE_MIGRATION='m000000_000000_base'; |
||||||
|
|
||||||
|
/** |
||||||
|
* @var string the directory that stores the migrations. This must be specified |
||||||
|
* in terms of a path alias, and the corresponding directory must exist. |
||||||
|
* Defaults to 'application.migrations' (meaning 'protected/migrations'). |
||||||
|
*/ |
||||||
|
public $migrationPath='application.migrations'; |
||||||
|
/** |
||||||
|
* @var string the name of the table for keeping applied migration information. |
||||||
|
* This table will be automatically created if not exists. Defaults to 'tbl_migration'. |
||||||
|
* The table structure is: (version varchar(255) primary key, apply_time integer) |
||||||
|
*/ |
||||||
|
public $migrationTable='tbl_migration'; |
||||||
|
/** |
||||||
|
* @var string the application component ID that specifies the database connection for |
||||||
|
* storing migration information. Defaults to 'db'. |
||||||
|
*/ |
||||||
|
public $connectionID='db'; |
||||||
|
/** |
||||||
|
* @var string the path of the template file for generating new migrations. This |
||||||
|
* must be specified in terms of a path alias (e.g. application.migrations.template). |
||||||
|
* If not set, an internal template will be used. |
||||||
|
*/ |
||||||
|
public $templateFile; |
||||||
|
/** |
||||||
|
* @var string the default command action. It defaults to 'up'. |
||||||
|
*/ |
||||||
|
public $defaultAction='up'; |
||||||
|
/** |
||||||
|
* @var boolean whether to execute the migration in an interactive mode. Defaults to true. |
||||||
|
* Set this to false when performing migration in a cron job or background process. |
||||||
|
*/ |
||||||
|
public $interactive=true; |
||||||
|
|
||||||
|
public function beforeAction($action,$params) |
||||||
|
{ |
||||||
|
$path=Yii::getPathOfAlias($this->migrationPath); |
||||||
|
if($path===false || !is_dir($path)) |
||||||
|
die('Error: The migration directory does not exist: '.$this->migrationPath."\n"); |
||||||
|
$this->migrationPath=$path; |
||||||
|
|
||||||
|
$yiiVersion=Yii::getVersion(); |
||||||
|
echo "\nYii Migration Tool v1.0 (based on Yii v{$yiiVersion})\n\n"; |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public function actionUp($args) |
||||||
|
{ |
||||||
|
if(($migrations=$this->getNewMigrations())===array()) |
||||||
|
{ |
||||||
|
echo "No new migration found. Your system is up-to-date.\n"; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
$total=count($migrations); |
||||||
|
$step=isset($args[0]) ? (int)$args[0] : 0; |
||||||
|
if($step>0) |
||||||
|
$migrations=array_slice($migrations,0,$step); |
||||||
|
|
||||||
|
$n=count($migrations); |
||||||
|
if($n===$total) |
||||||
|
echo "Total $n new ".($n===1 ? 'migration':'migrations')." to be applied:\n"; |
||||||
|
else |
||||||
|
echo "Total $n out of $total new ".($total===1 ? 'migration':'migrations')." to be applied:\n"; |
||||||
|
|
||||||
|
foreach($migrations as $migration) |
||||||
|
echo " $migration\n"; |
||||||
|
echo "\n"; |
||||||
|
|
||||||
|
if($this->confirm('Apply the above '.($n===1 ? 'migration':'migrations')."?")) |
||||||
|
{ |
||||||
|
foreach($migrations as $migration) |
||||||
|
{ |
||||||
|
if($this->migrateUp($migration)===false) |
||||||
|
{ |
||||||
|
echo "\nMigration failed. All later migrations are canceled.\n"; |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
echo "\nMigrated up successfully.\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function actionDown($args) |
||||||
|
{ |
||||||
|
$step=isset($args[0]) ? (int)$args[0] : 1; |
||||||
|
if($step<1) |
||||||
|
die("Error: The step parameter must be greater than 0.\n"); |
||||||
|
|
||||||
|
if(($migrations=$this->getMigrationHistory($step))===array()) |
||||||
|
{ |
||||||
|
echo "No migration has been done before.\n"; |
||||||
|
return; |
||||||
|
} |
||||||
|
$migrations=array_keys($migrations); |
||||||
|
|
||||||
|
$n=count($migrations); |
||||||
|
echo "Total $n ".($n===1 ? 'migration':'migrations')." to be reverted:\n"; |
||||||
|
foreach($migrations as $migration) |
||||||
|
echo " $migration\n"; |
||||||
|
echo "\n"; |
||||||
|
|
||||||
|
if($this->confirm('Revert the above '.($n===1 ? 'migration':'migrations')."?")) |
||||||
|
{ |
||||||
|
foreach($migrations as $migration) |
||||||
|
{ |
||||||
|
if($this->migrateDown($migration)===false) |
||||||
|
{ |
||||||
|
echo "\nMigration failed. All later migrations are canceled.\n"; |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
echo "\nMigrated down successfully.\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function actionRedo($args) |
||||||
|
{ |
||||||
|
$step=isset($args[0]) ? (int)$args[0] : 1; |
||||||
|
if($step<1) |
||||||
|
die("Error: The step parameter must be greater than 0.\n"); |
||||||
|
|
||||||
|
if(($migrations=$this->getMigrationHistory($step))===array()) |
||||||
|
{ |
||||||
|
echo "No migration has been done before.\n"; |
||||||
|
return; |
||||||
|
} |
||||||
|
$migrations=array_keys($migrations); |
||||||
|
|
||||||
|
$n=count($migrations); |
||||||
|
echo "Total $n ".($n===1 ? 'migration':'migrations')." to be redone:\n"; |
||||||
|
foreach($migrations as $migration) |
||||||
|
echo " $migration\n"; |
||||||
|
echo "\n"; |
||||||
|
|
||||||
|
if($this->confirm('Redo the above '.($n===1 ? 'migration':'migrations')."?")) |
||||||
|
{ |
||||||
|
foreach($migrations as $migration) |
||||||
|
{ |
||||||
|
if($this->migrateDown($migration)===false) |
||||||
|
{ |
||||||
|
echo "\nMigration failed. All later migrations are canceled.\n"; |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
foreach(array_reverse($migrations) as $migration) |
||||||
|
{ |
||||||
|
if($this->migrateUp($migration)===false) |
||||||
|
{ |
||||||
|
echo "\nMigration failed. All later migrations are canceled.\n"; |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
echo "\nMigration redone successfully.\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function actionTo($args) |
||||||
|
{ |
||||||
|
if(isset($args[0])) |
||||||
|
$version=$args[0]; |
||||||
|
else |
||||||
|
$this->usageError('Please specify which version to migrate to.'); |
||||||
|
|
||||||
|
$originalVersion=$version; |
||||||
|
if(preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/',$version,$matches)) |
||||||
|
$version='m'.$matches[1]; |
||||||
|
else |
||||||
|
die("Error: The version option must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table).\n"); |
||||||
|
|
||||||
|
// try migrate up |
||||||
|
$migrations=$this->getNewMigrations(); |
||||||
|
foreach($migrations as $i=>$migration) |
||||||
|
{ |
||||||
|
if(strpos($migration,$version.'_')===0) |
||||||
|
{ |
||||||
|
$this->actionUp(array($i+1)); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// try migrate down |
||||||
|
$migrations=array_keys($this->getMigrationHistory(-1)); |
||||||
|
foreach($migrations as $i=>$migration) |
||||||
|
{ |
||||||
|
if(strpos($migration,$version.'_')===0) |
||||||
|
{ |
||||||
|
if($i===0) |
||||||
|
echo "Already at '$originalVersion'. Nothing needs to be done.\n"; |
||||||
|
else |
||||||
|
$this->actionDown(array($i)); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
die("Error: Unable to find the version '$originalVersion'.\n"); |
||||||
|
} |
||||||
|
|
||||||
|
public function actionMark($args) |
||||||
|
{ |
||||||
|
if(isset($args[0])) |
||||||
|
$version=$args[0]; |
||||||
|
else |
||||||
|
$this->usageError('Please specify which version to mark to.'); |
||||||
|
$originalVersion=$version; |
||||||
|
if(preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/',$version,$matches)) |
||||||
|
$version='m'.$matches[1]; |
||||||
|
else |
||||||
|
die("Error: The version option must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table).\n"); |
||||||
|
|
||||||
|
$db=$this->getDbConnection(); |
||||||
|
|
||||||
|
// try mark up |
||||||
|
$migrations=$this->getNewMigrations(); |
||||||
|
foreach($migrations as $i=>$migration) |
||||||
|
{ |
||||||
|
if(strpos($migration,$version.'_')===0) |
||||||
|
{ |
||||||
|
if($this->confirm("Set migration history at $originalVersion?")) |
||||||
|
{ |
||||||
|
$command=$db->createCommand(); |
||||||
|
for($j=0;$j<=$i;++$j) |
||||||
|
{ |
||||||
|
$command->insert($this->migrationTable, array( |
||||||
|
'version'=>$migrations[$j], |
||||||
|
'apply_time'=>time(), |
||||||
|
)); |
||||||
|
} |
||||||
|
echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n"; |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// try mark down |
||||||
|
$migrations=array_keys($this->getMigrationHistory(-1)); |
||||||
|
foreach($migrations as $i=>$migration) |
||||||
|
{ |
||||||
|
if(strpos($migration,$version.'_')===0) |
||||||
|
{ |
||||||
|
if($i===0) |
||||||
|
echo "Already at '$originalVersion'. Nothing needs to be done.\n"; |
||||||
|
else |
||||||
|
{ |
||||||
|
if($this->confirm("Set migration history at $originalVersion?")) |
||||||
|
{ |
||||||
|
$command=$db->createCommand(); |
||||||
|
for($j=0;$j<$i;++$j) |
||||||
|
$command->delete($this->migrationTable, $db->quoteColumnName('version').'=:version', array(':version'=>$migrations[$j])); |
||||||
|
echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
die("Error: Unable to find the version '$originalVersion'.\n"); |
||||||
|
} |
||||||
|
|
||||||
|
public function actionHistory($args) |
||||||
|
{ |
||||||
|
$limit=isset($args[0]) ? (int)$args[0] : -1; |
||||||
|
$migrations=$this->getMigrationHistory($limit); |
||||||
|
if($migrations===array()) |
||||||
|
echo "No migration has been done before.\n"; |
||||||
|
else |
||||||
|
{ |
||||||
|
$n=count($migrations); |
||||||
|
if($limit>0) |
||||||
|
echo "Showing the last $n applied ".($n===1 ? 'migration' : 'migrations').":\n"; |
||||||
|
else |
||||||
|
echo "Total $n ".($n===1 ? 'migration has' : 'migrations have')." been applied before:\n"; |
||||||
|
foreach($migrations as $version=>$time) |
||||||
|
echo " (".date('Y-m-d H:i:s',$time).') '.$version."\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function actionNew($args) |
||||||
|
{ |
||||||
|
$limit=isset($args[0]) ? (int)$args[0] : -1; |
||||||
|
$migrations=$this->getNewMigrations(); |
||||||
|
if($migrations===array()) |
||||||
|
echo "No new migrations found. Your system is up-to-date.\n"; |
||||||
|
else |
||||||
|
{ |
||||||
|
$n=count($migrations); |
||||||
|
if($limit>0 && $n>$limit) |
||||||
|
{ |
||||||
|
$migrations=array_slice($migrations,0,$limit); |
||||||
|
echo "Showing $limit out of $n new ".($n===1 ? 'migration' : 'migrations').":\n"; |
||||||
|
} |
||||||
|
else |
||||||
|
echo "Found $n new ".($n===1 ? 'migration' : 'migrations').":\n"; |
||||||
|
|
||||||
|
foreach($migrations as $migration) |
||||||
|
echo " ".$migration."\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function actionCreate($args) |
||||||
|
{ |
||||||
|
if(isset($args[0])) |
||||||
|
$name=$args[0]; |
||||||
|
else |
||||||
|
$this->usageError('Please provide the name of the new migration.'); |
||||||
|
|
||||||
|
if(!preg_match('/^\w+$/',$name)) |
||||||
|
die("Error: The name of the migration must contain letters, digits and/or underscore characters only.\n"); |
||||||
|
|
||||||
|
$name='m'.gmdate('ymd_His').'_'.$name; |
||||||
|
$content=strtr($this->getTemplate(), array('{ClassName}'=>$name)); |
||||||
|
$file=$this->migrationPath.DIRECTORY_SEPARATOR.$name.'.php'; |
||||||
|
|
||||||
|
if($this->confirm("Create new migration '$file'?")) |
||||||
|
{ |
||||||
|
file_put_contents($file, $content); |
||||||
|
echo "New migration created successfully.\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function confirm($message) |
||||||
|
{ |
||||||
|
if(!$this->interactive) |
||||||
|
return true; |
||||||
|
return parent::confirm($message); |
||||||
|
} |
||||||
|
|
||||||
|
protected function migrateUp($class) |
||||||
|
{ |
||||||
|
if($class===self::BASE_MIGRATION) |
||||||
|
return; |
||||||
|
|
||||||
|
echo "*** applying $class\n"; |
||||||
|
$start=microtime(true); |
||||||
|
$migration=$this->instantiateMigration($class); |
||||||
|
if($migration->up()!==false) |
||||||
|
{ |
||||||
|
$this->getDbConnection()->createCommand()->insert($this->migrationTable, array( |
||||||
|
'version'=>$class, |
||||||
|
'apply_time'=>time(), |
||||||
|
)); |
||||||
|
$time=microtime(true)-$start; |
||||||
|
echo "*** applied $class (time: ".sprintf("%.3f",$time)."s)\n\n"; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
$time=microtime(true)-$start; |
||||||
|
echo "*** failed to apply $class (time: ".sprintf("%.3f",$time)."s)\n\n"; |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected function migrateDown($class) |
||||||
|
{ |
||||||
|
if($class===self::BASE_MIGRATION) |
||||||
|
return; |
||||||
|
|
||||||
|
echo "*** reverting $class\n"; |
||||||
|
$start=microtime(true); |
||||||
|
$migration=$this->instantiateMigration($class); |
||||||
|
if($migration->down()!==false) |
||||||
|
{ |
||||||
|
$db=$this->getDbConnection(); |
||||||
|
$db->createCommand()->delete($this->migrationTable, $db->quoteColumnName('version').'=:version', array(':version'=>$class)); |
||||||
|
$time=microtime(true)-$start; |
||||||
|
echo "*** reverted $class (time: ".sprintf("%.3f",$time)."s)\n\n"; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
$time=microtime(true)-$start; |
||||||
|
echo "*** failed to revert $class (time: ".sprintf("%.3f",$time)."s)\n\n"; |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected function instantiateMigration($class) |
||||||
|
{ |
||||||
|
$file=$this->migrationPath.DIRECTORY_SEPARATOR.$class.'.php'; |
||||||
|
require_once($file); |
||||||
|
$migration=new $class; |
||||||
|
$migration->setDbConnection($this->getDbConnection()); |
||||||
|
return $migration; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @var CDbConnection |
||||||
|
*/ |
||||||
|
private $_db; |
||||||
|
protected function getDbConnection() |
||||||
|
{ |
||||||
|
if($this->_db!==null) |
||||||
|
return $this->_db; |
||||||
|
else if(($this->_db=Yii::app()->getComponent($this->connectionID)) instanceof CDbConnection) |
||||||
|
return $this->_db; |
||||||
|
else |
||||||
|
die("Error: CMigrationCommand.connectionID '{$this->connectionID}' is invalid. Please make sure it refers to the ID of a CDbConnection application component.\n"); |
||||||
|
} |
||||||
|
|
||||||
|
protected function getMigrationHistory($limit) |
||||||
|
{ |
||||||
|
$db=$this->getDbConnection(); |
||||||
|
if($db->schema->getTable($this->migrationTable)===null) |
||||||
|
{ |
||||||
|
$this->createMigrationHistoryTable(); |
||||||
|
} |
||||||
|
return CHtml::listData($db->createCommand() |
||||||
|
->select('version, apply_time') |
||||||
|
->from($this->migrationTable) |
||||||
|
->order('version DESC') |
||||||
|
->limit($limit) |
||||||
|
->queryAll(), 'version', 'apply_time'); |
||||||
|
} |
||||||
|
|
||||||
|
protected function createMigrationHistoryTable() |
||||||
|
{ |
||||||
|
$db=$this->getDbConnection(); |
||||||
|
echo 'Creating migration history table "'.$this->migrationTable.'"...'; |
||||||
|
$db->createCommand()->createTable($this->migrationTable,array( |
||||||
|
'version'=>'string NOT NULL PRIMARY KEY', |
||||||
|
'apply_time'=>'integer', |
||||||
|
)); |
||||||
|
$db->createCommand()->insert($this->migrationTable,array( |
||||||
|
'version'=>self::BASE_MIGRATION, |
||||||
|
'apply_time'=>time(), |
||||||
|
)); |
||||||
|
echo "done.\n"; |
||||||
|
} |
||||||
|
|
||||||
|
protected function getNewMigrations() |
||||||
|
{ |
||||||
|
$applied=array(); |
||||||
|
foreach($this->getMigrationHistory(-1) as $version=>$time) |
||||||
|
$applied[substr($version,1,13)]=true; |
||||||
|
|
||||||
|
$migrations=array(); |
||||||
|
$handle=opendir($this->migrationPath); |
||||||
|
while(($file=readdir($handle))!==false) |
||||||
|
{ |
||||||
|
if($file==='.' || $file==='..') |
||||||
|
continue; |
||||||
|
$path=$this->migrationPath.DIRECTORY_SEPARATOR.$file; |
||||||
|
if(preg_match('/^(m(\d{6}_\d{6})_.*?)\.php$/',$file,$matches) && is_file($path) && !isset($applied[$matches[2]])) |
||||||
|
$migrations[]=$matches[1]; |
||||||
|
} |
||||||
|
closedir($handle); |
||||||
|
sort($migrations); |
||||||
|
return $migrations; |
||||||
|
} |
||||||
|
|
||||||
|
public function getHelp() |
||||||
|
{ |
||||||
|
return <<<EOD |
||||||
|
USAGE |
||||||
|
yiic migrate [action] [parameter] |
||||||
|
|
||||||
|
DESCRIPTION |
||||||
|
This command provides support for database migrations. The optional |
||||||
|
'action' parameter specifies which specific migration task to perform. |
||||||
|
It can take these values: up, down, to, create, history, new, mark. |
||||||
|
If the 'action' parameter is not given, it defaults to 'up'. |
||||||
|
Each action takes different parameters. Their usage can be found in |
||||||
|
the following examples. |
||||||
|
|
||||||
|
EXAMPLES |
||||||
|
* yiic migrate |
||||||
|
Applies ALL new migrations. This is equivalent to 'yiic migrate up'. |
||||||
|
|
||||||
|
* yiic migrate create create_user_table |
||||||
|
Creates a new migration named 'create_user_table'. |
||||||
|
|
||||||
|
* yiic migrate up 3 |
||||||
|
Applies the next 3 new migrations. |
||||||
|
|
||||||
|
* yiic migrate down |
||||||
|
Reverts the last applied migration. |
||||||
|
|
||||||
|
* yiic migrate down 3 |
||||||
|
Reverts the last 3 applied migrations. |
||||||
|
|
||||||
|
* yiic migrate to 101129_185401 |
||||||
|
Migrates up or down to version 101129_185401. |
||||||
|
|
||||||
|
* yiic migrate mark 101129_185401 |
||||||
|
Modifies the migration history up or down to version 101129_185401. |
||||||
|
No actual migration will be performed. |
||||||
|
|
||||||
|
* yiic migrate history |
||||||
|
Shows all previously applied migration information. |
||||||
|
|
||||||
|
* yiic migrate history 10 |
||||||
|
Shows the last 10 applied migrations. |
||||||
|
|
||||||
|
* yiic migrate new |
||||||
|
Shows all new migrations. |
||||||
|
|
||||||
|
* yiic migrate new 10 |
||||||
|
Shows the next 10 migrations that have not been applied. |
||||||
|
|
||||||
|
EOD; |
||||||
|
} |
||||||
|
|
||||||
|
protected function getTemplate() |
||||||
|
{ |
||||||
|
if($this->templateFile!==null) |
||||||
|
return file_get_contents(Yii::getPathOfAlias($this->templateFile).'.php'); |
||||||
|
else |
||||||
|
return <<<EOD |
||||||
|
<?php |
||||||
|
|
||||||
|
class {ClassName} extends CDbMigration |
||||||
|
{ |
||||||
|
public function up() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public function down() |
||||||
|
{ |
||||||
|
echo "{ClassName} does not support migration down.\\n"; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
// Use safeUp/safeDown to do migration with transaction |
||||||
|
public function safeUp() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public function safeDown() |
||||||
|
{ |
||||||
|
} |
||||||
|
*/ |
||||||
|
} |
||||||
|
EOD; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,148 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* ShellCommand class file. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
* @version $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* ShellCommand executes the specified Web application and provides a shell for interaction. |
||||||
|
* |
||||||
|
* @property string $help The help information for the shell command. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @version $Id$ |
||||||
|
* @package system.cli.commands |
||||||
|
* @since 1.0 |
||||||
|
*/ |
||||||
|
class ShellCommand extends CConsoleCommand |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @return string the help information for the shell command |
||||||
|
*/ |
||||||
|
public function getHelp() |
||||||
|
{ |
||||||
|
return <<<EOD |
||||||
|
USAGE |
||||||
|
yiic shell [entry-script | config-file] |
||||||
|
|
||||||
|
DESCRIPTION |
||||||
|
This command allows you to interact with a Web application |
||||||
|
on the command line. It also provides tools to automatically |
||||||
|
generate new controllers, views and data models. |
||||||
|
|
||||||
|
It is recommended that you execute this command under |
||||||
|
the directory that contains the entry script file of |
||||||
|
the Web application. |
||||||
|
|
||||||
|
PARAMETERS |
||||||
|
* entry-script | config-file: optional, the path to |
||||||
|
the entry script file or the configuration file for |
||||||
|
the Web application. If not given, it is assumed to be |
||||||
|
the 'index.php' file under the current directory. |
||||||
|
|
||||||
|
EOD; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Execute the action. |
||||||
|
* @param array $args command line parameters specific for this command |
||||||
|
*/ |
||||||
|
public function run($args) |
||||||
|
{ |
||||||
|
if(!isset($args[0])) |
||||||
|
$args[0]='index.php'; |
||||||
|
$entryScript=isset($args[0]) ? $args[0] : 'index.php'; |
||||||
|
if(($entryScript=realpath($args[0]))===false || !is_file($entryScript)) |
||||||
|
$this->usageError("{$args[0]} does not exist or is not an entry script file."); |
||||||
|
|
||||||
|
// fake the web server setting |
||||||
|
$cwd=getcwd(); |
||||||
|
chdir(dirname($entryScript)); |
||||||
|
$_SERVER['SCRIPT_NAME']='/'.basename($entryScript); |
||||||
|
$_SERVER['REQUEST_URI']=$_SERVER['SCRIPT_NAME']; |
||||||
|
$_SERVER['SCRIPT_FILENAME']=$entryScript; |
||||||
|
$_SERVER['HTTP_HOST']='localhost'; |
||||||
|
$_SERVER['SERVER_NAME']='localhost'; |
||||||
|
$_SERVER['SERVER_PORT']=80; |
||||||
|
|
||||||
|
// reset context to run the web application |
||||||
|
restore_error_handler(); |
||||||
|
restore_exception_handler(); |
||||||
|
Yii::setApplication(null); |
||||||
|
Yii::setPathOfAlias('application',null); |
||||||
|
|
||||||
|
ob_start(); |
||||||
|
$config=require($entryScript); |
||||||
|
ob_end_clean(); |
||||||
|
|
||||||
|
// oops, the entry script turns out to be a config file |
||||||
|
if(is_array($config)) |
||||||
|
{ |
||||||
|
chdir($cwd); |
||||||
|
$_SERVER['SCRIPT_NAME']='/index.php'; |
||||||
|
$_SERVER['REQUEST_URI']=$_SERVER['SCRIPT_NAME']; |
||||||
|
$_SERVER['SCRIPT_FILENAME']=$cwd.DIRECTORY_SEPARATOR.'index.php'; |
||||||
|
Yii::createWebApplication($config); |
||||||
|
} |
||||||
|
|
||||||
|
restore_error_handler(); |
||||||
|
restore_exception_handler(); |
||||||
|
|
||||||
|
$yiiVersion=Yii::getVersion(); |
||||||
|
echo <<<EOD |
||||||
|
Yii Interactive Tool v1.1 (based on Yii v{$yiiVersion}) |
||||||
|
Please type 'help' for help. Type 'exit' to quit. |
||||||
|
EOD; |
||||||
|
$this->runShell(); |
||||||
|
} |
||||||
|
|
||||||
|
protected function runShell() |
||||||
|
{ |
||||||
|
// disable E_NOTICE so that the shell is more friendly |
||||||
|
error_reporting(E_ALL ^ E_NOTICE); |
||||||
|
|
||||||
|
$_runner_=new CConsoleCommandRunner; |
||||||
|
$_runner_->addCommands(dirname(__FILE__).'/shell'); |
||||||
|
$_runner_->addCommands(Yii::getPathOfAlias('application.commands.shell')); |
||||||
|
if(($_path_=@getenv('YIIC_SHELL_COMMAND_PATH'))!==false) |
||||||
|
$_runner_->addCommands($_path_); |
||||||
|
$_commands_=$_runner_->commands; |
||||||
|
$log=Yii::app()->log; |
||||||
|
|
||||||
|
while(($_line_=$this->prompt("\n>>"))!==false) |
||||||
|
{ |
||||||
|
$_line_=trim($_line_); |
||||||
|
if($_line_==='exit') |
||||||
|
return; |
||||||
|
try |
||||||
|
{ |
||||||
|
$_args_=preg_split('/[\s,]+/',rtrim($_line_,';'),-1,PREG_SPLIT_NO_EMPTY); |
||||||
|
if(isset($_args_[0]) && isset($_commands_[$_args_[0]])) |
||||||
|
{ |
||||||
|
$_command_=$_runner_->createCommand($_args_[0]); |
||||||
|
array_shift($_args_); |
||||||
|
$_command_->init(); |
||||||
|
$_command_->run($_args_); |
||||||
|
} |
||||||
|
else |
||||||
|
echo eval($_line_.';'); |
||||||
|
} |
||||||
|
catch(Exception $e) |
||||||
|
{ |
||||||
|
if($e instanceof ShellException) |
||||||
|
echo $e->getMessage(); |
||||||
|
else |
||||||
|
echo $e; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class ShellException extends CException |
||||||
|
{ |
||||||
|
} |
Loading…
Reference in new issue