WebAppCommand.php 6.53 KB
Newer Older
JULIO JARAMILLO's avatar
JULIO JARAMILLO committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
<?php
/**
 * WebAppCommand class file.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.yiiframework.com/
 * @copyright 2008-2013 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

/**
 * WebAppCommand creates an Yii Web application at the specified location.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @package system.cli.commands
 * @since 1.0
 */
class WebAppCommand extends CConsoleCommand
{
	private $_rootPath;

	public function getHelp()
	{
		return <<<EOD
USAGE
  yiic webapp <app-path> [<vcs>]

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.
 * vcs: optional, version control system you're going to use in the new project.
   Application generator will create all needed files to the specified VCS
   (such as .gitignore, .gitkeep, etc.). Possible values: git, hg. Do not
   use this argument if you're going to create VCS files yourself.

EOD;
	}

	/**
	 * Execute the action.
	 * @param array $args command line parameters specific for this command
	 */
	public function run($args)
	{
		$vcs=false;
		if(isset($args[1]))
		{
			if($args[1]!='git' && $args[1]!='hg')
				$this->usageError('Unsupported VCS specified. Currently only git and hg supported.');
			$vcs=$args[1];
		}
		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;
		if(basename($path)=='..')
			$path.=DIRECTORY_SEPARATOR.'.';
		$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=$this->getSourceDir();
			if($sourceDir===false)
				die("\nUnable to locate the source directory.\n");
			$ignoreFiles=array();
			$renameMap=array();
			switch($vcs)
			{
				case 'git':
					$renameMap=array('git-gitignore'=>'.gitignore','git-gitkeep'=>'.gitkeep'); // move with rename git files
					$ignoreFiles=array('hg-hgignore','hg-hgkeep'); // ignore only hg files
					break;
				case 'hg':
					$renameMap=array('hg-hgignore'=>'.hgignore','hg-hgkeep'=>'.hgkeep'); // move with rename hg files
					$ignoreFiles=array('git-gitignore','git-gitkeep'); // ignore only git files
					break;
				default:
					// no files for renaming
					$ignoreFiles=array('git-gitignore','git-gitkeep','hg-hgignore','hg-hgkeep'); // ignore both git and hg files
					break;
			}
			$list=$this->buildFileList($sourceDir,$path,'',$ignoreFiles,$renameMap);
			$this->addFileModificationCallbacks($list);
			$this->copyFiles($list);
			$this->setPermissions($path);
			echo "\nYour application has been created successfully under {$path}.\n";
		}
	}

	/**
	 * Adjusts created application file and directory permissions
	 *
	 * @param string $targetDir path to created application
	 */
	protected function setPermissions($targetDir)
	{
		@chmod($targetDir.'/assets',0777);
		@chmod($targetDir.'/protected/runtime',0777);
		@chmod($targetDir.'/protected/data',0777);
		@chmod($targetDir.'/protected/data/testdrive.db',0777);
		@chmod($targetDir.'/protected/yiic',0755);
	}

	/**
	 * @return string path to application bootstrap source files
	 */
	protected function getSourceDir()
	{
		return realpath(dirname(__FILE__).'/../views/webapp');
	}

	/**
	 * Adds callbacks that will modify source files
	 *
	 * @param array $fileList
	 */
	protected function addFileModificationCallbacks(&$fileList)
	{
		$fileList['index.php']['callback']=array($this,'generateIndex');
		$fileList['index-test.php']['callback']=array($this,'generateIndex');
		$fileList['protected/tests/bootstrap.php']['callback']=array($this,'generateTestBoostrap');
		$fileList['protected/yiic.php']['callback']=array($this,'generateYiic');
	}

	/**
	 * Inserts path to framework's yii.php into application's index.php
	 *
	 * @param string $source source file path
	 * @param array $params
	 * @return string modified source file content
	 */
	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);
	}

	/**
	 * Inserts path to framework's yiit.php into application's index-test.php
	 *
	 * @param string $source source file path
	 * @param array $params
	 * @return string modified source file 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);
	}

	/**
	 * Inserts path to framework's yiic.php into application's yiic.php
	 *
	 * @param string $source source file path
	 * @param array $params
	 * @return string modified source file 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);
	}

	/**
	 * Returns variant of $path1 relative to $path2
	 *
	 * @param string $path1
	 * @param string $path2
	 * @return string $path1 relative to $path2
	 */
	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).'\'';
	}
}