You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							353 lines
						
					
					
						
							8.9 KiB
						
					
					
				
			
		
		
	
	
							353 lines
						
					
					
						
							8.9 KiB
						
					
					
				<?php | 
						|
/** | 
						|
 * @link http://www.yiiframework.com/ | 
						|
 * @copyright Copyright (c) 2008 Yii Software LLC | 
						|
 * @license http://www.yiiframework.com/license/ | 
						|
 */ | 
						|
 | 
						|
namespace yii\apidoc\templates\html; | 
						|
 | 
						|
use yii\apidoc\helpers\Markdown; | 
						|
use yii\apidoc\models\BaseDoc; | 
						|
use yii\apidoc\models\ConstDoc; | 
						|
use yii\apidoc\models\EventDoc; | 
						|
use yii\apidoc\models\MethodDoc; | 
						|
use yii\apidoc\models\PropertyDoc; | 
						|
use yii\apidoc\models\TypeDoc; | 
						|
use yii\apidoc\models\ClassDoc; | 
						|
use yii\apidoc\models\Context; | 
						|
use yii\apidoc\models\InterfaceDoc; | 
						|
use yii\apidoc\models\TraitDoc; | 
						|
use yii\apidoc\templates\BaseRenderer; | 
						|
use yii\base\ViewContextInterface; | 
						|
use yii\console\Controller; | 
						|
use yii\helpers\Console; | 
						|
use yii\helpers\Html; | 
						|
use Yii; | 
						|
use yii\web\AssetManager; | 
						|
use yii\web\View; | 
						|
 | 
						|
/** | 
						|
 * The base class for HTML API documentation renderers. | 
						|
 * | 
						|
 * @author Carsten Brandt <mail@cebe.cc> | 
						|
 * @since 2.0 | 
						|
 */ | 
						|
