Commit 52c92563 by Qiang Xue

Merge pull request #1784 from yiisoft/api-doc-generator

api doc generator
parents 5eed149f c7c4d3c0
Yii Framework 2 apidoc extension Change Log
===========================================
2.0.0 beta under development
----------------------------
- Initial release.
The Yii framework is free software. It is released under the terms of
the following BSD License.
Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Yii Software LLC nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
API documentation generator for Yii 2
=====================================
This extension provides an API documentation generator for the Yii framework 2.0.
Installation
------------
The preferred way to install this extension is through [composer](http://getcomposer.org/download/).
Either run
```
php composer.phar require yiisoft/yii2-apidoc "*"
```
or add
```json
"yiisoft/yii2-apidoc": "*"
```
to the require section of your composer.json.
Usage
-----
To generate API documentation, run the `apidoc` command.
```
vendor/bin/apidoc source/directory ./output
```
By default the `offline` template will be used. You can choose a different templates with the `--template=name` parameter.
Currently there is only the `offline` template available.
You may also add the `yii\apidoc\commands\RenderController` to your console application class map and
run it inside of your applications console app.
Creating your own templates
---------------------------
TDB
Using the model layer
---------------------
TDB
\ No newline at end of file
#!/usr/bin/env php
<?php
/**
* Yii Framework 2.0 API documentation generator
*
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
defined('YII_DEBUG') or define('YII_DEBUG', false);
$composerAutoload = [
__DIR__ . '/vendor/autoload.php', // standalone with "composer install" run
__DIR__ . '/../../../../autoload.php', // script is installed as a composer binary
];
foreach($composerAutoload as $autoload) {
if (file_exists($autoload)) {
require($autoload);
break;
}
}
$yiiDirs = [
__DIR__ . '/../../../framework', // in yii2-dev repo
__DIR__ . '/vendor/yiisoft/yii2', // standalone with "composer install" run
__DIR__ . '/../../../../yiisoft/yii2', // script is installed as a composer binary
];
foreach($yiiDirs as $dir) {
if (file_exists($dir . '/yii/Yii.php')) {
require($dir . '/yii/Yii.php');
break;
}
}
if (!class_exists('Yii')) {
echo PHP_EOL . "The Yii Framework 2.0 does not seem to be installed. Try running composer install." . PHP_EOL . PHP_EOL;
exit(1);
}
Yii::setAlias('@yii/apidoc', __DIR__);
$application = new yii\console\Application([
'id' => 'yii2-apidoc',
'basePath' => __DIR__,
'enableCoreCommands' => false,
'controllerNamespace' => 'yii\\apidoc\\commands',
'controllerPath' => '@yii/apidoc/commands',
]);
$exitCode = $application->run();
exit($exitCode);
@echo off
rem -------------------------------------------------------------
rem Yii command line bootstrap script for Windows.
rem
rem @author Qiang Xue <qiang.xue@gmail.com>
rem @link http://www.yiiframework.com/
rem @copyright Copyright &copy; 2012 Yii Software LLC
rem @license http://www.yiiframework.com/license/
rem -------------------------------------------------------------
@setlocal
set YII_PATH=%~dp0
if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
"%PHP_COMMAND%" "%YII_PATH%apidoc" %*
@endlocal
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\commands;
use phpDocumentor\Reflection\FileReflector;
use TokenReflection\ReflectionFile;
use yii\apidoc\templates\BaseRenderer;
use yii\console\Controller;
use yii\helpers\Console;
use yii\helpers\FileHelper;
use yii\apidoc\components\OfflineRenderer;
use yii\apidoc\models\Context;
use Yii;
/**
* Command to render API Documentation files
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class RenderController extends Controller
{
public $template = 'offline';
/**
* Renders API documentation files
* @param array $sourceDirs
* @param string $targetDir
* @return int
*/
public function actionIndex(array $sourceDirs, $targetDir)
{
$targetDir = rtrim(Yii::getAlias($targetDir), '\\/');
if (is_dir($targetDir) && !$this->confirm('TargetDirectory already exists. Overwrite?')) {
return 2;
}
if (!is_dir($targetDir)) {
mkdir($targetDir);
}
$renderer = $this->findRenderer();
$renderer->targetDir = $targetDir;
$this->stdout('Searching files to process... ');
$files = [];
foreach($sourceDirs as $source) {
foreach($this->findFiles($source) as $fileName) {
$files[$fileName] = $fileName;
}
}
$this->stdout('done.' . PHP_EOL, Console::FG_GREEN);
$context = new Context();
$cacheFile = $targetDir . '/cache/' . md5(serialize($files)) . '.tmp';
if (file_exists($cacheFile)) {
$this->stdout('Loading processed data from cache... ');
$context = unserialize(file_get_contents($cacheFile));
$this->stdout('done.' . PHP_EOL, Console::FG_GREEN);
$this->stdout('Checking for updated files... ');
foreach($context->files as $file => $sha) {
if (sha1_file($file) === $sha) {
unset($files[$file]);
}
}
$this->stdout('done.' . PHP_EOL, Console::FG_GREEN);
}
$fileCount = count($files);
$this->stdout($fileCount . ' file' . ($fileCount == 1 ? '' : 's') . ' to update.' . PHP_EOL);
Console::startProgress(0, $fileCount, 'Processing files... ', false);
$done = 0;
foreach($files as $file) {
$context->addFile($file);
Console::updateProgress(++$done, $fileCount);
}
Console::endProgress(true);
$this->stdout('done.' . PHP_EOL, Console::FG_GREEN);
// save processed data to cache
if (!is_dir(dirname($cacheFile))) {
mkdir(dirname($cacheFile));
}
file_put_contents($cacheFile, serialize($context));
$this->stdout('Updating cross references and backlinks... ');
$context->updateReferences();
$this->stdout('done.' . PHP_EOL, Console::FG_GREEN);
// render models
$renderer->render($context, $this);
}
/**
* @return BaseRenderer
*/
protected function findRenderer()
{
$file = Yii::getAlias('@yii/apidoc/templates/' . $this->template . '/Renderer.php');
$reflection = new FileReflector($file, true);
$reflection->process();
$classes = $reflection->getClasses();
if (empty($classes)) {
$this->stderr('Renderer not found.' . PHP_EOL);
}
$rendererClass = reset($classes)->getName();
require($file);
return new $rendererClass();
}
protected function findFiles($path, $except = [])
{
$path = FileHelper::normalizePath($path);
$options = [
'filter' => function ($path) {
if (is_file($path)) {
$file = basename($path);
if ($file[0] < 'A' || $file[0] > 'Z') {
return false;
}
}
return null;
},
'only' => ['.php'],
'except' => $except,
];
return FileHelper::findFiles($path, $options);
}
/**
* @inheritdoc
*/
public function globalOptions()
{
return array_merge(parent::globalOptions(), ['template']);
}
}
\ No newline at end of file
{
"name": "yiisoft/yii2-apidoc",
"description": "API Documentation generator for the Yii framework 2.0",
"keywords": ["yii", "phpdoc", "apidoc", "api", "documentation"],
"type": "yii2-extension",
"license": "BSD-3-Clause",
"support": {
"issues": "https://github.com/yiisoft/yii2/issues?labels=ext%3Aapidoc",
"forum": "http://www.yiiframework.com/forum/",
"wiki": "http://www.yiiframework.com/wiki/",
"irc": "irc://irc.freenode.net/yii",
"source": "https://github.com/yiisoft/yii2"
},
"authors": [
{
"name": "Carsten Brandt",
"email": "mail@cebe.cc"
}
],
"minimum-stability": "dev",
"require": {
"yiisoft/yii2": "*",
"phpdocumentor/reflection": "1.0.2"
},
"autoload": {
"psr-0": { "yii\\apidoc\\": "" }
},
"target-dir": "yii/apidoc",
"bin": ["apidoc"]
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\helpers;
use phpDocumentor\Reflection\DocBlock\Type\Collection;
use yii\apidoc\models\MethodDoc;
use yii\apidoc\models\TypeDoc;
use yii\apidoc\templates\BaseRenderer;
/**
* A Markdown helper with support for class reference links.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class Markdown extends \yii\helpers\Markdown
{
/**
* @var BaseRenderer
*/
public static $renderer;
/**
* Converts markdown into HTML
*
* @param string $content
* @param TypeDoc $context
* @return string
*/
public static function process($content, $context)
{
$content = trim(parent::process($content, []));
if (!strncmp($content, '<p>', 3) && substr($content, -4, 4) == '</p>') {
$content = substr($content, 3, -4);
}
$content = preg_replace_callback('/\[\[([\w\d\\\\\(\):]+)(\|[\w\d ]*)?\]\]/xm', function($matches) use ($context) {
$object = $matches[1];
$title = (empty($matches[2]) || $matches[2] == '|') ? null : substr($matches[2], 1);
if (($pos = strpos($object, '::')) !== false) {
$typeName = substr($object, 0, $pos);
$subjectName = substr($object, $pos + 2);
// Collection resolves relative types
$typeName = (new Collection([$typeName], $context->phpDocContext))->__toString();
$type = static::$renderer->context->getType($typeName);
if ($type === null) {
return '<span style="background: #f00;">' . $typeName . '::' . $subjectName . '</span>';
} else {
if (($subject = $type->findSubject($subjectName)) !== null) {
if ($title === null) {
$title = $type->name . '::' . $subject->name;
if ($subject instanceof MethodDoc) {
$title .= '()';
}
}
return static::$renderer->subjectLink($subject, $title);
} else {
return '<span style="background: #ff0;">' . $type->name . '</span><span style="background: #f00;">::' . $subjectName . '</span>';
}
}
} elseif (($subject = $context->findSubject($object)) !== null) {
return static::$renderer->subjectLink($subject, $title);
}
// Collection resolves relative types
$object = (new Collection([$object], $context->phpDocContext))->__toString();
if (($type = static::$renderer->context->getType($object)) !== null) {
return static::$renderer->typeLink($type, $title);
}
return '<span style="background: #f00;">' . $object . '</span>';
}, $content);
return $content;
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\helpers;
use PHPParser_Node_Expr;
use PHPParser_Node_Expr_Array;
/**
* Enhances the phpDocumentor PrettyPrinter with short array syntax
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class PrettyPrinter extends \phpDocumentor\Reflection\PrettyPrinter
{
public function pExpr_Array(PHPParser_Node_Expr_Array $node)
{
return '[' . $this->pCommaSeparated($node->items) . ']';
}
/**
* Returns a simple human readable output for a value.
*
* @param PHPParser_Node_Expr $value The value node as provided by PHP-Parser.
* @return string
*/
public static function getRepresentationOfValue(PHPParser_Node_Expr $value)
{
if ($value === null) {
return '';
}
$printer = new static();
return $printer->prettyPrintExpr($value);
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\models;
use phpDocumentor\Reflection\DocBlock\Tag\DeprecatedTag;
use phpDocumentor\Reflection\DocBlock\Tag\SinceTag;
use yii\base\Object;
/**
* Base class for API documentation information.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class BaseDoc extends Object
{
/**
* @var \phpDocumentor\Reflection\DocBlock\Context
*/
public $phpDocContext;
public $name;
public $sourceFile;
public $startLine;
public $endLine;
public $shortDescription;
public $description;
public $since;
public $deprecatedSince;
public $deprecatedReason;
/**
* @var \phpDocumentor\Reflection\DocBlock\Tag[]
*/
public $tags = [];
/**
* @param \phpDocumentor\Reflection\BaseReflector $reflector
* @param array $config
*/
public function __construct($reflector = null, $config = [])
{
parent::__construct($config);
if ($reflector === null) {
return;
}
// base properties
$this->name = ltrim($reflector->getName(), '\\');
$this->startLine = $reflector->getNode()->getAttribute('startLine');
$this->endLine = $reflector->getNode()->getAttribute('endLine');
$docblock = $reflector->getDocBlock();
if ($docblock !== null) {
$this->shortDescription = ucfirst($docblock->getShortDescription());
$this->description = $docblock->getLongDescription();
$this->phpDocContext = $docblock->getContext();
$this->tags = $docblock->getTags();
foreach($this->tags as $i => $tag) {
if ($tag instanceof SinceTag) {
$this->since = $tag->getVersion();
unset($this->tags[$i]);
} elseif ($tag instanceof DeprecatedTag) {
$this->deprecatedSince = $tag->getVersion();
$this->deprecatedReason = $tag->getDescription();
unset($this->tags[$i]);
}
}
}
}
// TODO
public function loadSource($reflection)
{
$this->sourcePath=str_replace('\\','/',str_replace(YII_PATH,'',$reflection->getFileName()));
$this->startLine=$reflection->getStartLine();
$this->endLine=$reflection->getEndLine();
}
public function getSourceUrl($baseUrl,$line=null)
{
if($line===null)
return $baseUrl.$this->sourcePath;
else
return $baseUrl.$this->sourcePath.'#'.$line;
}
public function getSourceCode()
{
$lines=file(YII_PATH.$this->sourcePath);
return implode("",array_slice($lines,$this->startLine-1,$this->endLine-$this->startLine+1));
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\models;
/**
* Represents API documentation information for a `class`.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class ClassDoc extends TypeDoc
{
public $parentClass;
public $isAbstract;
public $isFinal;
public $interfaces = [];
public $traits = [];
// will be set by Context::updateReferences()
public $subclasses = [];
/**
* @var EventDoc[]
*/
public $events = [];
/**
* @var ConstDoc[]
*/
public $constants = [];
public function findSubject($subjectName)
{
if (($subject = parent::findSubject($subjectName)) !== null) {
return $subject;
}
foreach($this->events as $name => $event) {
if ($subjectName == $name) {
return $event;
}
}
foreach($this->constants as $name => $constant) {
if ($subjectName == $name) {
return $constant;
}
}
return null;
}
/**
* @return EventDoc[]
*/
public function getNativeEvents()
{
$events = [];
foreach($this->events as $name => $event) {
if ($event->definedBy != $this->name) {
continue;
}
$events[$name] = $event;
}
return $events;
}
/**
* @param \phpDocumentor\Reflection\ClassReflector $reflector
* @param array $config
*/
public function __construct($reflector = null, $config = [])
{
parent::__construct($reflector, $config);
if ($reflector === null) {
return;
}
$this->parentClass = ltrim($reflector->getParentClass(), '\\');
if (empty($this->parentClass)) {
$this->parentClass = null;
}
$this->isAbstract = $reflector->isAbstract();
$this->isFinal = $reflector->isFinal();
foreach($reflector->getInterfaces() as $interface) {
$this->interfaces[] = ltrim($interface, '\\');
}
foreach($reflector->getTraits() as $trait) {
$this->traits[] = ltrim($trait, '\\');
}
foreach($reflector->getConstants() as $constantReflector) {
$docblock = $constantReflector->getDocBlock();
if ($docblock !== null && count($docblock->getTagsByName('event')) > 0) {
$event = new EventDoc($constantReflector);
$event->definedBy = $this->name;
$this->events[$event->name] = $event;
} else {
$constant = new ConstDoc($constantReflector);
$constant->definedBy = $this->name;
$this->constants[$constant->name] = $constant;
}
}
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\models;
/**
* Represents API documentation information for a `constant`.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class ConstDoc extends BaseDoc
{
public $definedBy;
public $value;
/**
* @param \phpDocumentor\Reflection\ClassReflector\ConstantReflector $reflector
* @param array $config
*/
public function __construct($reflector = null, $config = [])
{
parent::__construct($reflector, $config);
if ($reflector === null) {
return;
}
$this->value = $reflector->getValue();
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\models;
use phpDocumentor\Reflection\FileReflector;
use yii\base\Component;
use yii\base\Exception;
/**
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class Context extends Component
{
/**
* @var array list of php files that have been added to this context.
*/
public $files = [];
/**
* @var ClassDoc[]
*/
public $classes = [];
/**
* @var InterfaceDoc[]
*/
public $interfaces = [];
/**
* @var TraitDoc[]
*/
public $traits = [];
public function getType($type)
{
$type = ltrim($type, '\\');
if (isset($this->classes[$type])) {
return $this->classes[$type];
} elseif (isset($this->interfaces[$type])) {
return $this->interfaces[$type];
} elseif (isset($this->traits[$type])) {
return $this->traits[$type];
}
return null;
}
public function addFile($fileName)
{
$this->files[$fileName] = sha1_file($fileName);
$reflection = new FileReflector($fileName, true);
$reflection->process();
foreach($reflection->getClasses() as $class) {
$class = new ClassDoc($class);
$class->sourceFile = $fileName;
$this->classes[$class->name] = $class;
}
foreach($reflection->getInterfaces() as $interface) {
$interface = new InterfaceDoc($interface);
$interface->sourceFile = $fileName;
$this->interfaces[$interface->name] = $interface;
}
foreach($reflection->getTraits() as $trait) {
$trait = new TraitDoc($trait);
$trait->sourceFile = $fileName;
$this->traits[$trait->name] = $trait;
}
}
public function updateReferences()
{
// update all subclass references
foreach($this->classes as $class) {
$className = $class->name;
while (isset($this->classes[$class->parentClass])) {
$class = $this->classes[$class->parentClass];
$class->subclasses[] = $className;
}
}
// update interfaces of subclasses
foreach($this->classes as $class) {
$this->updateSubclassInferfacesTraits($class);
}
// update implementedBy and usedBy for interfaces and traits
foreach($this->classes as $class) {
foreach($class->interfaces as $interface) {
if (isset($this->interfaces[$interface])) {
$this->interfaces[$interface]->implementedBy[] = $class->name;
}
}
foreach($class->traits as $trait) {
if (isset($this->traits[$trait])) {
$trait = $this->traits[$trait];
$trait->usedBy[] = $class->name;
$class->properties = array_merge($trait->properties, $class->properties);
$class->methods = array_merge($trait->methods, $class->methods);
}
}
}
// inherit properties, methods, contants and events to subclasses
foreach($this->classes as $class) {
$this->updateSubclassInheritance($class);
}
// add properties from getters and setters
foreach($this->classes as $class) {
$this->handlePropertyFeature($class);
}
// TODO reference exceptions to methods where they are thrown
}
/**
* Add implemented interfaces and used traits to subclasses
* @param ClassDoc $class
*/
protected function updateSubclassInferfacesTraits($class)
{
foreach($class->subclasses as $subclass) {
$subclass = $this->classes[$subclass];
$subclass->interfaces = array_unique(array_merge($subclass->interfaces, $class->interfaces));
$subclass->traits = array_unique(array_merge($subclass->traits, $class->traits));
$this->updateSubclassInferfacesTraits($subclass);
}
}
/**
* Add implemented interfaces and used traits to subclasses
* @param ClassDoc $class
*/
protected function updateSubclassInheritance($class)
{
foreach($class->subclasses as $subclass) {
$subclass = $this->classes[$subclass];
$subclass->events = array_merge($class->events, $subclass->events);
$subclass->constants = array_merge($class->constants, $subclass->constants);
$subclass->properties = array_merge($class->properties, $subclass->properties);
$subclass->methods = array_merge($class->methods, $subclass->methods);
$this->updateSubclassInheritance($subclass);
}
}
/**
* Add properties for getters and setters if class is subclass of [[yii\base\Object]].
* @param ClassDoc $class
*/
protected function handlePropertyFeature($class)
{
if (!$this->isSubclassOf($class, 'yii\base\Object')) {
return;
}
foreach($class->getPublicMethods() as $name => $method) {
if (!strncmp($name, 'get', 3) && $this->paramsOptional($method)) {
$propertyName = '$' . lcfirst(substr($method->name, 3));
if (isset($class->properties[$propertyName])) {
$property = $class->properties[$propertyName];
if ($property->getter === null && $property->setter === null) {
echo "Property $propertyName conflicts with a defined getter {$method->name} in {$class->name}.\n"; // TODO log these messages somewhere
}
$property->getter = $method;
} else {
$class->properties[$propertyName] = new PropertyDoc(null, [
'name' => $propertyName,
'definedBy' => $class->name,
'visibility' => 'public',
'isStatic' => false,
'type' => $method->returnType,
'types' => $method->returnTypes,
'shortDescription' => (($pos = strpos($method->return, '.')) !== false) ?
substr($method->return, 0, $pos) : $method->return,
'description' => $method->return,
'getter' => $method
// TODO set default value
]);
}
}
if (!strncmp($name, 'set', 3) && $this->paramsOptional($method, 1)) {
$propertyName = '$' . lcfirst(substr($method->name, 3));
if (isset($class->properties[$propertyName])) {
$property = $class->properties[$propertyName];
if ($property->getter === null && $property->setter === null) {
echo "Property $propertyName conflicts with a defined setter {$method->name} in {$class->name}.\n"; // TODO log these messages somewhere
}
$property->setter = $method;
} else {
$param = $this->getFirstNotOptionalParameter($method);
$class->properties[$propertyName] = new PropertyDoc(null, [
'name' => $propertyName,
'definedBy' => $class->name,
'visibility' => 'public',
'isStatic' => false,
'type' => $param->type,
'types' => $param->types,
'shortDescription' => (($pos = strpos($param->description, '.')) !== false) ?
substr($param->description, 0, $pos) : $param->description,
'description' => $param->description,
'setter' => $method
]);
}
}
}
}
/**
* @param MethodDoc $method
* @param integer $number number of not optional parameters
* @return bool
*/
private function paramsOptional($method, $number = 0)
{
foreach($method->params as $param) {
if (!$param->isOptional && $number-- <= 0) {
return false;
}
}
return true;
}
/**
* @param MethodDoc $method
* @return ParamDoc
*/
private function getFirstNotOptionalParameter($method)
{
foreach($method->params as $param) {
if (!$param->isOptional) {
return $param;
}
}
return null;
}
/**
* @param ClassDoc $classA
* @param ClassDoc $classB
*/
protected function isSubclassOf($classA, $classB)
{
if (is_object($classB)) {
$classB = $classB->name;
}
if ($classA->name == $classB) {
return true;
}
while($classA->parentClass !== null && isset($this->classes[$classA->parentClass])) {
$classA = $this->classes[$classA->parentClass];
if ($classA->name == $classB) {
return true;
}
}
return false;
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\models;
use phpDocumentor\Reflection\DocBlock\Tag\ParamTag;
use phpDocumentor\Reflection\DocBlock\Tag\ReturnTag;
/**
* Represents API documentation information for an `event`.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class EventDoc extends ConstDoc
{
public $type;
public $types;
/**
* @param \phpDocumentor\Reflection\ClassReflector\ConstantReflector $reflector
* @param array $config
*/
public function __construct($reflector = null, $config = [])
{
parent::__construct($reflector, $config);
if ($reflector === null) {
return;
}
foreach($this->tags as $i => $tag) {
if ($tag->getName() == 'event') {
$eventTag = new ReturnTag('event', $tag->getContent(), $tag->getDocBlock(), $tag->getLocation());
$this->type = $eventTag->getType();
$this->types = $eventTag->getTypes();
$this->description = ucfirst($eventTag->getDescription());
if (($pos = strpos($this->description, '.')) !== false) {
$this->shortDescription = substr($this->description, 0, $pos);
} else {
$this->shortDescription = $this->description;
}
unset($this->tags[$i]);
}
}
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\models;
use phpDocumentor\Reflection\DocBlock\Tag\ParamTag;
use phpDocumentor\Reflection\DocBlock\Tag\PropertyTag;
use phpDocumentor\Reflection\DocBlock\Tag\ReturnTag;
use phpDocumentor\Reflection\DocBlock\Tag\ThrowsTag;
use yii\base\Exception;
/**
* Represents API documentation information for a `function`.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class FunctionDoc extends BaseDoc
{
/**
* @var ParamDoc[]
*/
public $params = [];
public $exceptions = [];
public $return;
public $returnType;
public $returnTypes;
public $isReturnByReference;
/**
* @param \phpDocumentor\Reflection\FunctionReflector $reflector
* @param array $config
*/
public function __construct($reflector = null, $config = [])
{
parent::__construct($reflector, $config);
if ($reflector === null) {
return;
}
$this->isReturnByReference = $reflector->isByRef();
foreach($reflector->getArguments() as $arg) {
$arg = new ParamDoc($arg);
$this->params[$arg->name] = $arg;
}
foreach($this->tags as $i => $tag) {
if ($tag instanceof ThrowsTag) {
$this->exceptions[$tag->getType()] = $tag->getDescription();
unset($this->tags[$i]);
} elseif ($tag instanceof PropertyTag) {
// ignore property tag
} elseif ($tag instanceof ParamTag) {
$paramName = $tag->getVariableName();
if (!isset($this->params[$paramName])) {
echo 'undefined parameter documented: ' . $paramName . ' in ' . $this->name . "()\n"; // TODO log these messages somewhere
continue;
}
$this->params[$paramName]->description = ucfirst($tag->getDescription());
$this->params[$paramName]->type = $tag->getType();
$this->params[$paramName]->types = $tag->getTypes();
unset($this->tags[$i]);
} elseif ($tag instanceof ReturnTag) {
$this->returnType = $tag->getType();
$this->returnTypes = $tag->getTypes();
$this->return = $tag->getDescription();
unset($this->tags[$i]);
}
}
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\models;
/**
* Represents API documentation information for an `interface`.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class InterfaceDoc extends TypeDoc
{
public $parentInterfaces = [];
// will be set by Context::updateReferences()
public $implementedBy = [];
/**
* @param \phpDocumentor\Reflection\InterfaceReflector $reflector
* @param array $config
*/
public function __construct($reflector = null, $config = [])
{
parent::__construct($reflector, $config);
if ($reflector === null) {
return;
}
foreach($reflector->getParentInterfaces() as $interface) {
$this->parentInterfaces[] = ltrim($interface, '\\');
}
// interface can not have properties
$this->properties = null;
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\models;
/**
* Represents API documentation information for a `method`.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class MethodDoc extends FunctionDoc
{
public $isAbstract;
public $isFinal;
public $isStatic;
public $visibility;
// will be set by creating class
public $definedBy;
/**
* @param \phpDocumentor\Reflection\ClassReflector\MethodReflector $reflector
* @param array $config
*/
public function __construct($reflector = null, $config = [])
{
parent::__construct($reflector, $config);
if ($reflector === null) {
return;
}
$this->isAbstract = $reflector->isAbstract();
$this->isFinal = $reflector->isFinal();
$this->isStatic = $reflector->isStatic();
$this->visibility = $reflector->getVisibility();
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\models;
use yii\apidoc\helpers\PrettyPrinter;
use yii\base\Object;
/**
* Represents API documentation information for a [[FunctionDoc|function]] or [[MethodDoc|method]] `param`.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class ParamDoc extends Object
{
public $name;
public $typeHint;
public $isOptional;
public $defaultValue;
public $isPassedByReference;
// will be set by creating class
public $description;
public $type;
public $types;
/**
* @param \phpDocumentor\Reflection\FunctionReflector\ArgumentReflector $reflector
* @param array $config
*/
public function __construct($reflector = null, $config = [])
{
parent::__construct($config);
if ($reflector === null) {
return;
}
$this->name = $reflector->getName();
$this->typeHint = $reflector->getType();
$this->isOptional = $reflector->getDefault() !== null;
// bypass $reflector->getDefault() for short array syntax
if ($reflector->getNode()->default) {
$this->defaultValue = PrettyPrinter::getRepresentationOfValue($reflector->getNode()->default);
}
$this->isPassedByReference = $reflector->isByRef();
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\models;
use phpDocumentor\Reflection\DocBlock\Tag\VarTag;
use yii\apidoc\helpers\PrettyPrinter;
/**
* Represents API documentation information for a `property`.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class PropertyDoc extends BaseDoc
{
public $visibility;
public $isStatic;
public $type;
public $types;
public $defaultValue;
// will be set by creating class
public $getter;
public $setter;
// will be set by creating class
public $definedBy;
public function getIsReadOnly()
{
return $this->getter !== null && $this->setter === null;
}
public function getIsWriteOnly()
{
return $this->getter === null && $this->setter !== null;
}
/**
* @param \phpDocumentor\Reflection\ClassReflector\PropertyReflector $reflector
* @param array $config
*/
public function __construct($reflector = null, $config = [])
{
parent::__construct($reflector, $config);
if ($reflector === null) {
return;
}
$this->visibility = $reflector->getVisibility();
$this->isStatic = $reflector->isStatic();
// bypass $reflector->getDefault() for short array syntax
if ($reflector->getNode()->default) {
$this->defaultValue = PrettyPrinter::getRepresentationOfValue($reflector->getNode()->default);
}
foreach($this->tags as $i => $tag) {
if ($tag instanceof VarTag) {
$this->type = $tag->getType();
$this->types = $tag->getTypes();
$this->description = ucfirst($tag->getDescription());
if (($pos = strpos($this->description, '.')) !== false) {
$this->shortDescription = substr($this->description, 0, $pos);
} else {
$this->shortDescription = $this->description;
}
}
}
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\models;
/**
* Represents API documentation information for a `trait`.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class TraitDoc extends TypeDoc
{
// classes using the trait
// will be set by Context::updateReferences()
public $usedBy = [];
public $traits = [];
/**
* @param \phpDocumentor\Reflection\TraitReflector $reflector
* @param array $config
*/
public function __construct($reflector = null, $config = [])
{
parent::__construct($reflector, $config);
if ($reflector === null) {
return;
}
foreach($reflector->getTraits() as $trait) {
$this->traits[] = ltrim($trait, '\\');
}
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\models;
use phpDocumentor\Reflection\DocBlock\Tag\AuthorTag;
use yii\base\Exception;
use yii\helpers\StringHelper;
/**
* Base class for API documentation information for classes, interfaces and traits.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class TypeDoc extends BaseDoc
{
public $authors = [];
/**
* @var MethodDoc[]
*/
public $methods = [];
/**
* @var PropertyDoc[]
*/
public $properties = [];
public $namespace;
public function findSubject($subjectName)
{
if ($subjectName[0] != '$') {
foreach($this->methods as $name => $method) {
if (rtrim($subjectName, '()') == $name) {
return $method;
}
}
}
if (substr($subjectName, -2, 2) == '()') {
return null;
}
if ($this->properties === null) {
return null;
}
foreach($this->properties as $name => $property) {
if (ltrim($subjectName, '$') == ltrim($name, '$')) {
return $property;
}
}
return null;
}
/**
* @return MethodDoc[]
*/
public function getNativeMethods()
{
return $this->getFilteredMethods(null, $this->name);
}
/**
* @return MethodDoc[]
*/
public function getPublicMethods()
{
return $this->getFilteredMethods('public');
}
/**
* @return MethodDoc[]
*/
public function getProtectedMethods()
{
return $this->getFilteredMethods('protected');
}
/**
* @param null $visibility
* @param null $definedBy
* @return MethodDoc[]
*/
private function getFilteredMethods($visibility = null, $definedBy = null)
{
$methods = [];
foreach($this->methods as $name => $method) {
if ($visibility !== null && $method->visibility != $visibility) {
continue;
}
if ($definedBy !== null && $method->definedBy != $definedBy) {
continue;
}
$methods[$name] = $method;
}
return $methods;
}
/**
* @return PropertyDoc[]
*/
public function getNativeProperties()
{
return $this->getFilteredProperties(null, $this->name);
}
/**
* @return PropertyDoc[]
*/
public function getPublicProperties()
{
return $this->getFilteredProperties('public');
}
/**
* @return PropertyDoc[]
*/
public function getProtectedProperties()
{
return $this->getFilteredProperties('protected');
}
/**
* @param null $visibility
* @param null $definedBy
* @return PropertyDoc[]
*/
private function getFilteredProperties($visibility = null, $definedBy = null)
{
if ($this->properties === null) {
return [];
}
$properties = [];
foreach($this->properties as $name => $property) {
if ($visibility !== null && $property->visibility != $visibility) {
continue;
}
if ($definedBy !== null && $property->definedBy != $definedBy) {
continue;
}
$properties[$name] = $property;
}
return $properties;
}
/**
* @param \phpDocumentor\Reflection\InterfaceReflector $reflector
* @param array $config
*/
public function __construct($reflector = null, $config = [])
{
parent::__construct($reflector, $config);
$this->namespace = StringHelper::basename($this->name);
if ($reflector === null) {
return;
}
foreach($this->tags as $i => $tag) {
if ($tag instanceof AuthorTag) {
$this->authors[$tag->getAuthorName()] = $tag->getAuthorEmail();
unset($this->tags[$i]);
}
}
foreach($reflector->getProperties() as $propertyReflector) {
if ($propertyReflector->getVisibility() != 'private') {
$property = new PropertyDoc($propertyReflector);
$property->definedBy = $this->name;
$this->properties[$property->name] = $property;
}
}
foreach($reflector->getMethods() as $methodReflector) {
if ($methodReflector->getVisibility() != 'private') {
$method = new MethodDoc($methodReflector);
$method->definedBy = $this->name;
$this->methods[$method->name] = $method;
}
}
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\templates;
use Yii;
use yii\apidoc\models\ClassDoc;
use yii\apidoc\models\ConstDoc;
use yii\apidoc\models\Context;
use yii\apidoc\models\EventDoc;
use yii\apidoc\models\InterfaceDoc;
use yii\apidoc\models\MethodDoc;
use yii\apidoc\models\PropertyDoc;
use yii\apidoc\models\TraitDoc;
use yii\base\Component;
use yii\console\Controller;
/**
* Base class for all API documentation renderers
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
abstract class BaseRenderer extends Component
{
/**
* @var Context the [[Context]] currently being rendered.
*/
public $context;
/**
* 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 abstract function render($context, $controller);
/**
* creates a link to a type (class, interface or trait)
* @param ClassDoc|InterfaceDoc|TraitDoc $types
* @param string $title
* @return string
*/
public abstract function typeLink($types, $title = null);
/**
* creates a link to a subject
* @param PropertyDoc|MethodDoc|ConstDoc|EventDoc $subject
* @param string $title
* @return string
*/
public abstract function subjectLink($subject, $title = null);
}
\ No newline at end of file
The html API doc template
-------------------------
This templates provides view files and a Renderer class that can be reused in other html templates.
\ No newline at end of file
<?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(" &raquo;\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);
}
}
\ No newline at end of file
<?php
use yii\apidoc\helpers\Markdown;
use yii\apidoc\models\ClassDoc;
/**
* @var ClassDoc $type
* @var yii\web\View $this
*/
if (empty($type->constants)) {
return;
} ?>
<div class="summary docConst">
<h2>Constants</h2>
<p><a href="#" class="toggle">Hide inherited constants</a></p>
<table class="summaryTable">
<colgroup>
<col class="col-const" />
<col class="col-description" />
<col class="col-defined" />
</colgroup>
<tr>
<th>Constant</th><th>Value</th><th>Description</th><th>Defined By</th>
</tr>
<?php foreach($type->constants as $constant): ?>
<tr<?= $constant->definedBy != $type->name ? ' class="inherited"' : '' ?> id="<?= $constant->name ?>">
<td><?= $constant->name ?></td>
<td><?= $constant->value ?></td>
<td><?= Markdown::process($constant->shortDescription . "\n" . $constant->description, $type) ?></td>
<td><?= $this->context->typeLink($constant->definedBy) ?></td>
</tr>
<?php endforeach; ?>
</table>
</div>
\ No newline at end of file
<?php
use yii\apidoc\helpers\Markdown;
use yii\apidoc\models\ClassDoc;
/**
* @var ClassDoc $type
* @var yii\web\View $this
*/
$events = $type->getNativeEvents();
if (empty($events)) {
return;
} ?>
<h2>Event Details</h2>
<?php foreach($events as $event): ?>
<div class="detailHeader" id="<?= $event->name.'-detail' ?>">
<?php echo $event->name; ?>
<span class="detailHeaderTag">
event
<?php if(!empty($event->since)): ?>
(available since version <?= $event->since ?>)
<?php endif; ?>
</span>
</div>
<?php /*
<div class="signature">
<?php echo $event->trigger->signature; ?>
</div>*/ ?>
<p><?= Markdown::process($event->description, $type); ?></p>
<?= $this->render('seeAlso', ['object' => $event]); ?>
<?php endforeach; ?>
<?php
use yii\apidoc\helpers\Markdown;
use yii\apidoc\models\ClassDoc;
/**
* @var ClassDoc $type
* @var yii\web\View $this
*/
if (empty($type->events)) {
return;
} ?>
<div class="summary docEvent">
<h2>Events</h2>
<p><a href="#" class="toggle">Hide inherited events</a></p>
<table class="summaryTable">
<colgroup>
<col class="col-event" />
<col class="col-description" />
<col class="col-defined" />
</colgroup>
<tr>
<th>Event</th><th>Type</th><th>Description</th><th>Defined By</th>
</tr>
<?php foreach($type->events as $event): ?>
<tr<?= $event->definedBy != $type->name ? ' class="inherited"' : '' ?> id="<?= $event->name ?>">
<td><?= $this->context->subjectLink($event) ?></td>
<td><?= $this->context->typeLink($event->types) ?></td>
<td>
<?= Markdown::process($event->shortDescription, $type) ?>
<?php if(!empty($event->since)): ?>
(available since version <?php echo $event->since; ?>)
<?php endif; ?>
</td>
<td><?= $this->context->typeLink($event->definedBy) ?></td>
</tr>
<?php endforeach; ?>
</table>
</div>
\ No newline at end of file
<?php
use yii\apidoc\helpers\Markdown;
use yii\apidoc\models\ClassDoc;
use yii\apidoc\models\TraitDoc;
/**
* @var ClassDoc|TraitDoc $type
* @var yii\web\View $this
*/
$methods = $type->getNativeMethods();
if (empty($methods)) {
return;
} ?>
<h2>Method Details</h2>
<?php foreach($methods as $method): ?>
<div class="detailHeader" id="<?= $method->name . '()-detail' ?>">
<?= $method->name ?>()
<span class="detailHeaderTag">
method
<?php if (!empty($method->since)): ?>
(available since version <?php echo $method->since; ?>)
<?php endif; ?>
</span>
</div>
<table class="summaryTable">
<tr><td colspan="3">
<div class="signature2">
<?= $this->context->renderMethodSignature($method) ?>
</div>
</td></tr>
<?php if(!empty($method->params) || !empty($method->return) || !empty($method->exceptions)): ?>
<?php foreach($method->params as $param): ?>
<tr>
<td class="paramNameCol"><?= $param->name ?></td>
<td class="paramTypeCol"><?= $this->context->typeLink($param->types) ?></td>
<td class="paramDescCol"><?= Markdown::process($param->description, $type) ?></td>
</tr>
<?php endforeach; ?>
<?php if(!empty($method->return)): ?>
<tr>
<td class="paramNameCol"><?= 'return'; ?></td>
<td class="paramTypeCol"><?= $this->context->typeLink($method->returnTypes); ?></td>
<td class="paramDescCol"><?= Markdown::process($method->return, $type); ?></td>
</tr>
<?php endif; ?>
<?php foreach($method->exceptions as $exception => $description): ?>
<tr>
<td class="paramNameCol"><?= 'throws' ?></td>
<td class="paramTypeCol"><?= $this->context->typeLink($exception) ?></td>
<td class="paramDescCol"><?= Markdown::process($description, $type) ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</table>
<!-- --><?php //$this->renderPartial('sourceCode',array('object'=>$method)); ?>
<p><?= Markdown::process($method->shortDescription, $type) ?></strong></p>
<p><?= Markdown::process($method->description, $type) ?></p>
<?= $this->render('seeAlso', ['object' => $method]); ?>
<?php endforeach; ?>
\ No newline at end of file
<?php
use yii\apidoc\helpers\Markdown;
use yii\apidoc\models\ClassDoc;
use yii\apidoc\models\InterfaceDoc;
use yii\apidoc\models\TraitDoc;
/**
* @var ClassDoc|InterfaceDoc|TraitDoc $type
* @var boolean $protected
* @var yii\web\View $this
*/
if ($protected && count($type->getProtectedMethods()) == 0 || !$protected && count($type->getPublicMethods()) == 0) {
return;
} ?>
<div class="summary docMethod">
<h2><?= $protected ? 'Protected Methods' : 'Public Methods' ?></h2>
<p><a href="#" class="toggle">Hide inherited methods</a></p>
<table class="summaryTable">
<colgroup>
<col class="col-method" />
<col class="col-description" />
<col class="col-defined" />
</colgroup>
<tr>
<th>Method</th><th>Description</th><th>Defined By</th>
</tr>
<?php foreach($type->methods as $method): ?>
<?php if($protected && $method->visibility == 'protected' || !$protected && $method->visibility != 'protected'): ?>
<tr<?= $method->definedBy != $type->name ? ' class="inherited"' : '' ?> id="<?= $method->name ?>()">
<td><?= $this->context->subjectLink($method, $method->name.'()') ?></td>
<td><?= Markdown::process($method->shortDescription, $type) ?></td>
<td><?= $this->context->typeLink($method->definedBy, $type) ?></td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
</table>
</div>
\ No newline at end of file
<?php
use yii\apidoc\helpers\Markdown;
use yii\apidoc\models\ClassDoc;
use yii\apidoc\models\TraitDoc;
/**
* @var ClassDoc|TraitDoc $type
* @var yii\web\View $this
*/
$properties = $type->getNativeProperties();
if (empty($properties)) {
return;
} ?>
<h2>Property Details</h2>
<?php foreach($properties as $property): ?>
<div class="detailHeader" id="<?= $property->name.'-detail' ?>">
<?php echo $property->name; ?>
<span class="detailHeaderTag">
property
<?php if($property->getIsReadOnly()) echo ' <em>read-only</em> '; ?>
<?php if($property->getIsWriteOnly()) echo ' <em>write-only</em> '; ?>
<?php if(!empty($property->since)): ?>
(available since version <?php echo $property->since; ?>)
<?php endif; ?>
</span>
</div>
<div class="signature">
<?php echo $this->context->renderPropertySignature($property); ?>
</div>
<p><?= Markdown::process($property->description, $type) ?></p>
<?= $this->render('seeAlso', ['object' => $property]); ?>
<?php endforeach; ?>
<?php
use yii\apidoc\helpers\Markdown;
use yii\apidoc\models\ClassDoc;
use yii\apidoc\models\TraitDoc;
/**
* @var ClassDoc|TraitDoc $type
* @var boolean $protected
* @var yii\web\View $this
*/
if ($protected && count($type->getProtectedProperties()) == 0 || !$protected && count($type->getPublicProperties()) == 0) {
return;
} ?>
<div class="summary docProperty">
<h2><?= $protected ? 'Protected Properties' : 'Public Properties' ?></h2>
<p><a href="#" class="toggle">Hide inherited properties</a></p>
<table class="summaryTable">
<colgroup>
<col class="col-property" />
<col class="col-type" />
<col class="col-description" />
<col class="col-defined" />
</colgroup>
<tr>
<th>Property</th><th>Type</th><th>Description</th><th>Defined By</th>
</tr>
<?php foreach($type->properties as $property): ?>
<?php if($protected && $property->visibility == 'protected' || !$protected && $property->visibility != 'protected'): ?>
<tr<?= $property->definedBy != $type->name ? ' class="inherited"' : '' ?> id="<?= $property->name ?>">
<td><?= $this->context->subjectLink($property) ?></td>
<td><?= $this->context->typeLink($property->types) ?></td>
<td><?= Markdown::process($property->shortDescription, $type) ?></td>
<td><?= $this->context->typeLink($property->definedBy) ?></td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
</table>
</div>
\ No newline at end of file
<?php
/**
* @var yii\apidoc\models\BaseDoc $object
* @var yii\web\View $this
*/
$see = [];
foreach($object->tags as $tag) {
/** @var $tag phpDocumentor\Reflection\DocBlock\Tag\SeeTag */
if (get_class($tag) == 'phpDocumentor\Reflection\DocBlock\Tag\SeeTag') {
$ref = $tag->getReference();
if (strpos($ref, '://') === false) {
$see[] = '[[' . $ref . ']]';
} else {
$see[] = $ref;
}
}
}
if (empty($see)) {
return;
}
?>
<div class="SeeAlso">
<h4>See Also</h4>
<ul>
<?php foreach($see as $ref): ?>
<li><?= \yii\apidoc\helpers\Markdown::process($ref, $this->context->context->getType($object->definedBy)) ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php
use yii\apidoc\helpers\Markdown;
use yii\apidoc\models\ClassDoc;
use yii\apidoc\models\InterfaceDoc;
use yii\apidoc\models\TraitDoc;
/**
* @var ClassDoc|InterfaceDoc|TraitDoc $type
* @var yii\web\View $this
* @var \yii\apidoc\templates\html\Renderer $renderer
*/
$renderer = $this->context;
?>
<h1><?php
if ($type instanceof InterfaceDoc) {
echo 'Interface ';
} elseif ($type instanceof TraitDoc) {
echo 'Trait ';
} else {
if ($type->isFinal) {
echo 'Final ';
}
if ($type->isAbstract) {
echo 'Abstract ';
}
echo 'Class ';
}
echo $type->name;
?></h1>
<div id="nav">
<a href="index.html">All Classes</a>
<?php if(!($type instanceof InterfaceDoc) && !empty($type->properties)): ?>
| <a href="#properties">Properties</a>
<?php endif; ?>
<?php if(!empty($type->methods)): ?>
| <a href="#methods">Methods</a>
<?php endif; ?>
<?php if($type instanceof ClassDoc && !empty($type->events)): ?>
| <a href="#events">Events</a>
<?php endif; ?>
<?php if($type instanceof ClassDoc && !empty($type->constants)): ?>
| <a href="#constants">Constants</a>
<?php endif; ?>
</div>
<table class="summaryTable docClass">
<colgroup>
<col class="col-name" />
<col class="col-value" />
</colgroup>
<?php if ($type instanceof ClassDoc): ?>
<tr><th>Inheritance</th><td><?= $renderer->renderInheritance($type) ?></td></tr>
<?php endif; ?>
<?php if ($type instanceof ClassDoc && !empty($type->interfaces)): ?>
<tr><th>Implements</th><td><?= $renderer->renderInterfaces($type->interfaces) ?></td></tr>
<?php endif; ?>
<?php if(!($type instanceof InterfaceDoc) && !empty($type->traits)): ?>
<tr><th>Uses Traits</th><td><?= $renderer->renderTraits($type->traits) ?></td></tr>
<?php endif; ?>
<?php if($type instanceof ClassDoc && !empty($type->subclasses)): ?>
<tr><th>Subclasses</th><td><?= $renderer->renderClasses($type->subclasses) ?></td></tr>
<?php endif; ?>
<?php if ($type instanceof InterfaceDoc && !empty($type->implementedBy)): ?>
<tr><th>Implemented by</th><td><?= $renderer->renderClasses($type->implementedBy) ?></td></tr>
<?php endif; ?>
<?php if ($type instanceof TraitDoc && !empty($type->usedBy)): ?>
<tr><th>Implemented by</th><td><?= $renderer->renderClasses($type->usedBy) ?></td></tr>
<?php endif; ?>
<?php if(!empty($type->since)): ?>
<tr><th>Available since version</th><td><?= $type->since ?></td></tr>
<?php endif; ?>
<tr>
<th>Source Code</th>
<td><?php // TODO echo $this->renderSourceLink($type->sourcePath) ?></td>
</tr>
</table>
<div id="classDescription">
<strong><?= Markdown::process($type->shortDescription, $type) ?></strong>
<p><?= Markdown::process($type->description, $type) ?></p>
</div>
<a name="properties"></a>
<?= $this->render('@yii/apidoc/templates/html/views/propertySummary', ['type' => $type,'protected' => false]) ?>
<?= $this->render('@yii/apidoc/templates/html/views/propertySummary', ['type' => $type,'protected' => true]) ?>
<a name="methods"></a>
<?= $this->render('@yii/apidoc/templates/html/views/methodSummary', ['type' => $type, 'protected' => false]) ?>
<?= $this->render('@yii/apidoc/templates/html/views/methodSummary', ['type' => $type, 'protected' => true]) ?>
<a name="events"></a>
<?= $this->render('@yii/apidoc/templates/html/views/eventSummary', ['type' => $type]) ?>
<a name="constants"></a>
<?= $this->render('@yii/apidoc/templates/html/views/constSummary', ['type' => $type]) ?>
<?= $this->render('@yii/apidoc/templates/html/views/propertyDetails', ['type' => $type]) ?>
<?= $this->render('@yii/apidoc/templates/html/views/methodDetails', ['type' => $type]) ?>
<?php if($type instanceof ClassDoc): ?>
<?= $this->render('@yii/apidoc/templates/html/views/eventDetails', ['type' => $type]) ?>
<?php endif; ?>
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\templates\offline;
use yii\apidoc\models\Context;
use yii\console\Controller;
use Yii;
use yii\helpers\Console;
use yii\helpers\FileHelper;
/**
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class Renderer extends \yii\apidoc\templates\html\Renderer
{
public $layout = '@yii/apidoc/templates/offline/views/offline.php';
public $indexView = '@yii/apidoc/templates/offline/views/index.php';
public $pageTitle = 'Yii Framework 2.0 API Documentation';
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\templates\offline\assets;
use yii\web\JqueryAsset;
use yii\web\View;
/**
* The asset bundle for the offline template.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class AssetBundle extends \yii\web\AssetBundle
{
public $sourcePath = '@yii/apidoc/templates/offline/assets/css';
public $css = [
'api.css',
'style.css',
];
public $depends = [
'yii\web\JqueryAsset',
];
public $jsOptions = [
'position' => View::POS_HEAD,
];
}
pre {
color: #000000;
background-color: #FFF5E6;
font-family: "courier new", "times new roman", monospace;
line-height: 1.3em;
/* Put a nice border around it. */
padding: 1px;
width: 90%;
/* Don't wrap its contents, and show scrollbars. */
/* white-space: nowrap;*/
overflow: auto;
/* Stop after about 24 lines, and just show a scrollbar. */
/* max-height: 24em; */
margin: 5px;
padding-left: 20px;
border: 1px solid #FFE6BF;
border-left: 6px solid #FFE6BF;
}
code {
color: #000000;
background-color: #FFF5E6;
padding: 1px;
}
div.code {
display: none;
color: #000000;
background-color: #FFF5E6;
font-family: "courier new", "times new roman", monospace;
line-height: 1.3em;
/* Put a nice border around it. */
padding: 1px;
width: 90%;
/* Don't wrap its contents, and show scrollbars. */
/* white-space: nowrap;*/
overflow: auto;
/* Stop after about 24 lines, and just show a scrollbar. */
/* max-height: 24em; */
margin: 5px;
padding-left: 20px;
border-left: 6px solid #FFE6BF;
}
table.summaryTable {
background: #E6ECFF;
border-collapse: collapse;
width: 100%;
}
table.summaryTable th, table.summaryTable td {
border: 1px #BFCFFF solid;
padding: 0.2em;
}
table.summaryTable th {
background: #CCD9FF;
text-align: left;
}
#nav {
padding: 3px;
margin: 0 0 10px 0;
border-top: 1px #BFCFFF solid;
}
#classDescription {
padding: 5px;
margin: 10px 0 20px 0;
border-bottom: 1px solid #BFCFFF;
}
.detailHeader {
font-weight: bold;
font-size: 12pt;
margin: 30px 0 5px 0;
border-bottom: 1px solid #BFCFFF;
}
.detailHeaderTag {
font-weight: normal;
font-size: 10pt;
}
.signature, .signature2 {
padding: 3px;
color: #000000;
font-family: "courier new", "times new roman", monospace;
line-height: 1.3em;
}
.signature {
margin: 10px 0 10px 0;
background: #E6ECFF;
border: 1px #BFCFFF solid;
}
.paramNameCol {
width: 12%;
font-weight: bold;
}
.paramTypeCol {
width: 12%;
}
.sourceCode {
margin: 5px 0;
padding:5px;
background:#FFF5E6;
}
\ No newline at end of file
body
{
}
body, div, span, p, input
{
font-family: Verdana, Arial, sans-serif;
font-size: 10pt;
color: #333333;
}
#apiPage {
}
#apiHeader {
padding: 3px;
color: white;
background: #6078BF;
margin-bottom: 5px;
font-weight: bold;
}
#apiHeader a {
color: white;
}
#apiFooter {
margin-top: 5px;
padding: 3px;
border-top: 1px solid #BFCFFF;
text-align: center;
}
<?php
use yii\apidoc\models\ClassDoc;
use yii\apidoc\models\InterfaceDoc;
use yii\apidoc\models\TraitDoc;
/**
* @var ClassDoc[]|InterfaceDoc[]|TraitDoc[] $types
* @var yii\web\View $this
*/
?><h1>Class Reference</h1>
<table class="summaryTable docIndex">
<colgroup>
<col class="col-package" />
<col class="col-class" />
<col class="col-description" />
</colgroup>
<tr>
<th>Class</th><th>Description</th>
</tr>
<?php
ksort($types);
foreach($types as $i=>$class): ?>
<tr>
<td><?php echo $this->context->typeLink($class, $class->name); ?></td>
<td><?php echo $class->shortDescription; ?></td>
</tr>
<?php endforeach; ?>
</table>
<?php
/**
* @var yii\web\View $this
*/
\yii\apidoc\templates\offline\assets\AssetBundle::register($this);
$this->beginPage();
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="language" content="en" />
<?php $this->head(); ?>
<title><?php echo $this->context->pageTitle; ?></title>
</head>
<body>
<?php $this->beginBody(); ?>
<div id="apiPage">
<div id="apiHeader">
<a href="http://www.yiiframework.com">Yii Framework</a> v<?php echo Yii::getVersion(); ?> Class Reference
</div><!-- end of header -->
<div id="content">
<?php echo $content; ?>
</div><!-- end of content -->
<div id="apiFooter">
&copy; 2008-2013 by <a href="http://www.yiisoft.com">Yii Software LLC</a><br/>
All Rights Reserved.<br/>
</div><!-- end of footer -->
<script type="text/javascript">
/*<![CDATA[*/
$("a.toggle").on('click', function() {
var $this = $(this);
if ($this.hasClass('properties-hidden')) {
$this.text($this.text().replace(/Show/,'Hide'));
$this.parents(".summary").find(".inherited").show();
$this.removeClass('properties-hidden');
} else {
$this.text($this.text().replace(/Hide/,'Show'));
$this.parents(".summary").find(".inherited").hide();
$this.addClass('properties-hidden');
}
return false;
});
/*
$(".sourceCode a.show").toggle(function(){
$(this).text($(this).text().replace(/show/,'hide'));
$(this).parents(".sourceCode").find("div.code").show();
},function(){
$(this).text($(this).text().replace(/hide/,'show'));
$(this).parents(".sourceCode").find("div.code").hide();
});
$("a.sourceLink").click(function(){
$(this).attr('target','_blank');
});
*/
/*]]>*/
</script>
</div><!-- end of page -->
<?php $this->endBody(); ?>
</body>
</html>
<?php $this->endPage(); ?>
\ No newline at end of file
......@@ -34,7 +34,7 @@ class Exception extends \Exception implements Arrayable
/**
* Returns the array representation of the exception and all previous exceptions recursively.
* @param \Exception exception object
* @param \Exception $exception object
* @return array the array representation of the exception.
*/
protected function toArrayRecursive($exception)
......
......@@ -138,7 +138,6 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
* @param array $attributes attribute values (name-value pairs) to be saved into the table
* @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.
* Please refer to [[Query::where()]] on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
* @return integer the number of rows updated
*/
public static function updateAll($attributes, $condition = '')
......@@ -379,7 +378,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
* Populates the named relation with the related records.
* Note that this method does not check if the relation exists or not.
* @param string $name the relation name (case-sensitive)
* @param ActiveRecord|array|null the related records to be populated into the relation.
* @param ActiveRecord|array|null $records the related records to be populated into the relation.
*/
public function populateRelation($name, $records)
{
......
......@@ -1471,7 +1471,7 @@ class BaseHtml
return Yii::$app->getRequest()->getUrl();
} else {
$url = Yii::getAlias($url);
if ($url !== '' && ($url[0] === '/' || $url[0] === '#' || strpos($url, '://'))) {
if ($url !== '' && ($url[0] === '/' || $url[0] === '#' || strpos($url, '://') || !strncmp($url, './', 2))) {
return $url;
} else {
return Yii::$app->getRequest()->getBaseUrl() . '/' . $url;
......
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