diff --git a/README.md b/README.md index d7020d9..e6ba00d 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,9 @@ DOCUMENTATION A draft of the [Definitive Guide](docs/guide/index.md) is available. +API docs and a (quite bad) rendering of the definitive guide are currently +available at http://stuff.cebe.cc/yii2docs/ (updated four times per hour). + For 1.1 users, you may refer to [Upgrading from Yii 1.1](docs/guide/upgrade-from-v1.md) to have a general idea of what has changed in 2.0. diff --git a/extensions/apidoc/commands/RenderController.php b/extensions/apidoc/commands/RenderController.php index d6ae80f..3c676e1 100644 --- a/extensions/apidoc/commands/RenderController.php +++ b/extensions/apidoc/commands/RenderController.php @@ -128,7 +128,7 @@ class RenderController extends Controller return new $rendererClass(); } - protected function findFiles($path, $except = ['/vendor/', '/tests/']) + protected function findFiles($path, $except = ['vendor/', 'tests/']) { $path = FileHelper::normalizePath($path); $options = [ diff --git a/extensions/apidoc/models/Context.php b/extensions/apidoc/models/Context.php index 5a9e57f..168bc01 100644 --- a/extensions/apidoc/models/Context.php +++ b/extensions/apidoc/models/Context.php @@ -162,6 +162,9 @@ class Context extends Component return; } foreach($class->getPublicMethods() as $name => $method) { + if ($method->isStatic) { + continue; + } if (!strncmp($name, 'get', 3) && $this->paramsOptional($method)) { $propertyName = '$' . lcfirst(substr($method->name, 3)); if (isset($class->properties[$propertyName])) { diff --git a/extensions/apidoc/templates/bootstrap/Renderer.php b/extensions/apidoc/templates/bootstrap/Renderer.php index b40426d..73dcb27 100644 --- a/extensions/apidoc/templates/bootstrap/Renderer.php +++ b/extensions/apidoc/templates/bootstrap/Renderer.php @@ -28,6 +28,110 @@ class Renderer extends \yii\apidoc\templates\html\Renderer public $guideUrl; + public $extensions = [ + 'apidoc', + 'authclient', + 'bootstrap', + 'codeception', + 'composer', + 'debug', + 'elasticsearch', + 'faker', + 'gii', + 'imagine', + 'jui', + 'mongodb', + 'redis', + 'smarty', + 'sphinx', + 'swiftmailer', + 'twig', + ]; + + /** + * 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 renderApi($context, $controller) + { + parent::renderApi($context, $controller); + $dir = Yii::getAlias($this->targetDir); + $types = array_merge($context->classes, $context->interfaces, $context->traits); + + $controller->stdout('generating extension index files...'); + foreach($this->extensions as $ext) { + $readme = @file_get_contents("https://raw.github.com/yiisoft/yii2-$ext/master/README.md"); + $indexFileContent = $this->renderWithLayout($this->indexView, [ + 'docContext' => $context, + 'types' => $this->filterTypes($types, $ext), + 'readme' => $readme ?: null, + ]); + file_put_contents($dir . "/ext_{$ext}_index.html", $indexFileContent); + } + $readme = @file_get_contents("https://raw.github.com/yiisoft/yii2-framework/master/README.md"); + $indexFileContent = $this->renderWithLayout($this->indexView, [ + 'docContext' => $context, + 'types' => $this->filterTypes($types, 'yii'), + 'readme' => $readme ?: null, + ]); + file_put_contents($dir . '/index.html', $indexFileContent); + $controller->stdout('done.' . PHP_EOL, Console::FG_GREEN); + } + + public function getNavTypes($type, $types) + { + if ($type === null) { + return $types; + } + $extensions = $this->extensions; + $navClasses = 'app'; + if (isset($type)) { + if ($type->name == 'Yii') { + $navClasses = 'yii'; + } elseif (strncmp($type->name, 'yii\\', 4) == 0) { + $subName = substr($type->name, 4); + if (($pos = strpos($subName, '\\')) !== false) { + $subNamespace = substr($subName, 0, $pos); + if (in_array($subNamespace, $extensions)) { + $navClasses = $subNamespace; + } else { + $navClasses = 'yii'; + } + } + } + } + return $this->filterTypes($types, $navClasses); + } + + protected function filterTypes($types, $navClasses) + { + switch ($navClasses) + { + case 'app': + $types = array_filter($types, function($val) { + return strncmp($val->name, 'yii\\', 4) !== 0; + }); + break; + case 'yii': + $self = $this; + $types = array_filter($types, function($val) use ($self) { + if (strlen($val->name) < 5) { + return false; + } + $subName = substr($val->name, 4, strpos($val->name, '\\', 5) - 4); + return strncmp($val->name, 'yii\\', 4) === 0 && !in_array($subName, $self->extensions); + }); + break; + default: + $types = array_filter($types, function($val) use ($navClasses) { + return strncmp($val->name, "yii\\$navClasses\\", strlen("yii\\$navClasses\\")) === 0; + }); + } + return $types; + } + /** * Renders a given [[Context]]. * diff --git a/extensions/apidoc/templates/bootstrap/layouts/api.php b/extensions/apidoc/templates/bootstrap/layouts/api.php index 157e7fd..935705d 100644 --- a/extensions/apidoc/templates/bootstrap/layouts/api.php +++ b/extensions/apidoc/templates/bootstrap/layouts/api.php @@ -11,6 +11,7 @@ $this->beginContent('@yii/apidoc/templates/bootstrap/layouts/main.php'); ?> <div class="row"> <div class="col-md-2"> <?php + $types = $this->context->getNavTypes(isset($type) ? $type : null, $types); ksort($types); $nav = []; foreach($types as $i=>$class) { diff --git a/extensions/apidoc/templates/bootstrap/layouts/main.php b/extensions/apidoc/templates/bootstrap/layouts/main.php index 46b96c1..90708ba 100644 --- a/extensions/apidoc/templates/bootstrap/layouts/main.php +++ b/extensions/apidoc/templates/bootstrap/layouts/main.php @@ -37,9 +37,17 @@ $this->beginPage(); 'padded' => false, 'view' => $this, ]); + $extItems = []; + foreach($this->context->extensions as $ext) { + $extItems[] = [ + 'label' => $ext, + 'url' => "./ext_{$ext}_index.html", + ]; + } $nav = [ ['label' => 'Class reference', 'url' => './index.html'], // ['label' => 'Application API', 'url' => '/site/about'], + ['label' => 'Extensions', 'items' => $extItems], ]; if ($this->context->guideUrl !== null) { $nav[] = ['label' => 'Guide', 'url' => $this->context->guideUrl . 'guide_index.html']; @@ -60,7 +68,8 @@ $this->beginPage(); <footer class="footer"> <?php /* <p class="pull-left">© My Company <?= date('Y') ?></p> */ ?> - <p class="pull-right"><?= Yii::powered() ?></p> + <p class="pull-right"><small>Page generated on <?= date('r') ?></small></p> + <?= Yii::powered() ?> </footer> <?php $this->endBody() ?> diff --git a/extensions/apidoc/templates/bootstrap/views/index.php b/extensions/apidoc/templates/bootstrap/views/index.php index a8b29fc..b971b8e 100644 --- a/extensions/apidoc/templates/bootstrap/views/index.php +++ b/extensions/apidoc/templates/bootstrap/views/index.php @@ -8,6 +8,10 @@ use yii\apidoc\models\TraitDoc; * @var yii\web\View $this */ +if (isset($readme)) { + echo \yii\apidoc\helpers\Markdown::process($readme); +} + ?><h1>Class Reference</h1> <table class="summaryTable docIndex table table-bordered table-striped table-hover"> diff --git a/framework/README.md b/framework/README.md index 2e39742..22af4a4 100644 --- a/framework/README.md +++ b/framework/README.md @@ -7,7 +7,7 @@ This is the core framework code of [Yii 2](https://github.com/yiisoft/yii2). Installation ------------ -The preferred way to install this extension is through [composer](http://getcomposer.org/download/). +The preferred way to install the Yii framework is through [composer](http://getcomposer.org/download/). Either run diff --git a/framework/web/Response.php b/framework/web/Response.php index 0068afd..f84d2a0 100644 --- a/framework/web/Response.php +++ b/framework/web/Response.php @@ -870,9 +870,13 @@ class Response extends \yii\base\Response } if (is_array($this->content)) { - $this->content = 'array()'; + throw new InvalidParamException("Response content must not be an array."); } elseif (is_object($this->content)) { - $this->content = method_exists($this->content, '__toString') ? $this->content->__toString() : get_class($this->content); + if (method_exists($this->content, '__toString')) { + $this->content = $this->content->__toString(); + } else { + throw new InvalidParamException("Response content can only be an object when it implements __toString() method."); + } } } }