abstract class Renderer extends BaseRenderer implements ViewContextInterface | 
						|
{ | 
						|
	/** | 
						|
	 * @var string directory to use for output of html files. Can be a path alias. | 
						|
	 */ | 
						|
	public $targetDir; | 
						|
	/** | 
						|
	 * @var string string to use as the title of the generated page. | 
						|
	 */ | 
						|
	public $pageTitle = 'Yii Framework 2.0 API Documentation'; | 
						|
	/** | 
						|
	 * @var string path or alias of the layout file to use. | 
						|
	 */ | 
						|
	public $layout; | 
						|
	/** | 
						|
	 * @var string path or alias of the view file to use for rendering types (classes, interfaces, traits). | 
						|
	 */ | 
						|
	public $typeView = '@yii/apidoc/templates/html/views/type.php'; | 
						|
	/** | 
						|
	 * @var string path or alias of the view file to use for rendering the index page. | 
						|
	 */ | 
						|
	public $indexView = '@yii/apidoc/templates/html/views/index.php'; | 
						|
	/** | 
						|
	 * @var View | 
						|
	 */ | 
						|
	private $_view; | 
						|
 | 
						|
 | 
						|
	public function init() | 
						|
	{ | 
						|
		Markdown::$renderer = $this; | 
						|
	} | 
						|
 | 
						|
	/** | 
						|
	 * @return View the view instance | 
						|
	 */ | 
						|
	public function getView() | 
						|
	{ | 
						|
		if ($this->_view === null) { | 
						|
			$this->_view = new View(); | 
						|
			$assetPath = Yii::getAlias($this->targetDir) . '/assets'; | 
						|
			if (!is_dir($assetPath)) { | 
						|
				mkdir($assetPath); | 
						|
			} | 
						|
			$this->_view->assetManager = new AssetManager([ | 
						|
				'basePath' => $assetPath, | 
						|
				'baseUrl' => './assets', | 
						|
			]); | 
						|
		} | 
						|
		return $this->_view; | 
						|
	} | 
						|
 | 
						|
	/** | 
						|
	 * Renders a given [[Context]]. | 
						|
	 * | 
						|
	 * @param Context $context the api documentation context to render. | 
						|
	 * @param Controller $controller the apidoc controller instance. Can be used to control output. | 
						|
	 */ | 
						|
	public function render($context, $controller) | 
						|
	{ | 
						|
		$this->context = $context; | 
						|
		$dir = Yii::getAlias($this->targetDir); | 
						|
		if (!is_dir($dir)) { | 
						|
			mkdir($dir); | 
						|
		} | 
						|
 | 
						|
		$types = array_merge($context->classes, $context->interfaces, $context->traits); | 
						|
		$typeCount = count($types) + 1; | 
						|
		Console::startProgress(0, $typeCount, 'Rendering files: ', false); | 
						|
		$done = 0; | 
						|
		foreach($types as $type) { | 
						|
			$fileContent = $this->renderWithLayout($this->typeView, [ | 
						|
				'type' => $type, | 
						|
				'docContext' => $context, | 
						|
			]); | 
						|
			file_put_contents($dir . '/' . $this->generateFileName($type->name), $fileContent); | 
						|
			Console::updateProgress(++$done, $typeCount); | 
						|
		} | 
						|
		$indexFileContent = $this->renderWithLayout($this->indexView, [ | 
						|
			'docContext' => $context, | 
						|
			'types' => $types, | 
						|
		]); | 
						|
		file_put_contents($dir . '/index.html', $indexFileContent); | 
						|
		Console::updateProgress(++$done, $typeCount); | 
						|
		Console::endProgress(true); | 
						|
		$controller->stdout('done.' . PHP_EOL, Console::FG_GREEN); | 
						|
	} | 
						|
 | 
						|
	protected function renderWithLayout($viewFile, $params) | 
						|
	{ | 
						|
		$output = $this->getView()->render($viewFile, $params, $this); | 
						|
		if ($this->layout !== false) { | 
						|
			$params['content'] = $output; | 
						|
			return $this->getView()->renderFile($this->layout, $params, $this); | 
						|
		} else { | 
						|
			return $output; | 
						|
		} | 
						|
	} | 
						|
 | 
						|
	/** | 
						|
	 * creates a link to a type (class, interface or trait) | 
						|
	 * @param ClassDoc|InterfaceDoc|TraitDoc $types | 
						|
	 * @param BaseDoc $context | 
						|
	 * @return string | 
						|
	 */ | 
						|
	public function typeLink($types, $context = null) | 
						|
	{ | 
						|
		if (!is_array($types)) { | 
						|
			$types = [$types]; | 
						|
		} | 
						|
		$links = []; | 
						|
		foreach($types as $type) { | 
						|
			$postfix = ''; | 
						|
			if (!is_object($type)) { | 
						|
				if (substr($type, -2, 2) == '[]') { | 
						|
					$postfix = '[]'; | 
						|
					$type = substr($type, 0, -2); | 
						|
				} | 
						|
 | 
						|
				if (($t = $this->context->getType(ltrim($type, '\\'))) !== null) { | 
						|
					$type = $t; | 
						|
				} elseif ($type[0] !== '\\' && ($t = $this->context->getType($this->resolveNamespace($context) . '\\' . ltrim($type, '\\'))) !== null) { | 
						|
					$type = $t; | 
						|
				} else { | 
						|
					ltrim($type, '\\'); | 
						|
				} | 
						|
			} | 
						|
			if (!is_object($type)) { | 
						|
				$links[] = $type; | 
						|
			} else { | 
						|
				$links[] = Html::a( | 
						|
					$type->name, | 
						|
					null, | 
						|
					['href' => $this->generateFileName($type->name)] | 
						|
				) . $postfix; | 
						|
			} | 
						|
		} | 
						|
		return implode('|', $links); | 
						|
	} | 
						|
 | 
						|
	/** | 
						|
	 * creates a link to a subject | 
						|
	 * @param PropertyDoc|MethodDoc|ConstDoc|EventDoc $subject | 
						|
	 * @param string $title | 
						|
	 * @return string | 
						|
	 */ | 
						|
	public function subjectLink($subject, $title = null) | 
						|
	{ | 
						|
		if ($title === null) { | 
						|
			if ($subject instanceof MethodDoc) { | 
						|
				$title = $subject->name . '()'; | 
						|
			} else { | 
						|
				$title = $subject->name; | 
						|
			} | 
						|
		} | 
						|
		if (($type = $this->context->getType($subject->definedBy)) === null) { | 
						|
			return $subject->name; | 
						|
		} else { | 
						|
			$link = $this->generateFileName($type->name); | 
						|
			if ($subject instanceof MethodDoc) { | 
						|
				$link .= '#' . $subject->name . '()'; | 
						|
			} else { | 
						|
				$link .= '#' . $subject->name; | 
						|
			} | 
						|
			$link .= '-detail'; | 
						|
			return Html::a($title, null, ['href' => $link]); | 
						|
		} | 
						|
	} | 
						|
 | 
						|
	/** | 
						|
	 * @param BaseDoc $context | 
						|
	 */ | 
						|
	private function resolveNamespace($context) | 
						|
	{ | 
						|
		// TODO use phpdoc Context for this | 
						|
		if ($context === null) { | 
						|
			return ''; | 
						|
		} | 
						|
		if ($context instanceof TypeDoc) { | 
						|
			return $context->namespace; | 
						|
		} | 
						|
		if ($context->hasProperty('definedBy')) { | 
						|
			$type = $this->context->getType($context); | 
						|
			if ($type !== null) { | 
						|
				return $type->namespace; | 
						|
			} | 
						|
		} | 
						|
		return ''; | 
						|
	} | 
						|
 | 
						|
	/** | 
						|
	 * @param ClassDoc $class | 
						|
	 * @return string | 
						|
	 */ | 
						|
	public function renderInheritance($class) | 
						|
	{ | 
						|
		$parents[] = $this->typeLink($class); | 
						|
		while ($class->parentClass !== null) { | 
						|
			if(isset($this->context->classes[$class->parentClass])) { | 
						|
				$class = $this->context->classes[$class->parentClass]; | 
						|
				$parents[] = $this->typeLink($class); | 
						|
			} else { | 
						|
				$parents[] = $class->parentClass; // TODO link to php.net | 
						|
				break; | 
						|
			} | 
						|
		} | 
						|
		return implode(" »\n",$parents); | 
						|
	} | 
						|
 | 
						|
	/** | 
						|
	 * @param array $names | 
						|
	 * @return string | 
						|
	 */ | 
						|
	public function renderInterfaces($names) | 
						|
	{ | 
						|
		$interfaces = []; | 
						|
		sort($names, SORT_STRING); | 
						|
		foreach($names as $interface) { | 
						|
			if(isset($this->context->interfaces[$interface])) { | 
						|
				$interfaces[] = $this->typeLink($this->context->interfaces[$interface]); | 
						|
			} else { | 
						|
				$interfaces[] = $interface; // TODO link to php.net | 
						|
			} | 
						|
		} | 
						|
		return implode(', ',$interfaces); | 
						|
	} | 
						|
 | 
						|
	/** | 
						|
	 * @param array $names | 
						|
	 * @return string | 
						|
	 */ | 
						|
	public function renderTraits($names) | 
						|
	{ | 
						|
		$traits = []; | 
						|
		sort($names, SORT_STRING); | 
						|
		foreach($names as $trait) { | 
						|
			if(isset($this->context->traits[$trait])) { | 
						|
				$traits[] = $this->typeLink($this->context->traits[$trait]); | 
						|
			} else { | 
						|
				$traits[] = $trait; // TODO link to php.net | 
						|
			} | 
						|
		} | 
						|
		return implode(', ',$traits); | 
						|
	} | 
						|
 | 
						|
	/** | 
						|
	 * @param array $names | 
						|
	 * @return string | 
						|
	 */ | 
						|
	public function renderClasses($names) | 
						|
	{ | 
						|
		$classes = []; | 
						|
		sort($names, SORT_STRING); | 
						|
		foreach($names as $class) { | 
						|
			if(isset($this->context->classes[$class])) { | 
						|
				$classes[] = $this->typeLink($this->context->classes[$class]); | 
						|
			} else { | 
						|
				$classes[] = $class; // TODO link to php.net | 
						|
			} | 
						|
		} | 
						|
		return implode(', ',$classes); | 
						|
	} | 
						|
 | 
						|
	/** | 
						|
	 * @param PropertyDoc $property | 
						|
	 * @return string | 
						|
	 */ | 
						|
	public function renderPropertySignature($property) | 
						|
	{ | 
						|
		if ($property->getter !== null || $property->setter !== null) { | 
						|
			$sig = []; | 
						|
			if ($property->getter !== null) { | 
						|
				$sig[] = $this->renderMethodSignature($property->getter); | 
						|
			} | 
						|
			if ($property->setter !== null) { | 
						|
				$sig[] = $this->renderMethodSignature($property->setter); | 
						|
			} | 
						|
			return implode('<br />', $sig); | 
						|
		} | 
						|
		return $this->typeLink($property->types) . ' ' . $property->name . ' = ' . ($property->defaultValue === null ? 'null' : $property->defaultValue); | 
						|
	} | 
						|
 | 
						|
	/** | 
						|
	 * @param MethodDoc $method | 
						|
	 * @return string | 
						|
	 */ | 
						|
	public function renderMethodSignature($method) | 
						|
	{ | 
						|
		$params = []; | 
						|
		foreach($method->params as $param) { | 
						|
			$params[] = (empty($param->typeHint) ? '' : $param->typeHint . ' ') | 
						|
				. ($param->isPassedByReference ? '<b>&</b>' : '') | 
						|
				. $param->name | 
						|
				. ($param->isOptional ? ' = ' . $param->defaultValue : ''); | 
						|
		} | 
						|
 | 
						|
		return ($method->isReturnByReference ? '<b>&</b>' : '') | 
						|
			. ($method->returnType === null ? 'void' : $this->typeLink($method->returnTypes)) | 
						|
			. ' ' . $this->subjectLink($method, $method->name) . '( ' | 
						|
			. implode(', ', $params) | 
						|
			. ' )'; | 
						|
	} | 
						|
 | 
						|
	protected function generateFileName($typeName) | 
						|
	{ | 
						|
		return strtolower(str_replace('\\', '_', $typeName)) . '.html'; | 
						|
	} | 
						|
 | 
						|
	/** | 
						|
	 * Finds the view file corresponding to the specified relative view name. | 
						|
	 * @param string $view a relative view name. The name does NOT start with a slash. | 
						|
	 * @return string the view file path. Note that the file may not exist. | 
						|
	 */ | 
						|
	public function findViewFile($view) | 
						|
	{ | 
						|
		return Yii::getAlias('@yii/apidoc/templates/html/views/' . $view); | 
						|
	} | 
						|
} |