* @link http://www.yiiframework.com/ * @copyright Copyright © 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ Yii::import('application.commands.api.ApiModel'); /** * 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 * @version $Id$ * @package system.build * @since 1.0 */ class ApiCommand extends CConsoleCommand { const URL_PATTERN='/\{\{(.*?)\|(.*?)\}\}/s'; public $classes; public $packages; public $pageTitle; public $themePath; public $currentClass; public function getHelp() { return << [mode] DESCRIPTION This command generates offline API documentation for the Yii framework. PARAMETERS * output-path: required, the directory where the generated documentation would be saved. * mode: optional, either 'online' or 'offline' (default). This indicates whether the generated documentation are for online or offline use. EOD; } /** * Execute the action. * @param array command line parameters specific for this command */ public function run($args) { if(!isset($args[0])) $this->usageError('the output directory is not specified.'); if(!is_dir($docPath=$args[0])) $this->usageError("the output directory {$docPath} does not exist."); $offline=true; if(isset($args[1]) && $args[1]==='online') $offline=false; $options=array( 'fileTypes'=>array('php'), 'exclude'=>array( '.svn', 'yiilite.php', '/cli', '/i18n/data', '/messages', '/vendors', '/views', '/web/js', ), ); $this->pageTitle='Yii Framework Class Reference'; $themePath=dirname(__FILE__).'/api'; $model=$this->buildModel(YII_PATH,$options); $this->classes=$model->classes; $this->packages=$model->packages; if($offline) $this->buildOfflinePages($docPath.DIRECTORY_SEPARATOR.'api',$themePath); else { $this->buildOnlinePages($docPath.DIRECTORY_SEPARATOR.'api',$themePath); $this->buildKeywords($docPath); } } protected function buildKeywords($docPath) { $keywords=array(); foreach($this->classes as $class) $keywords[]=$class->name; foreach($this->classes as $class) { $name=$class->name; foreach($class->properties as $property) { if(!$property->isInherited) $keywords[]=$name.'.'.$property->name; } foreach($class->methods as $method) { if(!$method->isInherited) $keywords[]=$name.'.'.$method->name.'()'; } } file_put_contents($docPath.'/apiKeywords.txt',implode(',',$keywords)); } public function render($view,$data=null,$return=false,$layout='main') { $viewFile=$this->themePath."/views/{$view}.php"; $layoutFile=$this->themePath."/layouts/{$layout}.php"; $content=$this->renderFile($viewFile,$data,true); return $this->renderFile($layoutFile,array('content'=>$content),$return); } public function renderPartial($view,$data=null,$return=false) { $viewFile=$this->themePath."/views/{$view}.php"; return $this->renderFile($viewFile,$data,$return); } protected function buildOfflinePages($docPath,$themePath) { $this->themePath=$themePath; @mkdir($docPath); $content=$this->render('index',null,true); $content=preg_replace_callback(self::URL_PATTERN,array($this,'fixOfflineLink'),$content); file_put_contents($docPath.'/index.html',$content); foreach($this->classes as $name=>$class) { $this->currentClass=$name; $this->pageTitle=$name; $content=$this->render('class',array('class'=>$class),true); $content=preg_replace_callback(self::URL_PATTERN,array($this,'fixOfflineLink'),$content); file_put_contents($docPath.'/'.$name.'.html',$content); } CFileHelper::copyDirectory($this->themePath.'/assets',$docPath,array('exclude'=>array('.svn'))); $content=$this->renderPartial('chmProject',null,true); file_put_contents($docPath.'/manual.hhp',$content); $content=$this->renderPartial('chmIndex',null,true); file_put_contents($docPath.'/manual.hhk',$content); $content=$this->renderPartial('chmContents',null,true); file_put_contents($docPath.'/manual.hhc',$content); } protected function buildOnlinePages($docPath,$themePath) { $this->themePath=$themePath; @mkdir($docPath); $content=$this->renderPartial('index',null,true); $content=preg_replace_callback(self::URL_PATTERN,array($this,'fixOnlineLink'),$content); file_put_contents($docPath.'/index.php',$content); foreach($this->classes as $name=>$class) { $this->currentClass=$name; $this->pageTitle=$name; $content=$this->renderPartial('class',array('class'=>$class),true); $content=preg_replace_callback(self::URL_PATTERN,array($this,'fixOnlineLink'),$content); file_put_contents($docPath.'/'.$name.'.php',$content); } } protected function buildModel($sourcePath,$options) { $files=CFileHelper::findFiles($sourcePath,$options); $model=new ApiModel; $model->build($files); return $model; } public function renderInheritance($class) { $parents=array($class->signature); foreach($class->parentClasses as $parent) { if(isset($this->classes[$parent])) $parents[]='{{'.$parent.'|'.$parent.'}}'; else $parents[]=$parent; } return implode(" »\n",$parents); } public function renderImplements($class) { $interfaces=array(); foreach($class->interfaces as $interface) { if(isset($this->classes[$interface])) $interfaces[]='{{'.$interface.'|'.$interface.'}}'; else $interfaces[]=$interface; } return implode(', ',$interfaces); } public function renderSubclasses($class) { $subclasses=array(); foreach($class->subclasses as $subclass) { if(isset($this->classes[$subclass])) $subclasses[]='{{'.$subclass.'|'.$subclass.'}}'; else $subclasses[]=$subclass; } return implode(', ',$subclasses); } public function renderTypeUrl($type) { if(isset($this->classes[$type]) && $type!==$this->currentClass) return '{{'.$type.'|'.$type.'}}'; else return $type; } public function renderSubjectUrl($type,$subject,$text=null) { if($text===null) $text=$subject; if(isset($this->classes[$type])) return '{{'.$type.'::'.$subject.'|'.$text.'}}'; else return $text; } public function renderPropertySignature($property) { if(!empty($property->signature)) return $property->signature; $sig=''; if(!empty($property->getter)) $sig=$property->getter->signature; if(!empty($property->setter)) { if($sig!=='') $sig.='
'; $sig.=$property->setter->signature; } return $sig; } protected function fixOfflineLink($matches) { if(($pos=strpos($matches[1],'::'))!==false) { $className=substr($matches[1],0,$pos); $method=substr($matches[1],$pos+2); return "{$matches[2]}"; } else return "{$matches[2]}"; } protected function fixOnlineLink($matches) { if(($pos=strpos($matches[1],'::'))!==false) { $className=substr($matches[1],0,$pos); $method=substr($matches[1],$pos+2); if($className==='index') return "{$matches[2]}"; else return "{$matches[2]}"; } else { if($matches[1]==='index') return "{$matches[2]}"; else return "{$matches[2]}"; } } }