From 7aa586d38abb46aef98f5820af760f66b078dc52 Mon Sep 17 00:00:00 2001 From: Bizley Date: Mon, 30 Aug 2021 15:20:46 +0200 Subject: [PATCH 01/58] Draft for 3.0 --- .gitignore | 4 +- commands/ApiController.php | 3 +- composer.json | 31 +++------- models/BaseDoc.php | 27 ++++++--- models/ClassDoc.php | 4 +- models/Context.php | 77 +++++++++++++++++++----- models/FunctionDoc.php | 15 +++-- models/PropertyDoc.php | 13 ++-- models/TypeDoc.php | 4 +- renderers/BaseRenderer.php | 31 ++++------ templates/bootstrap/RendererTrait.php | 2 +- templates/html/views/methodSummary.php | 4 +- templates/html/views/propertySummary.php | 2 +- tests/TestCase.php | 6 +- tests/commands/ApiControllerTest.php | 18 +++--- tests/commands/GuideControllerTest.php | 18 +++--- 16 files changed, 155 insertions(+), 104 deletions(-) diff --git a/.gitignore b/.gitignore index 803a6f0d..f2cdbfd5 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,6 @@ phpunit.phar /tests/runtime # local tests configuration -/tests/data/config.local.php \ No newline at end of file +/tests/data/config.local.php + +.phpunit.result.cache diff --git a/commands/ApiController.php b/commands/ApiController.php index e2484385..d1e4259b 100644 --- a/commands/ApiController.php +++ b/commands/ApiController.php @@ -97,7 +97,7 @@ public function actionIndex(array $sourceDirs, $targetDir) $done = 0; foreach ($files as $file) { try { - $context->addFile($file); + $context->addProjectFile($file); } catch (\Exception $e) { $context->errors[] = "Unable to process \"$file\": " . $e->getMessage(); } @@ -105,6 +105,7 @@ public function actionIndex(array $sourceDirs, $targetDir) } Console::endProgress(true); $this->stdout('done.' . PHP_EOL, Console::FG_GREEN); + $context->processFiles(); // save processed data to cache $this->storeContext($context, $targetDir); diff --git a/composer.json b/composer.json index 9ba468fc..5d0a74b1 100644 --- a/composer.json +++ b/composer.json @@ -19,20 +19,19 @@ ], "minimum-stability": "dev", "require": { - "php": ">=5.4", - "yiisoft/yii2": "~2.0.13", + "php": "^7.2", + "yiisoft/yii2": "~2.0.16", "yiisoft/yii2-bootstrap": "~2.0.0", - "phpdocumentor/reflection": "^3.0.1", - "phpdocumentor/reflection-docblock": "^2.0.4", - "nikic/php-parser": "^1.0", - "cebe/js-search": "~0.9.3", - "cebe/markdown": "~1.0.0 | ~1.1.0 | ~1.2.0", - "cebe/markdown-latex": "~1.0", - "scrivo/highlight.php": "~9.13" + "phpdocumentor/reflection": "^4.0 | ^5.0", + "phpdocumentor/reflection-docblock": "^4.0 | ^5.0", + "nikic/php-parser": "^4.0", + "cebe/js-search": "~0.9.0", + "cebe/markdown": "^1.0", + "cebe/markdown-latex": "^1.0", + "scrivo/highlight.php": "^9.0" }, "require-dev": { - "cweagans/composer-patches": "^1.7", - "phpunit/phpunit": "4.8.34" + "phpunit/phpunit": "*" }, "repositories": [ { @@ -47,16 +46,6 @@ "extra": { "branch-alias": { "dev-master": "2.1.x-dev" - }, - "composer-exit-on-patch-failure": true, - "patches": { - "phpunit/phpunit-mock-objects": { - "Fix PHP 7 and 8 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_mock_objects.patch" - }, - "phpunit/phpunit": { - "Fix PHP 7 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_php7.patch", - "Fix PHP 8 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_php8.patch" - } } } } diff --git a/models/BaseDoc.php b/models/BaseDoc.php index 3f4d94fe..5ed7a2a5 100644 --- a/models/BaseDoc.php +++ b/models/BaseDoc.php @@ -10,6 +10,10 @@ use phpDocumentor\Reflection\DocBlock\Tag; use phpDocumentor\Reflection\DocBlock\Tag\DeprecatedTag; use phpDocumentor\Reflection\DocBlock\Tag\SinceTag; +use phpDocumentor\Reflection\DocBlock\Tags\Deprecated; +use phpDocumentor\Reflection\DocBlock\Tags\Generic; +use phpDocumentor\Reflection\DocBlock\Tags\Since; +use phpDocumentor\Reflection\Php\Class_; use yii\base\BaseObject; use yii\helpers\StringHelper; @@ -26,6 +30,7 @@ class BaseDoc extends BaseObject */ public $phpDocContext; public $name; + public $shortName; public $sourceFile; public $startLine; public $endLine; @@ -101,7 +106,7 @@ public function getPackageName() } /** - * @param \phpDocumentor\Reflection\BaseReflector $reflector + * @param Class_ $reflector * @param Context $context * @param array $config */ @@ -114,13 +119,17 @@ public function __construct($reflector = null, $context = null, $config = []) } // base properties - $this->name = ltrim($reflector->getName(), '\\'); - $this->startLine = $reflector->getNode()->getAttribute('startLine'); - $this->endLine = $reflector->getNode()->getAttribute('endLine'); + $this->name = ltrim((string) $reflector->getFqsen(), '\\'); + $separator = strrpos($this->name, '::'); + if ($separator !== false) { + $this->shortName = substr($this->name, $separator + 2); + } + $this->startLine = $reflector->getLocation()->getLineNumber(); + //$this->endLine = $reflector->getNode()->getAttribute('endLine'); $docblock = $reflector->getDocBlock(); if ($docblock !== null) { - $this->shortDescription = static::mbUcFirst($docblock->getShortDescription()); + $this->shortDescription = StringHelper::mb_ucfirst($docblock->getSummary()); if (empty($this->shortDescription) && !($this instanceof PropertyDoc) && $context !== null && $docblock->getTagsByName('inheritdoc') === null) { $context->warnings[] = [ 'line' => $this->startLine, @@ -128,16 +137,16 @@ public function __construct($reflector = null, $context = null, $config = []) 'message' => "No short description for " . substr(StringHelper::basename(get_class($this)), 0, -3) . " '{$this->name}'", ]; } - $this->description = $docblock->getLongDescription()->getContents(); + $this->description = $docblock->getDescription()->render(); $this->phpDocContext = $docblock->getContext(); $this->tags = $docblock->getTags(); foreach ($this->tags as $i => $tag) { - if ($tag instanceof SinceTag) { + if ($tag instanceof Since) { $this->since = $tag->getVersion(); unset($this->tags[$i]); - } elseif ($tag instanceof DeprecatedTag) { + } elseif ($tag instanceof Deprecated) { $this->deprecatedSince = $tag->getVersion(); $this->deprecatedReason = $tag->getDescription(); unset($this->tags[$i]); @@ -147,7 +156,7 @@ public function __construct($reflector = null, $context = null, $config = []) if ($this->shortDescription === '{@inheritdoc}') { // Mock up parsing of '{@inheritdoc}' (in brackets) tag, which is not yet supported at "phpdocumentor/reflection-docblock" 2.x // todo consider removal in case of "phpdocumentor/reflection-docblock" upgrade - $this->tags[] = new Tag('inheritdoc', ''); + $this->tags[] = new Generic('inheritdoc'); $this->shortDescription = ''; } diff --git a/models/ClassDoc.php b/models/ClassDoc.php index 8e9a12bf..813b1ce7 100644 --- a/models/ClassDoc.php +++ b/models/ClassDoc.php @@ -101,7 +101,7 @@ public function __construct($reflector = null, $context = null, $config = []) return; } - $this->parentClass = ltrim($reflector->getParentClass(), '\\'); + $this->parentClass = ltrim($reflector->getParent(), '\\'); if (empty($this->parentClass)) { $this->parentClass = null; } @@ -111,7 +111,7 @@ public function __construct($reflector = null, $context = null, $config = []) foreach ($reflector->getInterfaces() as $interface) { $this->interfaces[] = ltrim($interface, '\\'); } - foreach ($reflector->getTraits() as $trait) { + foreach ($reflector->getUsedTraits() as $trait) { $this->traits[] = ltrim($trait, '\\'); } foreach ($reflector->getConstants() as $constantReflector) { diff --git a/models/Context.php b/models/Context.php index 24dea163..3f201640 100644 --- a/models/Context.php +++ b/models/Context.php @@ -5,15 +5,18 @@ * @license http://www.yiiframework.com/license/ */ +declare(strict_types=1); + namespace yii\apidoc\models; -use phpDocumentor\Reflection\FileReflector; +use phpDocumentor\Reflection\File\LocalFile; +use phpDocumentor\Reflection\Php\Project; +use phpDocumentor\Reflection\Php\ProjectFactory; use yii\base\Component; /** - * * @author Carsten Brandt - * @since 2.0 + * @author Paweł Brzozowski */ class Context extends Component { @@ -39,7 +42,6 @@ class Context extends Component public $errors = []; /** * @var array - * @since 2.0.6 */ public $warnings = []; @@ -49,29 +51,58 @@ class Context extends Component * @param string $type * @return null|ClassDoc|InterfaceDoc|TraitDoc */ - public function getType($type) + public function getType(string $type) { $type = ltrim($type, '\\'); + if (isset($this->classes[$type])) { return $this->classes[$type]; - } elseif (isset($this->interfaces[$type])) { + } + if (isset($this->interfaces[$type])) { return $this->interfaces[$type]; - } elseif (isset($this->traits[$type])) { + } + if (isset($this->traits[$type])) { return $this->traits[$type]; } return null; } + public function addProjectFile($fileName): void + { + $this->files[$fileName] = sha1_file($fileName); + } + + public function processFiles(): void + { + $projectFiles = $this->getReflectionProject()->getFiles(); + foreach ($this->files as $fileName => $hash) { + $reflection = $projectFiles[$fileName]; + + foreach ($reflection->getClasses() as $class) { + $class = new ClassDoc($class, $this, ['sourceFile' => $fileName]); + $this->classes[$class->name] = $class; + } + foreach ($reflection->getInterfaces() as $interface) { + $interface = new InterfaceDoc($interface, $this, ['sourceFile' => $fileName]); + $this->interfaces[$interface->name] = $interface; + } + foreach ($reflection->getTraits() as $trait) { + $trait = new TraitDoc($trait, $this, ['sourceFile' => $fileName]); + $this->traits[$trait->name] = $trait; + } + } + } + /** * Adds file to context * @param string $fileName */ - public function addFile($fileName) + public function addFile(string $fileName): void { $this->files[$fileName] = sha1_file($fileName); - $reflection = new FileReflector($fileName, true); + $reflection = new \phpDocumentor\Reflection\File\LocalFile($fileName, true); $reflection->process(); foreach ($reflection->getClasses() as $class) { @@ -91,7 +122,7 @@ public function addFile($fileName) /** * Updates references */ - public function updateReferences() + public function updateReferences(): void { // update all subclass references foreach ($this->classes as $class) { @@ -101,10 +132,12 @@ public function updateReferences() $class->subclasses[] = $className; } } + // update interfaces of subclasses foreach ($this->classes as $class) { $this->updateSubclassInterfacesTraits($class); } + // update implementedBy and usedBy for interfaces and traits foreach ($this->classes as $class) { foreach ($class->traits as $trait) { @@ -306,7 +339,7 @@ private function inheritMethodRecursive($method, $class) foreach($inheritanceCandidates as $candidate) { if (isset($candidate->methods[$method->name])) { $cmethod = $candidate->methods[$method->name]; - if ($cmethod->hasTag('inheritdoc')) { + if (!$candidate instanceof InterfaceDoc && $cmethod->hasTag('inheritdoc')) { $this->inheritDocs($candidate); } $methods[] = $cmethod; @@ -458,7 +491,7 @@ private function hasNonOptionalParams($method, $number = 0) $count++; } } - return $count == $number; + return $count === $number; } /** @@ -485,15 +518,31 @@ protected function isSubclassOf($classA, $classB) if (is_object($classB)) { $classB = $classB->name; } - if ($classA->name == $classB) { + if ($classA->name === $classB) { return true; } while ($classA->parentClass !== null && isset($this->classes[$classA->parentClass])) { $classA = $this->classes[$classA->parentClass]; - if ($classA->name == $classB) { + if ($classA->name === $classB) { return true; } } return false; } + + private $reflectionProject; + + public function getReflectionProject(): Project + { + if ($this->reflectionProject === null) { + $files = []; + foreach ($this->files as $fileName => $hash) { + $files[] = new LocalFile($fileName); + } + + $this->reflectionProject = ProjectFactory::createInstance()->create('ApiDoc', $files); + } + + return $this->reflectionProject; + } } diff --git a/models/FunctionDoc.php b/models/FunctionDoc.php index 728f4e25..0242ae8c 100644 --- a/models/FunctionDoc.php +++ b/models/FunctionDoc.php @@ -11,6 +11,11 @@ use phpDocumentor\Reflection\DocBlock\Tag\PropertyTag; use phpDocumentor\Reflection\DocBlock\Tag\ReturnTag; use phpDocumentor\Reflection\DocBlock\Tag\ThrowsTag; +use phpDocumentor\Reflection\DocBlock\Tags\Param; +use phpDocumentor\Reflection\DocBlock\Tags\Throws; +use phpDocumentor\Reflection\Php\Function_; +use phpDocumentor\Reflection\Php\Method; +use phpDocumentor\Reflection\Php\Property; /** * Represents API documentation information for a `function`. @@ -32,7 +37,7 @@ class FunctionDoc extends BaseDoc /** - * @param \phpDocumentor\Reflection\FunctionReflector $reflector + * @param Method $reflector * @param Context $context * @param array $config */ @@ -44,7 +49,7 @@ public function __construct($reflector = null, $context = null, $config = []) return; } - $this->isReturnByReference = $reflector->isByRef(); + $this->isReturnByReference = false;//$reflector->isByRef(); foreach ($reflector->getArguments() as $arg) { $arg = new ParamDoc($arg, $context, ['sourceFile' => $this->sourceFile]); @@ -52,12 +57,12 @@ public function __construct($reflector = null, $context = null, $config = []) } foreach ($this->tags as $i => $tag) { - if ($tag instanceof ThrowsTag) { + if ($tag instanceof Throws) { $this->exceptions[$tag->getType()] = $tag->getDescription(); unset($this->tags[$i]); - } elseif ($tag instanceof PropertyTag) { + } elseif ($tag instanceof Property) { // ignore property tag - } elseif ($tag instanceof ParamTag) { + } elseif ($tag instanceof Param) { $paramName = $tag->getVariableName(); if (!isset($this->params[$paramName]) && $context !== null) { $context->errors[] = [ diff --git a/models/PropertyDoc.php b/models/PropertyDoc.php index 4a61c01b..8552fa28 100644 --- a/models/PropertyDoc.php +++ b/models/PropertyDoc.php @@ -7,7 +7,8 @@ namespace yii\apidoc\models; -use phpDocumentor\Reflection\DocBlock\Tag\VarTag; +use phpDocumentor\Reflection\DocBlock\Tags\Var_; +use phpDocumentor\Reflection\Php\Property; use yii\apidoc\helpers\PrettyPrinter; /** @@ -50,7 +51,7 @@ public function getIsWriteOnly() } /** - * @param \phpDocumentor\Reflection\ClassReflector\PropertyReflector $reflector + * @param Property $reflector * @param Context $context * @param array $config */ @@ -66,8 +67,8 @@ public function __construct($reflector = null, $context = null, $config = []) $this->isStatic = $reflector->isStatic(); // bypass $reflector->getDefault() for short array syntax - if ($reflector->getNode()->default) { - $this->defaultValue = PrettyPrinter::getRepresentationOfValue($reflector->getNode()->default); + if ($reflector->getDefault()) { + $this->defaultValue = $reflector->getDefault(); //PrettyPrinter::getRepresentationOfValue($reflector->getDefault()); } $hasInheritdoc = false; @@ -75,9 +76,9 @@ public function __construct($reflector = null, $context = null, $config = []) if ($tag->getName() === 'inheritdoc') { $hasInheritdoc = true; } - if ($tag instanceof VarTag) { + if ($tag instanceof Var_) { $this->type = $tag->getType(); - $this->types = $tag->getTypes(); + $this->types = [$tag->getType()]; $this->description = static::mbUcFirst($tag->getDescription()); $this->shortDescription = BaseDoc::extractFirstSentence($this->description); } diff --git a/models/TypeDoc.php b/models/TypeDoc.php index 97e283ba..a69fc03a 100644 --- a/models/TypeDoc.php +++ b/models/TypeDoc.php @@ -250,7 +250,7 @@ public function __construct($reflector = null, $context = null, $config = []) } foreach ($reflector->getProperties() as $propertyReflector) { - if ($propertyReflector->getVisibility() != 'private') { + if ($propertyReflector->getVisibility() !== 'private') { $property = new PropertyDoc($propertyReflector, $context, ['sourceFile' => $this->sourceFile]); $property->definedBy = $this->name; $this->properties[$property->name] = $property; @@ -258,7 +258,7 @@ public function __construct($reflector = null, $context = null, $config = []) } foreach ($reflector->getMethods() as $methodReflector) { - if ($methodReflector->getVisibility() != 'private') { + if ($methodReflector->getVisibility() !== 'private') { $method = new MethodDoc($methodReflector, $context, ['sourceFile' => $this->sourceFile]); $method->definedBy = $this->name; $this->methods[$method->name] = $method; diff --git a/renderers/BaseRenderer.php b/renderers/BaseRenderer.php index b2f55553..821323b5 100644 --- a/renderers/BaseRenderer.php +++ b/renderers/BaseRenderer.php @@ -95,6 +95,11 @@ public function createTypeLink($types, $context = null, $title = null, $options ltrim($type, '\\'); } } + + if (is_object($type) && \method_exists($type, '__toString')) { + $type = (string)$type; + } + if (is_string($type)) { $linkText = ltrim($type, '\\'); if ($title !== null) { @@ -157,32 +162,22 @@ public function createTypeLink($types, $context = null, $title = null, $options /** * creates a link to a subject * @param PropertyDoc|MethodDoc|ConstDoc|EventDoc $subject - * @param string $title + * @param string|null $title * @param array $options additional HTML attributes for the link. * @return string */ - public function createSubjectLink($subject, $title = null, $options = []) + public function createSubjectLink($subject, string $title = null, array $options = []): string { if ($title === null) { - if ($subject instanceof MethodDoc) { - $title = $subject->name . '()'; - } else { - $title = $subject->name; - } + $title = $subject->shortName; } if (($type = $this->apiContext->getType($subject->definedBy)) === null) { - return $subject->name; - } else { - $link = $this->generateApiUrl($type->name); - if ($subject instanceof MethodDoc) { - $link .= '#' . $subject->name . '()'; - } else { - $link .= '#' . $subject->name; - } - $link .= '-detail'; - - return $this->generateLink($title, $link, $options); + return $subject->shortName; } + + $link = $this->generateApiUrl($type->name) . '#' . $subject->shortName . '-detail'; + + return $this->generateLink($title, $link, $options); } /** diff --git a/templates/bootstrap/RendererTrait.php b/templates/bootstrap/RendererTrait.php index 2ec72bbd..c184b838 100644 --- a/templates/bootstrap/RendererTrait.php +++ b/templates/bootstrap/RendererTrait.php @@ -100,7 +100,7 @@ protected function filterTypes($types, $navClasses) case 'yii': $self = $this; $types = array_filter($types, function ($val) use ($self) { - if ($val->name == 'Yii' || $val->name == 'YiiRequirementChecker') { + if ($val->name === 'Yii' || $val->name === 'YiiRequirementChecker') { return true; } if (strlen($val->name) < 5) { diff --git a/templates/html/views/methodSummary.php b/templates/html/views/methodSummary.php index 170cdf13..04565ef8 100644 --- a/templates/html/views/methodSummary.php +++ b/templates/html/views/methodSummary.php @@ -36,8 +36,8 @@ ArrayHelper::multisort($methods, 'name'); foreach ($methods as $method): ?> visibility == 'protected' || !$protected && $method->visibility != 'protected'): ?> - definedBy != $type->name ? ' class="inherited"' : '' ?> id="name ?>()"> - createSubjectLink($method, $method->name.'()') ?> + definedBy != $type->name ? ' class="inherited"' : '' ?> id="shortName ?>"> + createSubjectLink($method) ?> shortDescription, $method->definedBy, true) ?> createTypeLink($method->definedBy, $type) ?> diff --git a/templates/html/views/propertySummary.php b/templates/html/views/propertySummary.php index 45e373a3..c4051a7e 100644 --- a/templates/html/views/propertySummary.php +++ b/templates/html/views/propertySummary.php @@ -36,7 +36,7 @@ ArrayHelper::multisort($properties, 'name'); foreach ($properties as $property): ?> visibility == 'protected' || !$protected && $property->visibility != 'protected'): ?> - definedBy != $type->name ? ' class="inherited"' : '' ?> id="name ?>"> + definedBy !== $type->name ? ' class="inherited"' : '' ?> id="shortName ?>"> createSubjectLink($property) ?> createTypeLink($property->types) ?> shortDescription, $property->definedBy, true) ?> diff --git a/tests/TestCase.php b/tests/TestCase.php index 3153dd6d..b55e8882 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -16,7 +16,7 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase * Clean up after test. * By default the application created with [[mockApplication]] will be destroyed. */ - protected function tearDown() + protected function tearDown(): void { parent::tearDown(); $this->removeRuntimeDirectory(); @@ -71,11 +71,11 @@ public function assertEqualsWithoutLE($expected, $actual) * @param mixed $haystack * @param string $message */ - public function assertContainsWithoutIndent($needle, $haystack, $message = '') + public function assertContainsWithoutIndent($needle, $haystack/*, $message = ''*/) { $needle = str_replace(["\r", "\n", "\t", ' '], '', $needle); $haystack = str_replace(["\r", "\n", "\t", ' '], '', $haystack); - $this->assertContains($needle, $haystack, $message); + $this->assertStringContainsString($needle, $haystack/*, $message*/); } /** diff --git a/tests/commands/ApiControllerTest.php b/tests/commands/ApiControllerTest.php index 1fda17d9..0acece92 100644 --- a/tests/commands/ApiControllerTest.php +++ b/tests/commands/ApiControllerTest.php @@ -19,7 +19,7 @@ class ApiControllerTest extends TestCase /** * {@inheritdoc} */ - protected function setUp() + protected function setUp(): void { parent::setUp(); $this->mockApplication(); @@ -56,7 +56,7 @@ public function testNoFiles() $output = $this->generateApi(Yii::getAlias('@yiiunit/apidoc/data/guide')); $this->assertNotEmpty($output); - $this->assertContains('Error: No files found to process', $output); + $this->assertStringContainsString('Error: No files found to process', $output); } public function testGenerateBootstrap() @@ -64,7 +64,7 @@ public function testGenerateBootstrap() $output = $this->generateApi(Yii::getAlias('@yiiunit/apidoc/data/api'), '@runtime', ['template' => 'bootstrap']); $this->assertNotEmpty($output); - $this->assertContains('generating search index...done.', $output); + $this->assertStringContainsString('generating search index...done.', $output); $outputPath = Yii::getAlias('@runtime'); @@ -72,9 +72,9 @@ public function testGenerateBootstrap() $animalFile = $outputPath . DIRECTORY_SEPARATOR . 'yiiunit-apidoc-data-api-animal-animal.html'; $this->assertTrue(file_exists($animalFile)); $animalContent = file_get_contents($animalFile); - $this->assertContains('

Abstract Class yiiunit\apidoc\data\api\animal\Animal

', $animalContent); - $this->assertContains('Available since version1.0', $animalContent); - $this->assertContains('Animal is a base class for animals.', $animalContent); + $this->assertStringContainsString('

Abstract Class yiiunit\apidoc\data\api\animal\Animal

', $animalContent); + $this->assertStringContainsString('Available since version1.0', $animalContent); + $this->assertStringContainsString('Animal is a base class for animals.', $animalContent); $this->assertContainsWithoutIndent( << @@ -123,13 +123,13 @@ public function testGenerateBootstrap() $dogFile = $outputPath . DIRECTORY_SEPARATOR . 'yiiunit-apidoc-data-api-animal-dog.html'; $this->assertTrue(file_exists($dogFile)); $dogContent = file_get_contents($dogFile); - $this->assertContains('Available since version1.1', $dogContent); - $this->assertNotContains('@inheritdoc', $dogContent); + $this->assertStringContainsString('Available since version1.1', $dogContent); + $this->assertStringNotContainsString('@inheritdoc', $dogContent); // Class `Cat` : $catFile = $outputPath . DIRECTORY_SEPARATOR . 'yiiunit-apidoc-data-api-animal-cat.html'; $this->assertTrue(file_exists($catFile)); $catContent = file_get_contents($catFile); - $this->assertNotContains('@inheritdoc', $catContent); + $this->assertStringNotContainsString('@inheritdoc', $catContent); } } diff --git a/tests/commands/GuideControllerTest.php b/tests/commands/GuideControllerTest.php index 99d1ca1d..9e6be3ae 100644 --- a/tests/commands/GuideControllerTest.php +++ b/tests/commands/GuideControllerTest.php @@ -19,7 +19,7 @@ class GuideControllerTest extends TestCase /** * {@inheritdoc} */ - protected function setUp() + protected function setUp(): void { parent::setUp(); $this->mockApplication(); @@ -56,7 +56,7 @@ public function testNoFiles() $output = $this->generateGuide(Yii::getAlias('@yiiunit/apidoc/support')); $this->assertNotEmpty($output); - $this->assertContains('Error: No files found to process', $output); + $this->assertStringContainsString('Error: No files found to process', $output); } public function testGenerateBootstrap() @@ -64,23 +64,23 @@ public function testGenerateBootstrap() $output = $this->generateGuide(Yii::getAlias('@yiiunit/apidoc/data/guide'), '@runtime', ['template' => 'bootstrap']); $this->assertNotEmpty($output); - $this->assertContains('generating search index...done.', $output); - $this->assertContains('Publishing images...done.', $output); + $this->assertStringContainsString('generating search index...done.', $output); + $this->assertStringContainsString('Publishing images...done.', $output); $outputPath = Yii::getAlias('@runtime'); $readmeFile = $outputPath . DIRECTORY_SEPARATOR . 'guide-README.html'; $this->assertTrue(file_exists($readmeFile)); $readmeContent = file_get_contents($readmeFile); - $this->assertContains('

The Test Guide ', $readmeContent); - $this->assertContains('Intro', $readmeContent); - $this->assertContains('Upgrade', $readmeContent); + $this->assertStringContainsString('

The Test Guide ', $readmeContent); + $this->assertStringContainsString('Intro', $readmeContent); + $this->assertStringContainsString('Upgrade', $readmeContent); $tocFile = $outputPath . DIRECTORY_SEPARATOR . 'guide-TOC.html'; $this->assertTrue(file_exists($tocFile)); $tocFile = file_get_contents($tocFile); - $this->assertContains('

TOC Test ', $tocFile); + $this->assertStringContainsString('

TOC Test ', $tocFile); $this->assertEquals(1, substr_count($tocFile, '
')); } @@ -89,7 +89,7 @@ public function testGeneratePdf() $output = $this->generateGuide(Yii::getAlias('@yiiunit/apidoc/data/guide'), '@runtime', ['template' => 'pdf']); $this->assertNotEmpty($output); - $this->assertContains('Publishing images...done.', $output); + $this->assertStringContainsString('Publishing images...done.', $output); $outputPath = Yii::getAlias('@runtime'); $this->assertTrue(file_exists($outputPath . DIRECTORY_SEPARATOR . 'Makefile')); From b8e27f6a3c0d70e240d16d5bc067a86b0ef368bc Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Wed, 3 Nov 2021 12:08:59 +0600 Subject: [PATCH 02/58] Simplify dependencies in composer.json --- composer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 5d0a74b1..6cef376a 100644 --- a/composer.json +++ b/composer.json @@ -22,8 +22,7 @@ "php": "^7.2", "yiisoft/yii2": "~2.0.16", "yiisoft/yii2-bootstrap": "~2.0.0", - "phpdocumentor/reflection": "^4.0 | ^5.0", - "phpdocumentor/reflection-docblock": "^4.0 | ^5.0", + "phpdocumentor/reflection": "^5.0", "nikic/php-parser": "^4.0", "cebe/js-search": "~0.9.0", "cebe/markdown": "^1.0", From ac33ed24a4dbbb24b8da2c512adc2c1b4ff517cf Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Thu, 4 Nov 2021 12:03:29 +0600 Subject: [PATCH 03/58] Minimal working version, various fixes --- .gitignore | 6 ++- commands/ApiController.php | 2 +- helpers/PrettyPrinter.php | 3 +- models/BaseDoc.php | 17 +++---- models/Context.php | 64 +++++++++--------------- models/EventDoc.php | 6 +-- models/FunctionDoc.php | 14 ++---- models/ParamDoc.php | 17 ++++--- models/PropertyDoc.php | 12 ++++- models/TypeDoc.php | 18 +++---- renderers/BaseRenderer.php | 12 ++--- templates/html/views/methodSummary.php | 4 +- templates/html/views/propertySummary.php | 2 +- 13 files changed, 84 insertions(+), 93 deletions(-) diff --git a/.gitignore b/.gitignore index f2cdbfd5..9ca9e4a4 100644 --- a/.gitignore +++ b/.gitignore @@ -25,13 +25,15 @@ composer.phar # phpunit itself is not needed phpunit.phar + # local phpunit config /phpunit.xml +# phpunit cache +.phpunit.result.cache + # local tests runtime /tests/runtime # local tests configuration /tests/data/config.local.php - -.phpunit.result.cache diff --git a/commands/ApiController.php b/commands/ApiController.php index d1e4259b..243f9b68 100644 --- a/commands/ApiController.php +++ b/commands/ApiController.php @@ -103,9 +103,9 @@ public function actionIndex(array $sourceDirs, $targetDir) } Console::updateProgress(++$done, $fileCount); } + $context->processFiles(); Console::endProgress(true); $this->stdout('done.' . PHP_EOL, Console::FG_GREEN); - $context->processFiles(); // save processed data to cache $this->storeContext($context, $targetDir); diff --git a/helpers/PrettyPrinter.php b/helpers/PrettyPrinter.php index 5a233384..2aeeca02 100644 --- a/helpers/PrettyPrinter.php +++ b/helpers/PrettyPrinter.php @@ -8,6 +8,7 @@ namespace yii\apidoc\helpers; use PhpParser\Node\Expr; +use PhpParser\PrettyPrinter\Standard as BasePrettyPrinter; /** * Enhances the phpDocumentor PrettyPrinter with short array syntax @@ -15,7 +16,7 @@ * @author Carsten Brandt * @since 2.0 */ -class PrettyPrinter extends \phpDocumentor\Reflection\PrettyPrinter +class PrettyPrinter extends BasePrettyPrinter { /** * @param Expr\Array_ $node diff --git a/models/BaseDoc.php b/models/BaseDoc.php index 8f5e89b5..be8e082d 100644 --- a/models/BaseDoc.php +++ b/models/BaseDoc.php @@ -8,8 +8,6 @@ namespace yii\apidoc\models; use phpDocumentor\Reflection\DocBlock\Tag; -use phpDocumentor\Reflection\DocBlock\Tag\DeprecatedTag; -use phpDocumentor\Reflection\DocBlock\Tag\SinceTag; use phpDocumentor\Reflection\DocBlock\Tags\Deprecated; use phpDocumentor\Reflection\DocBlock\Tags\Generic; use phpDocumentor\Reflection\DocBlock\Tags\Since; @@ -26,11 +24,11 @@ class BaseDoc extends BaseObject { /** - * @var \phpDocumentor\Reflection\DocBlock\Context + * @var \phpDocumentor\Reflection\Types\Context */ public $phpDocContext; public $name; - public $shortName; + public $fullName; public $sourceFile; public $startLine; public $endLine; @@ -119,13 +117,12 @@ public function __construct($reflector = null, $context = null, $config = []) } // base properties - $this->name = ltrim((string) $reflector->getFqsen(), '\\'); - $separator = strrpos($this->name, '::'); - if ($separator !== false) { - $this->shortName = substr($this->name, $separator + 2); - } + $this->fullName = trim((string) $reflector->getFqsen(), '\\()'); + + $position = strrpos($this->fullName, '::'); + $this->name = $position === false ? $this->fullName : substr($this->fullName, $position + 2); + $this->startLine = $reflector->getLocation()->getLineNumber(); - //$this->endLine = $reflector->getNode()->getAttribute('endLine'); $docblock = $reflector->getDocBlock(); if ($docblock !== null) { diff --git a/models/Context.php b/models/Context.php index 3f201640..55d4bfa8 100644 --- a/models/Context.php +++ b/models/Context.php @@ -5,8 +5,6 @@ * @license http://www.yiiframework.com/license/ */ -declare(strict_types=1); - namespace yii\apidoc\models; use phpDocumentor\Reflection\File\LocalFile; @@ -44,6 +42,10 @@ class Context extends Component * @var array */ public $warnings = []; + /** + * @var Project + */ + private $reflectionProject; /** @@ -68,43 +70,32 @@ public function getType(string $type) return null; } - public function addProjectFile($fileName): void - { - $this->files[$fileName] = sha1_file($fileName); - } - - public function processFiles(): void + public function processFiles() { $projectFiles = $this->getReflectionProject()->getFiles(); foreach ($this->files as $fileName => $hash) { $reflection = $projectFiles[$fileName]; - foreach ($reflection->getClasses() as $class) { - $class = new ClassDoc($class, $this, ['sourceFile' => $fileName]); - $this->classes[$class->name] = $class; - } - foreach ($reflection->getInterfaces() as $interface) { - $interface = new InterfaceDoc($interface, $this, ['sourceFile' => $fileName]); - $this->interfaces[$interface->name] = $interface; - } - foreach ($reflection->getTraits() as $trait) { - $trait = new TraitDoc($trait, $this, ['sourceFile' => $fileName]); - $this->traits[$trait->name] = $trait; - } + $this->parseFile($reflection, $fileName); } } /** * Adds file to context + * @deprecated Use addProjectFile() instead * @param string $fileName */ - public function addFile(string $fileName): void + public function addFile($fileName) { - $this->files[$fileName] = sha1_file($fileName); + } - $reflection = new \phpDocumentor\Reflection\File\LocalFile($fileName, true); - $reflection->process(); + public function addProjectFile($fileName) + { + $this->files[$fileName] = sha1_file($fileName); + } + private function parseFile($reflection, $fileName) + { foreach ($reflection->getClasses() as $class) { $class = new ClassDoc($class, $this, ['sourceFile' => $fileName]); $this->classes[$class->name] = $class; @@ -119,10 +110,7 @@ public function addFile(string $fileName): void } } - /** - * Updates references - */ - public function updateReferences(): void + public function updateReferences() { // update all subclass references foreach ($this->classes as $class) { @@ -132,12 +120,10 @@ public function updateReferences(): void $class->subclasses[] = $className; } } - // update interfaces of subclasses foreach ($this->classes as $class) { $this->updateSubclassInterfacesTraits($class); } - // update implementedBy and usedBy for interfaces and traits foreach ($this->classes as $class) { foreach ($class->traits as $trait) { @@ -530,19 +516,19 @@ protected function isSubclassOf($classA, $classB) return false; } - private $reflectionProject; - - public function getReflectionProject(): Project + public function getReflectionProject() { - if ($this->reflectionProject === null) { - $files = []; - foreach ($this->files as $fileName => $hash) { - $files[] = new LocalFile($fileName); - } + if ($this->reflectionProject !== null) { + return $this->reflectionProject; + } - $this->reflectionProject = ProjectFactory::createInstance()->create('ApiDoc', $files); + $files = []; + foreach ($this->files as $fileName => $hash) { + $files[] = new LocalFile($fileName); } + $this->reflectionProject = ProjectFactory::createInstance()->create('ApiDoc', $files); + return $this->reflectionProject; } } diff --git a/models/EventDoc.php b/models/EventDoc.php index 93ff9743..cf214b5c 100644 --- a/models/EventDoc.php +++ b/models/EventDoc.php @@ -7,7 +7,7 @@ namespace yii\apidoc\models; -use phpDocumentor\Reflection\DocBlock\Tag\ReturnTag; +use phpDocumentor\Reflection\DocBlock\Tags\Return_; /** * Represents API documentation information for an `event`. @@ -36,9 +36,9 @@ public function __construct($reflector = null, $context = null, $config = []) foreach ($this->tags as $i => $tag) { if ($tag->getName() == 'event') { - $eventTag = new ReturnTag('event', $tag->getContent(), $tag->getDocBlock(), $tag->getLocation()); + $eventTag = new Return_('event', $tag->getContent(), $tag->getDocBlock(), $tag->getLocation()); $this->type = $eventTag->getType(); - $this->types = $eventTag->getTypes(); + $this->types = $eventTag->getType(); $this->description = static::mbUcFirst($eventTag->getDescription()); $this->shortDescription = BaseDoc::extractFirstSentence($this->description); unset($this->tags[$i]); diff --git a/models/FunctionDoc.php b/models/FunctionDoc.php index 0242ae8c..7469ab81 100644 --- a/models/FunctionDoc.php +++ b/models/FunctionDoc.php @@ -7,13 +7,9 @@ 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 phpDocumentor\Reflection\DocBlock\Tags\Param; +use phpDocumentor\Reflection\DocBlock\Tags\Return_; use phpDocumentor\Reflection\DocBlock\Tags\Throws; -use phpDocumentor\Reflection\Php\Function_; use phpDocumentor\Reflection\Php\Method; use phpDocumentor\Reflection\Php\Property; @@ -58,7 +54,7 @@ public function __construct($reflector = null, $context = null, $config = []) foreach ($this->tags as $i => $tag) { if ($tag instanceof Throws) { - $this->exceptions[$tag->getType()] = $tag->getDescription(); + $this->exceptions[(string) $tag->getType()->getFqsen()] = $tag->getDescription(); unset($this->tags[$i]); } elseif ($tag instanceof Property) { // ignore property tag @@ -74,11 +70,11 @@ public function __construct($reflector = null, $context = null, $config = []) } $this->params[$paramName]->description = static::mbUcFirst($tag->getDescription()); $this->params[$paramName]->type = $tag->getType(); - $this->params[$paramName]->types = $tag->getTypes(); + $this->params[$paramName]->types = $tag->getType(); unset($this->tags[$i]); - } elseif ($tag instanceof ReturnTag) { + } elseif ($tag instanceof Return_) { $this->returnType = $tag->getType(); - $this->returnTypes = $tag->getTypes(); + $this->returnTypes = $tag->getType(); $this->return = static::mbUcFirst($tag->getDescription()); unset($this->tags[$i]); } diff --git a/models/ParamDoc.php b/models/ParamDoc.php index 01a800f1..d66d81e4 100644 --- a/models/ParamDoc.php +++ b/models/ParamDoc.php @@ -7,6 +7,7 @@ namespace yii\apidoc\models; +use phpDocumentor\Reflection\Php\Argument; use yii\apidoc\helpers\PrettyPrinter; use yii\base\BaseObject; @@ -31,7 +32,7 @@ class ParamDoc extends BaseObject /** - * @param \phpDocumentor\Reflection\FunctionReflector\ArgumentReflector $reflector + * @param Argument $reflector * @param Context $context * @param array $config */ @@ -43,14 +44,14 @@ public function __construct($reflector = null, $context = null, $config = []) return; } - $this->name = $reflector->getName(); + $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(); +// if ($reflector->getDefault()) { +// $this->defaultValue = PrettyPrinter::getRepresentationOfValue($reflector->getDefault()); +// } + + $this->isOptional = $reflector->getDefault() !== null; + $this->isPassedByReference = $reflector->isByReference(); } } diff --git a/models/PropertyDoc.php b/models/PropertyDoc.php index 8552fa28..353d9091 100644 --- a/models/PropertyDoc.php +++ b/models/PropertyDoc.php @@ -77,8 +77,16 @@ public function __construct($reflector = null, $context = null, $config = []) $hasInheritdoc = true; } if ($tag instanceof Var_) { - $this->type = $tag->getType(); - $this->types = [$tag->getType()]; + $this->type = (string) $tag->getType(); + + foreach ($tag->getType() as $type) { + $this->types[] = (string) $type; + } + + if (!$this->types) { + $this->types = [$this->type]; + } + $this->description = static::mbUcFirst($tag->getDescription()); $this->shortDescription = BaseDoc::extractFirstSentence($this->description); } diff --git a/models/TypeDoc.php b/models/TypeDoc.php index a69fc03a..96f4cd5d 100644 --- a/models/TypeDoc.php +++ b/models/TypeDoc.php @@ -7,9 +7,9 @@ namespace yii\apidoc\models; -use phpDocumentor\Reflection\DocBlock\Tag\AuthorTag; -use phpDocumentor\Reflection\DocBlock\Tag\MethodTag; -use phpDocumentor\Reflection\DocBlock\Tag\PropertyTag; +use phpDocumentor\Reflection\DocBlock\Tags\Author; +use phpDocumentor\Reflection\DocBlock\Tags\Method; +use phpDocumentor\Reflection\DocBlock\Tags\Property; use yii\helpers\StringHelper; /** @@ -190,12 +190,12 @@ public function __construct($reflector = null, $context = null, $config = []) } foreach ($this->tags as $i => $tag) { - if ($tag instanceof AuthorTag) { - $this->authors[$tag->getAuthorName()] = $tag->getAuthorEmail(); + if ($tag instanceof Author) { + $this->authors[$tag->getAuthorName()] = $tag->getEmail(); unset($this->tags[$i]); } - if ($tag instanceof PropertyTag) { + if ($tag instanceof Property) { $property = new PropertyDoc(null, $context, [ 'sourceFile' => $this->sourceFile, 'name' => $tag->getVariableName(), @@ -203,7 +203,7 @@ public function __construct($reflector = null, $context = null, $config = []) 'visibility' => 'public', 'definedBy' => $this->name, 'type' => $tag->getType(), - 'types' => $tag->getTypes(), + 'types' => $tag->getType(), 'shortDescription' => $tag->getDescription(), 'description' => $tag->getDescription(), ]); @@ -211,7 +211,7 @@ public function __construct($reflector = null, $context = null, $config = []) $this->properties[$property->name] = $property; } - if ($tag instanceof MethodTag) { + if ($tag instanceof Method) { $params = []; foreach ($tag->getArguments() as $tagArgument) { @@ -242,7 +242,7 @@ public function __construct($reflector = null, $context = null, $config = []) 'isStatic' => $tag->isStatic(), 'return' => ' ', 'returnType' => $tag->getType(), - 'returnTypes' => $tag->getTypes(), + 'returnTypes' => $tag->getType(), ]); $method->definedBy = $this->name; $this->methods[$method->name] = $method; diff --git a/renderers/BaseRenderer.php b/renderers/BaseRenderer.php index 821323b5..13935dde 100644 --- a/renderers/BaseRenderer.php +++ b/renderers/BaseRenderer.php @@ -96,8 +96,8 @@ public function createTypeLink($types, $context = null, $title = null, $options } } - if (is_object($type) && \method_exists($type, '__toString')) { - $type = (string)$type; + if (is_object($type) && method_exists($type, '__toString')) { + $type = (string) $type; } if (is_string($type)) { @@ -166,16 +166,16 @@ public function createTypeLink($types, $context = null, $title = null, $options * @param array $options additional HTML attributes for the link. * @return string */ - public function createSubjectLink($subject, string $title = null, array $options = []): string + public function createSubjectLink($subject, $title = null, $options = []) { if ($title === null) { - $title = $subject->shortName; + $title = $subject->name; } if (($type = $this->apiContext->getType($subject->definedBy)) === null) { - return $subject->shortName; + return $subject->name; } - $link = $this->generateApiUrl($type->name) . '#' . $subject->shortName . '-detail'; + $link = $this->generateApiUrl($type->name) . '#' . $subject->name . '-detail'; return $this->generateLink($title, $link, $options); } diff --git a/templates/html/views/methodSummary.php b/templates/html/views/methodSummary.php index 04565ef8..170cdf13 100644 --- a/templates/html/views/methodSummary.php +++ b/templates/html/views/methodSummary.php @@ -36,8 +36,8 @@ ArrayHelper::multisort($methods, 'name'); foreach ($methods as $method): ?> visibility == 'protected' || !$protected && $method->visibility != 'protected'): ?> - definedBy != $type->name ? ' class="inherited"' : '' ?> id="shortName ?>"> - createSubjectLink($method) ?> + definedBy != $type->name ? ' class="inherited"' : '' ?> id="name ?>()"> + createSubjectLink($method, $method->name.'()') ?> shortDescription, $method->definedBy, true) ?> createTypeLink($method->definedBy, $type) ?> diff --git a/templates/html/views/propertySummary.php b/templates/html/views/propertySummary.php index c4051a7e..4572a52b 100644 --- a/templates/html/views/propertySummary.php +++ b/templates/html/views/propertySummary.php @@ -36,7 +36,7 @@ ArrayHelper::multisort($properties, 'name'); foreach ($properties as $property): ?> visibility == 'protected' || !$protected && $property->visibility != 'protected'): ?> - definedBy !== $type->name ? ' class="inherited"' : '' ?> id="shortName ?>"> + definedBy !== $type->name ? ' class="inherited"' : '' ?> id="name ?>"> createSubjectLink($property) ?> createTypeLink($property->types) ?> shortDescription, $property->definedBy, true) ?> From 072e7602a6bb36d89165d9af5c966661736a54e0 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Thu, 4 Nov 2021 12:05:57 +0600 Subject: [PATCH 04/58] Remove older PHP versions for tests, add PHP 8 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6b43d2c6..02df822e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - php: ['5.4', '5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '7.4'] + php: ['7.2', '7.3', '7.4', '8.0'] steps: - name: Checkout From 1ac13d8d31237c424a39340d2fff03b7d6628638 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Thu, 4 Nov 2021 12:08:44 +0600 Subject: [PATCH 05/58] Allow PHP 8 as a dependency in composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 6cef376a..02792d5c 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ ], "minimum-stability": "dev", "require": { - "php": "^7.2", + "php": "^7.2 || ^8.0", "yiisoft/yii2": "~2.0.16", "yiisoft/yii2-bootstrap": "~2.0.0", "phpdocumentor/reflection": "^5.0", From 79f933edf73db90124707e4c2ffb1d183096025f Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Thu, 4 Nov 2021 13:08:29 +0600 Subject: [PATCH 06/58] Leave hard coded value (isReturnByReference in FunctionDoc) --- models/FunctionDoc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/FunctionDoc.php b/models/FunctionDoc.php index 7469ab81..c1db33e2 100644 --- a/models/FunctionDoc.php +++ b/models/FunctionDoc.php @@ -45,7 +45,7 @@ public function __construct($reflector = null, $context = null, $config = []) return; } - $this->isReturnByReference = false;//$reflector->isByRef(); + $this->isReturnByReference = false; foreach ($reflector->getArguments() as $arg) { $arg = new ParamDoc($arg, $context, ['sourceFile' => $this->sourceFile]); From b1095fc0bddf2ca1f6e5b779f9bd2f87e1085aa8 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Thu, 4 Nov 2021 13:13:25 +0600 Subject: [PATCH 07/58] Remove unnecessary elseif block --- models/FunctionDoc.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/models/FunctionDoc.php b/models/FunctionDoc.php index c1db33e2..cffd2977 100644 --- a/models/FunctionDoc.php +++ b/models/FunctionDoc.php @@ -56,8 +56,6 @@ public function __construct($reflector = null, $context = null, $config = []) if ($tag instanceof Throws) { $this->exceptions[(string) $tag->getType()->getFqsen()] = $tag->getDescription(); unset($this->tags[$i]); - } elseif ($tag instanceof Property) { - // ignore property tag } elseif ($tag instanceof Param) { $paramName = $tag->getVariableName(); if (!isset($this->params[$paramName]) && $context !== null) { From c70b0445d3e2145a68941b3af67cd98f70862837 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Thu, 4 Nov 2021 13:25:56 +0600 Subject: [PATCH 08/58] Move construct method to the top, remove commented out code --- models/BaseDoc.php | 132 ++++++++++++++++++++------------------------- 1 file changed, 59 insertions(+), 73 deletions(-) diff --git a/models/BaseDoc.php b/models/BaseDoc.php index be8e082d..a8ae6a78 100644 --- a/models/BaseDoc.php +++ b/models/BaseDoc.php @@ -43,66 +43,6 @@ class BaseDoc extends BaseObject public $tags = []; - /** - * Checks if doc has tag of a given name - * @param string $name tag name - * @return bool if doc has tag of a given name - */ - public function hasTag($name) - { - foreach ($this->tags as $tag) { - if (strtolower($tag->getName()) == $name) { - return true; - } - } - return false; - } - - /** - * Removes tag of a given name - * @param string $name - */ - public function removeTag($name) - { - foreach ($this->tags as $i => $tag) { - if (strtolower($tag->getName()) == $name) { - unset($this->tags[$i]); - } - } - } - - /** - * Get the first tag of a given name - * @param string $name tag name. - * @return Tag|null tag instance, `null` if not found. - * @since 2.0.5 - */ - public function getFirstTag($name) - { - foreach ($this->tags as $i => $tag) { - if (strtolower($tag->getName()) == $name) { - return $this->tags[$i]; - } - } - - return null; - } - - /** - * Returns the Composer package for this type, if it can be determined from [[sourceFile]]. - * - * @return string|null - * @since 2.1.3 - */ - public function getPackageName() - { - if (!$this->sourceFile || !preg_match('/\/vendor\/([\w\-]+\/[\w\-]+)/', $this->sourceFile, $match)) { - return null; - } - - return $match[1]; - } - /** * @param Class_ $reflector * @param Context $context @@ -166,19 +106,65 @@ public function __construct($reflector = null, $context = null, $config = []) } } - // TODO implement -// public function loadSource($reflection) -// { -// $this->sourceFile; -// $this->startLine; -// $this->endLine; -// } -// -// public function getSourceCode() -// { -// $lines = file(YII2_PATH . $this->sourcePath); -// return implode("", array_slice($lines, $this->startLine - 1, $this->endLine - $this->startLine + 1)); -// } + /** + * Checks if doc has tag of a given name + * @param string $name tag name + * @return bool if doc has tag of a given name + */ + public function hasTag($name) + { + foreach ($this->tags as $tag) { + if (strtolower($tag->getName()) == $name) { + return true; + } + } + return false; + } + + /** + * Removes tag of a given name + * @param string $name + */ + public function removeTag($name) + { + foreach ($this->tags as $i => $tag) { + if (strtolower($tag->getName()) == $name) { + unset($this->tags[$i]); + } + } + } + + /** + * Get the first tag of a given name + * @param string $name tag name. + * @return Tag|null tag instance, `null` if not found. + * @since 2.0.5 + */ + public function getFirstTag($name) + { + foreach ($this->tags as $i => $tag) { + if (strtolower($tag->getName()) == $name) { + return $this->tags[$i]; + } + } + + return null; + } + + /** + * Returns the Composer package for this type, if it can be determined from [[sourceFile]]. + * + * @return string|null + * @since 2.1.3 + */ + public function getPackageName() + { + if (!$this->sourceFile || !preg_match('/\/vendor\/([\w\-]+\/[\w\-]+)/', $this->sourceFile, $match)) { + return null; + } + + return $match[1]; + } /** * Extracts first sentence out of text From 22ab8c5f96451718d80d7129cca8937cc2382f0a Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Thu, 4 Nov 2021 13:34:34 +0600 Subject: [PATCH 09/58] Make use of StringHelper::mb_ucfirst() --- models/BaseDoc.php | 10 ---------- models/EventDoc.php | 3 ++- models/FunctionDoc.php | 5 +++-- models/PropertyDoc.php | 3 ++- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/models/BaseDoc.php b/models/BaseDoc.php index a8ae6a78..2458674e 100644 --- a/models/BaseDoc.php +++ b/models/BaseDoc.php @@ -186,14 +186,4 @@ public static function extractFirstSentence($text) return $text; } - - /** - * Multibyte version of ucfirst() - * @since 2.0.6 - */ - protected static function mbUcFirst($string) - { - $firstChar = mb_strtoupper(mb_substr($string, 0, 1, 'utf-8'), 'utf-8'); - return $firstChar . mb_substr($string, 1, mb_strlen($string, 'utf-8'), 'utf-8'); - } } diff --git a/models/EventDoc.php b/models/EventDoc.php index cf214b5c..844ba540 100644 --- a/models/EventDoc.php +++ b/models/EventDoc.php @@ -8,6 +8,7 @@ namespace yii\apidoc\models; use phpDocumentor\Reflection\DocBlock\Tags\Return_; +use yii\helpers\StringHelper; /** * Represents API documentation information for an `event`. @@ -39,7 +40,7 @@ public function __construct($reflector = null, $context = null, $config = []) $eventTag = new Return_('event', $tag->getContent(), $tag->getDocBlock(), $tag->getLocation()); $this->type = $eventTag->getType(); $this->types = $eventTag->getType(); - $this->description = static::mbUcFirst($eventTag->getDescription()); + $this->description = StringHelper::mb_ucfirst($eventTag->getDescription()); $this->shortDescription = BaseDoc::extractFirstSentence($this->description); unset($this->tags[$i]); } diff --git a/models/FunctionDoc.php b/models/FunctionDoc.php index cffd2977..69330c1e 100644 --- a/models/FunctionDoc.php +++ b/models/FunctionDoc.php @@ -12,6 +12,7 @@ use phpDocumentor\Reflection\DocBlock\Tags\Throws; use phpDocumentor\Reflection\Php\Method; use phpDocumentor\Reflection\Php\Property; +use yii\helpers\StringHelper; /** * Represents API documentation information for a `function`. @@ -66,14 +67,14 @@ public function __construct($reflector = null, $context = null, $config = []) ]; continue; } - $this->params[$paramName]->description = static::mbUcFirst($tag->getDescription()); + $this->params[$paramName]->description = StringHelper::mb_ucfirst($tag->getDescription()); $this->params[$paramName]->type = $tag->getType(); $this->params[$paramName]->types = $tag->getType(); unset($this->tags[$i]); } elseif ($tag instanceof Return_) { $this->returnType = $tag->getType(); $this->returnTypes = $tag->getType(); - $this->return = static::mbUcFirst($tag->getDescription()); + $this->return = StringHelper::mb_ucfirst($tag->getDescription()); unset($this->tags[$i]); } } diff --git a/models/PropertyDoc.php b/models/PropertyDoc.php index 353d9091..666d6d8e 100644 --- a/models/PropertyDoc.php +++ b/models/PropertyDoc.php @@ -10,6 +10,7 @@ use phpDocumentor\Reflection\DocBlock\Tags\Var_; use phpDocumentor\Reflection\Php\Property; use yii\apidoc\helpers\PrettyPrinter; +use yii\helpers\StringHelper; /** * Represents API documentation information for a `property`. @@ -87,7 +88,7 @@ public function __construct($reflector = null, $context = null, $config = []) $this->types = [$this->type]; } - $this->description = static::mbUcFirst($tag->getDescription()); + $this->description = StringHelper::mb_ucfirst($tag->getDescription()); $this->shortDescription = BaseDoc::extractFirstSentence($this->description); } } From 2f798bf636b160e783b66c0440b5d48d401047e7 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Thu, 4 Nov 2021 13:53:31 +0600 Subject: [PATCH 10/58] Actualize stubs for setting type and types based on getType() --- models/BaseDoc.php | 15 +++++++++++++++ models/EventDoc.php | 4 ++-- models/FunctionDoc.php | 8 ++++---- models/PropertyDoc.php | 9 +-------- renderers/GuideRenderer.php | 1 - 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/models/BaseDoc.php b/models/BaseDoc.php index 2458674e..af6a189f 100644 --- a/models/BaseDoc.php +++ b/models/BaseDoc.php @@ -11,6 +11,7 @@ use phpDocumentor\Reflection\DocBlock\Tags\Deprecated; use phpDocumentor\Reflection\DocBlock\Tags\Generic; use phpDocumentor\Reflection\DocBlock\Tags\Since; +use phpDocumentor\Reflection\DocBlock\Tags\TagWithType; use phpDocumentor\Reflection\Php\Class_; use yii\base\BaseObject; use yii\helpers\StringHelper; @@ -106,6 +107,20 @@ public function __construct($reflector = null, $context = null, $config = []) } } + /** + * @param TagWithType $tag + * @return string[] + */ + protected function getTagTypes($tag) + { + $types = []; + foreach ($tag->getType() as $type) { + $types[] = (string) $type; + } + + return $types ?: [$tag->getType()]; + } + /** * Checks if doc has tag of a given name * @param string $name tag name diff --git a/models/EventDoc.php b/models/EventDoc.php index 844ba540..c1b84d4a 100644 --- a/models/EventDoc.php +++ b/models/EventDoc.php @@ -38,8 +38,8 @@ public function __construct($reflector = null, $context = null, $config = []) foreach ($this->tags as $i => $tag) { if ($tag->getName() == 'event') { $eventTag = new Return_('event', $tag->getContent(), $tag->getDocBlock(), $tag->getLocation()); - $this->type = $eventTag->getType(); - $this->types = $eventTag->getType(); + $this->type = (string) $eventTag->getType(); + $this->types = $this->getTagTypes($eventTag); $this->description = StringHelper::mb_ucfirst($eventTag->getDescription()); $this->shortDescription = BaseDoc::extractFirstSentence($this->description); unset($this->tags[$i]); diff --git a/models/FunctionDoc.php b/models/FunctionDoc.php index 69330c1e..6aebb0cd 100644 --- a/models/FunctionDoc.php +++ b/models/FunctionDoc.php @@ -68,12 +68,12 @@ public function __construct($reflector = null, $context = null, $config = []) continue; } $this->params[$paramName]->description = StringHelper::mb_ucfirst($tag->getDescription()); - $this->params[$paramName]->type = $tag->getType(); - $this->params[$paramName]->types = $tag->getType(); + $this->params[$paramName]->type = (string) $tag->getType(); + $this->params[$paramName]->types = $this->getTagTypes($tag); unset($this->tags[$i]); } elseif ($tag instanceof Return_) { - $this->returnType = $tag->getType(); - $this->returnTypes = $tag->getType(); + $this->returnType = (string) $tag->getType(); + $this->returnTypes = $this->getTagTypes($tag); $this->return = StringHelper::mb_ucfirst($tag->getDescription()); unset($this->tags[$i]); } diff --git a/models/PropertyDoc.php b/models/PropertyDoc.php index 666d6d8e..ab428d35 100644 --- a/models/PropertyDoc.php +++ b/models/PropertyDoc.php @@ -79,14 +79,7 @@ public function __construct($reflector = null, $context = null, $config = []) } if ($tag instanceof Var_) { $this->type = (string) $tag->getType(); - - foreach ($tag->getType() as $type) { - $this->types[] = (string) $type; - } - - if (!$this->types) { - $this->types = [$this->type]; - } + $this->types = $this->getTagTypes($tag); $this->description = StringHelper::mb_ucfirst($tag->getDescription()); $this->shortDescription = BaseDoc::extractFirstSentence($this->description); diff --git a/renderers/GuideRenderer.php b/renderers/GuideRenderer.php index 53a879ad..37bbe501 100644 --- a/renderers/GuideRenderer.php +++ b/renderers/GuideRenderer.php @@ -26,7 +26,6 @@ abstract class GuideRenderer extends BaseRenderer */ abstract public function render($files, $targetDir); - /** * Loads guide structure from a set of files * @param array $files From 186944fdd010e5e5d8c9d70ec092eda22a3db5c5 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Thu, 4 Nov 2021 18:37:35 +0600 Subject: [PATCH 11/58] Use fork, restore endLine and isReturnByReference logic --- composer.json | 6 +++++- models/BaseDoc.php | 6 +++++- models/FunctionDoc.php | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 02792d5c..0e86e01c 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "php": "^7.2 || ^8.0", "yiisoft/yii2": "~2.0.16", "yiisoft/yii2-bootstrap": "~2.0.0", - "phpdocumentor/reflection": "^5.0", + "phpdocumentor/reflection": "dev-access-phpparser-node", "nikic/php-parser": "^4.0", "cebe/js-search": "~0.9.0", "cebe/markdown": "^1.0", @@ -36,6 +36,10 @@ { "type": "composer", "url": "https://asset-packagist.org" + }, + { + "type": "git", + "url": "https://github.com/arogachev/reflection" } ], "autoload": { diff --git a/models/BaseDoc.php b/models/BaseDoc.php index af6a189f..6dfa88ca 100644 --- a/models/BaseDoc.php +++ b/models/BaseDoc.php @@ -65,6 +65,10 @@ public function __construct($reflector = null, $context = null, $config = []) $this->startLine = $reflector->getLocation()->getLineNumber(); + if (method_exists($reflector, 'getNode')) { + $this->endLine = $reflector->getNode()->getAttribute('endLine'); + } + $docblock = $reflector->getDocBlock(); if ($docblock !== null) { $this->shortDescription = StringHelper::mb_ucfirst($docblock->getSummary()); @@ -118,7 +122,7 @@ protected function getTagTypes($tag) $types[] = (string) $type; } - return $types ?: [$tag->getType()]; + return $types ?: [(string) $tag->getType()]; } /** diff --git a/models/FunctionDoc.php b/models/FunctionDoc.php index 6aebb0cd..bbb0ab85 100644 --- a/models/FunctionDoc.php +++ b/models/FunctionDoc.php @@ -46,7 +46,7 @@ public function __construct($reflector = null, $context = null, $config = []) return; } - $this->isReturnByReference = false; + $this->isReturnByReference = $reflector->getNode()->byRef; foreach ($reflector->getArguments() as $arg) { $arg = new ParamDoc($arg, $context, ['sourceFile' => $this->sourceFile]); From aaf5dbd96d1ee314df289cdfccc65bd6d79f59b5 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 5 Nov 2021 11:44:36 +0600 Subject: [PATCH 12/58] Handle getNode()->default, do not remove deprecated methods' bodies --- helpers/PrettyPrinter.php | 1 + models/BaseDoc.php | 12 ++++++++++++ models/ParamDoc.php | 6 +----- models/PropertyDoc.php | 6 +----- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/helpers/PrettyPrinter.php b/helpers/PrettyPrinter.php index 2aeeca02..ad10b786 100644 --- a/helpers/PrettyPrinter.php +++ b/helpers/PrettyPrinter.php @@ -30,6 +30,7 @@ public function pExpr_Array(Expr\Array_ $node) /** * Returns a simple human readable output for a value. * + * @deprecated It's now handled in phpDocumentor library. * @param Expr $value The value node as provided by PHP-Parser. * @return string */ diff --git a/models/BaseDoc.php b/models/BaseDoc.php index 6dfa88ca..e91fdbac 100644 --- a/models/BaseDoc.php +++ b/models/BaseDoc.php @@ -205,4 +205,16 @@ public static function extractFirstSentence($text) return $text; } + + + /** + * Multibyte version of ucfirst() + * @deprecated Use \yii\helpers\StringHelper::mb_ucfirst() instead + * @since 2.0.6 + */ + protected static function mbUcFirst($string) + { + $firstChar = mb_strtoupper(mb_substr($string, 0, 1, 'utf-8'), 'utf-8'); + return $firstChar . mb_substr($string, 1, mb_strlen($string, 'utf-8'), 'utf-8'); + } } diff --git a/models/ParamDoc.php b/models/ParamDoc.php index d66d81e4..5fb74a10 100644 --- a/models/ParamDoc.php +++ b/models/ParamDoc.php @@ -46,11 +46,7 @@ public function __construct($reflector = null, $context = null, $config = []) $this->name = '$'. $reflector->getName(); $this->typeHint = $reflector->getType(); - -// if ($reflector->getDefault()) { -// $this->defaultValue = PrettyPrinter::getRepresentationOfValue($reflector->getDefault()); -// } - + $this->defaultValue = $reflector->getDefault(); $this->isOptional = $reflector->getDefault() !== null; $this->isPassedByReference = $reflector->isByReference(); } diff --git a/models/PropertyDoc.php b/models/PropertyDoc.php index ab428d35..42e70bf2 100644 --- a/models/PropertyDoc.php +++ b/models/PropertyDoc.php @@ -66,11 +66,7 @@ public function __construct($reflector = null, $context = null, $config = []) $this->visibility = $reflector->getVisibility(); $this->isStatic = $reflector->isStatic(); - - // bypass $reflector->getDefault() for short array syntax - if ($reflector->getDefault()) { - $this->defaultValue = $reflector->getDefault(); //PrettyPrinter::getRepresentationOfValue($reflector->getDefault()); - } + $this->defaultValue = $reflector->getDefault(); $hasInheritdoc = false; foreach ($this->tags as $tag) { From d12e8ce90754f9a1eb5f66d5a284fd620e2ace62 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 5 Nov 2021 11:46:32 +0600 Subject: [PATCH 13/58] Removed author for consistency --- models/Context.php | 1 - 1 file changed, 1 deletion(-) diff --git a/models/Context.php b/models/Context.php index 55d4bfa8..47a3f4f4 100644 --- a/models/Context.php +++ b/models/Context.php @@ -14,7 +14,6 @@ /** * @author Carsten Brandt - * @author Paweł Brzozowski */ class Context extends Component { From 60365664b59ecb50657bd7aafad59d04794eb9da Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 5 Nov 2021 11:57:19 +0600 Subject: [PATCH 14/58] Cleanup --- models/BaseDoc.php | 1 - models/Context.php | 8 ++++---- models/FunctionDoc.php | 1 - models/ParamDoc.php | 1 - models/PropertyDoc.php | 1 - models/TypeDoc.php | 4 ++-- templates/bootstrap/RendererTrait.php | 2 +- templates/html/views/propertySummary.php | 2 +- 8 files changed, 8 insertions(+), 12 deletions(-) diff --git a/models/BaseDoc.php b/models/BaseDoc.php index e91fdbac..ea1d11ba 100644 --- a/models/BaseDoc.php +++ b/models/BaseDoc.php @@ -206,7 +206,6 @@ public static function extractFirstSentence($text) return $text; } - /** * Multibyte version of ucfirst() * @deprecated Use \yii\helpers\StringHelper::mb_ucfirst() instead diff --git a/models/Context.php b/models/Context.php index 47a3f4f4..cfadb58b 100644 --- a/models/Context.php +++ b/models/Context.php @@ -52,7 +52,7 @@ class Context extends Component * @param string $type * @return null|ClassDoc|InterfaceDoc|TraitDoc */ - public function getType(string $type) + public function getType($type) { $type = ltrim($type, '\\'); @@ -476,7 +476,7 @@ private function hasNonOptionalParams($method, $number = 0) $count++; } } - return $count === $number; + return $count == $number; } /** @@ -503,12 +503,12 @@ protected function isSubclassOf($classA, $classB) if (is_object($classB)) { $classB = $classB->name; } - if ($classA->name === $classB) { + if ($classA->name == $classB) { return true; } while ($classA->parentClass !== null && isset($this->classes[$classA->parentClass])) { $classA = $this->classes[$classA->parentClass]; - if ($classA->name === $classB) { + if ($classA->name == $classB) { return true; } } diff --git a/models/FunctionDoc.php b/models/FunctionDoc.php index bbb0ab85..e4671fd1 100644 --- a/models/FunctionDoc.php +++ b/models/FunctionDoc.php @@ -11,7 +11,6 @@ use phpDocumentor\Reflection\DocBlock\Tags\Return_; use phpDocumentor\Reflection\DocBlock\Tags\Throws; use phpDocumentor\Reflection\Php\Method; -use phpDocumentor\Reflection\Php\Property; use yii\helpers\StringHelper; /** diff --git a/models/ParamDoc.php b/models/ParamDoc.php index 5fb74a10..992e9223 100644 --- a/models/ParamDoc.php +++ b/models/ParamDoc.php @@ -8,7 +8,6 @@ namespace yii\apidoc\models; use phpDocumentor\Reflection\Php\Argument; -use yii\apidoc\helpers\PrettyPrinter; use yii\base\BaseObject; /** diff --git a/models/PropertyDoc.php b/models/PropertyDoc.php index 42e70bf2..706755fc 100644 --- a/models/PropertyDoc.php +++ b/models/PropertyDoc.php @@ -9,7 +9,6 @@ use phpDocumentor\Reflection\DocBlock\Tags\Var_; use phpDocumentor\Reflection\Php\Property; -use yii\apidoc\helpers\PrettyPrinter; use yii\helpers\StringHelper; /** diff --git a/models/TypeDoc.php b/models/TypeDoc.php index 96f4cd5d..102fe39f 100644 --- a/models/TypeDoc.php +++ b/models/TypeDoc.php @@ -250,7 +250,7 @@ public function __construct($reflector = null, $context = null, $config = []) } foreach ($reflector->getProperties() as $propertyReflector) { - if ($propertyReflector->getVisibility() !== 'private') { + if ($propertyReflector->getVisibility() != 'private') { $property = new PropertyDoc($propertyReflector, $context, ['sourceFile' => $this->sourceFile]); $property->definedBy = $this->name; $this->properties[$property->name] = $property; @@ -258,7 +258,7 @@ public function __construct($reflector = null, $context = null, $config = []) } foreach ($reflector->getMethods() as $methodReflector) { - if ($methodReflector->getVisibility() !== 'private') { + if ($methodReflector->getVisibility() != 'private') { $method = new MethodDoc($methodReflector, $context, ['sourceFile' => $this->sourceFile]); $method->definedBy = $this->name; $this->methods[$method->name] = $method; diff --git a/templates/bootstrap/RendererTrait.php b/templates/bootstrap/RendererTrait.php index c184b838..2ec72bbd 100644 --- a/templates/bootstrap/RendererTrait.php +++ b/templates/bootstrap/RendererTrait.php @@ -100,7 +100,7 @@ protected function filterTypes($types, $navClasses) case 'yii': $self = $this; $types = array_filter($types, function ($val) use ($self) { - if ($val->name === 'Yii' || $val->name === 'YiiRequirementChecker') { + if ($val->name == 'Yii' || $val->name == 'YiiRequirementChecker') { return true; } if (strlen($val->name) < 5) { diff --git a/templates/html/views/propertySummary.php b/templates/html/views/propertySummary.php index 4572a52b..45e373a3 100644 --- a/templates/html/views/propertySummary.php +++ b/templates/html/views/propertySummary.php @@ -36,7 +36,7 @@ ArrayHelper::multisort($properties, 'name'); foreach ($properties as $property): ?> visibility == 'protected' || !$protected && $property->visibility != 'protected'): ?> - definedBy !== $type->name ? ' class="inherited"' : '' ?> id="name ?>"> + definedBy != $type->name ? ' class="inherited"' : '' ?> id="name ?>"> createSubjectLink($property) ?> createTypeLink($property->types) ?> shortDescription, $property->definedBy, true) ?> From 7f1178a2ae956f2bb2b027df5a7d17fd83470861 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 5 Nov 2021 12:38:48 +0600 Subject: [PATCH 15/58] Handle type / types in TypeDoc, get rid of addProjectFile --- commands/ApiController.php | 2 +- models/BaseDoc.php | 10 +++++----- models/Context.php | 5 ----- models/EventDoc.php | 2 +- models/FunctionDoc.php | 4 ++-- models/ParamDoc.php | 4 ++-- models/PropertyDoc.php | 2 +- models/TypeDoc.php | 8 ++++---- 8 files changed, 16 insertions(+), 21 deletions(-) diff --git a/commands/ApiController.php b/commands/ApiController.php index 243f9b68..9577a92a 100644 --- a/commands/ApiController.php +++ b/commands/ApiController.php @@ -97,7 +97,7 @@ public function actionIndex(array $sourceDirs, $targetDir) $done = 0; foreach ($files as $file) { try { - $context->addProjectFile($file); + $context->addFile($file); } catch (\Exception $e) { $context->errors[] = "Unable to process \"$file\": " . $e->getMessage(); } diff --git a/models/BaseDoc.php b/models/BaseDoc.php index ea1d11ba..2feef721 100644 --- a/models/BaseDoc.php +++ b/models/BaseDoc.php @@ -11,8 +11,8 @@ use phpDocumentor\Reflection\DocBlock\Tags\Deprecated; use phpDocumentor\Reflection\DocBlock\Tags\Generic; use phpDocumentor\Reflection\DocBlock\Tags\Since; -use phpDocumentor\Reflection\DocBlock\Tags\TagWithType; use phpDocumentor\Reflection\Php\Class_; +use phpDocumentor\Reflection\Php\Factory\Type; use yii\base\BaseObject; use yii\helpers\StringHelper; @@ -112,17 +112,17 @@ public function __construct($reflector = null, $context = null, $config = []) } /** - * @param TagWithType $tag + * @param Type $aggregatedType * @return string[] */ - protected function getTagTypes($tag) + protected function splitTypes($aggregatedType) { $types = []; - foreach ($tag->getType() as $type) { + foreach ($aggregatedType as $type) { $types[] = (string) $type; } - return $types ?: [(string) $tag->getType()]; + return $types ?: [(string) $type]; } /** diff --git a/models/Context.php b/models/Context.php index cfadb58b..41e5219b 100644 --- a/models/Context.php +++ b/models/Context.php @@ -81,14 +81,9 @@ public function processFiles() /** * Adds file to context - * @deprecated Use addProjectFile() instead * @param string $fileName */ public function addFile($fileName) - { - } - - public function addProjectFile($fileName) { $this->files[$fileName] = sha1_file($fileName); } diff --git a/models/EventDoc.php b/models/EventDoc.php index c1b84d4a..8ff558bf 100644 --- a/models/EventDoc.php +++ b/models/EventDoc.php @@ -39,7 +39,7 @@ public function __construct($reflector = null, $context = null, $config = []) if ($tag->getName() == 'event') { $eventTag = new Return_('event', $tag->getContent(), $tag->getDocBlock(), $tag->getLocation()); $this->type = (string) $eventTag->getType(); - $this->types = $this->getTagTypes($eventTag); + $this->types = $this->splitTypes($eventTag->getType()); $this->description = StringHelper::mb_ucfirst($eventTag->getDescription()); $this->shortDescription = BaseDoc::extractFirstSentence($this->description); unset($this->tags[$i]); diff --git a/models/FunctionDoc.php b/models/FunctionDoc.php index e4671fd1..e9be496c 100644 --- a/models/FunctionDoc.php +++ b/models/FunctionDoc.php @@ -68,11 +68,11 @@ public function __construct($reflector = null, $context = null, $config = []) } $this->params[$paramName]->description = StringHelper::mb_ucfirst($tag->getDescription()); $this->params[$paramName]->type = (string) $tag->getType(); - $this->params[$paramName]->types = $this->getTagTypes($tag); + $this->params[$paramName]->types = $this->splitTypes($tag->getType()); unset($this->tags[$i]); } elseif ($tag instanceof Return_) { $this->returnType = (string) $tag->getType(); - $this->returnTypes = $this->getTagTypes($tag); + $this->returnTypes = $this->splitTypes($tag->getType()); $this->return = StringHelper::mb_ucfirst($tag->getDescription()); unset($this->tags[$i]); } diff --git a/models/ParamDoc.php b/models/ParamDoc.php index 992e9223..61db53de 100644 --- a/models/ParamDoc.php +++ b/models/ParamDoc.php @@ -44,9 +44,9 @@ public function __construct($reflector = null, $context = null, $config = []) } $this->name = '$'. $reflector->getName(); - $this->typeHint = $reflector->getType(); + $this->typeHint = (string) $reflector->getType(); $this->defaultValue = $reflector->getDefault(); - $this->isOptional = $reflector->getDefault() !== null; + $this->isOptional = $this->defaultValue !== null; $this->isPassedByReference = $reflector->isByReference(); } } diff --git a/models/PropertyDoc.php b/models/PropertyDoc.php index 706755fc..43ccf87a 100644 --- a/models/PropertyDoc.php +++ b/models/PropertyDoc.php @@ -74,7 +74,7 @@ public function __construct($reflector = null, $context = null, $config = []) } if ($tag instanceof Var_) { $this->type = (string) $tag->getType(); - $this->types = $this->getTagTypes($tag); + $this->types = $this->splitTypes($tag->getType()); $this->description = StringHelper::mb_ucfirst($tag->getDescription()); $this->shortDescription = BaseDoc::extractFirstSentence($this->description); diff --git a/models/TypeDoc.php b/models/TypeDoc.php index 102fe39f..b04d7dda 100644 --- a/models/TypeDoc.php +++ b/models/TypeDoc.php @@ -202,8 +202,8 @@ public function __construct($reflector = null, $context = null, $config = []) 'isStatic' => false, 'visibility' => 'public', 'definedBy' => $this->name, - 'type' => $tag->getType(), - 'types' => $tag->getType(), + 'type' => (string) $tag->getType(), + 'types' => $this->splitTypes($tag->getType()), 'shortDescription' => $tag->getDescription(), 'description' => $tag->getDescription(), ]); @@ -241,8 +241,8 @@ public function __construct($reflector = null, $context = null, $config = []) 'params' => $params, 'isStatic' => $tag->isStatic(), 'return' => ' ', - 'returnType' => $tag->getType(), - 'returnTypes' => $tag->getType(), + 'returnType' => (string) $tag->getReturnType(), + 'returnTypes' => $this->splitTypes($tag->getReturnType()), ]); $method->definedBy = $this->name; $this->methods[$method->name] = $method; From b3b44fcdde1d33bba7b1f60d6b5c3c58c88be2e7 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 5 Nov 2021 12:43:23 +0600 Subject: [PATCH 16/58] Fix undefined variable $type --- models/BaseDoc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/BaseDoc.php b/models/BaseDoc.php index 2feef721..99eebb24 100644 --- a/models/BaseDoc.php +++ b/models/BaseDoc.php @@ -122,7 +122,7 @@ protected function splitTypes($aggregatedType) $types[] = (string) $type; } - return $types ?: [(string) $type]; + return $types ?: [(string) $aggregatedType]; } /** From e4f989c66f6a9c3cc9b76cbf43f21352bbaa75b4 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 5 Nov 2021 12:54:34 +0600 Subject: [PATCH 17/58] Cleanup tests --- tests/TestCase.php | 6 +++--- tests/commands/ApiControllerTest.php | 3 +-- tests/commands/GuideControllerTest.php | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index b55e8882..e7a353ba 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -16,7 +16,7 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase * Clean up after test. * By default the application created with [[mockApplication]] will be destroyed. */ - protected function tearDown(): void + protected function tearDown() { parent::tearDown(); $this->removeRuntimeDirectory(); @@ -71,11 +71,11 @@ public function assertEqualsWithoutLE($expected, $actual) * @param mixed $haystack * @param string $message */ - public function assertContainsWithoutIndent($needle, $haystack/*, $message = ''*/) + public function assertContainsWithoutIndent($needle, $haystack) { $needle = str_replace(["\r", "\n", "\t", ' '], '', $needle); $haystack = str_replace(["\r", "\n", "\t", ' '], '', $haystack); - $this->assertStringContainsString($needle, $haystack/*, $message*/); + $this->assertStringContainsString($needle, $haystack); } /** diff --git a/tests/commands/ApiControllerTest.php b/tests/commands/ApiControllerTest.php index 0acece92..19e00aa3 100644 --- a/tests/commands/ApiControllerTest.php +++ b/tests/commands/ApiControllerTest.php @@ -19,7 +19,7 @@ class ApiControllerTest extends TestCase /** * {@inheritdoc} */ - protected function setUp(): void + protected function setUp() { parent::setUp(); $this->mockApplication(); @@ -117,7 +117,6 @@ public function testGenerateBootstrap() HTML , $animalContent ); - //$this->assertContains('$age', $animalContent); // Class `Dog` : $dogFile = $outputPath . DIRECTORY_SEPARATOR . 'yiiunit-apidoc-data-api-animal-dog.html'; diff --git a/tests/commands/GuideControllerTest.php b/tests/commands/GuideControllerTest.php index 9e6be3ae..4b4236fa 100644 --- a/tests/commands/GuideControllerTest.php +++ b/tests/commands/GuideControllerTest.php @@ -19,7 +19,7 @@ class GuideControllerTest extends TestCase /** * {@inheritdoc} */ - protected function setUp(): void + protected function setUp() { parent::setUp(); $this->mockApplication(); From ab6b134d451119546c833b313f6ad2188a703f0a Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 5 Nov 2021 17:59:30 +0600 Subject: [PATCH 18/58] Fix first 5 errors after testing on Yii 2 docs --- models/ClassDoc.php | 6 +++--- models/ConstDoc.php | 5 ++++- models/EventDoc.php | 30 ++++++++++++++++++++---------- models/FunctionDoc.php | 2 +- models/InterfaceDoc.php | 9 +++++++-- models/TraitDoc.php | 6 ++++-- models/TypeDoc.php | 19 ++++++++++++------- tests/TestCase.php | 2 +- 8 files changed, 52 insertions(+), 27 deletions(-) diff --git a/models/ClassDoc.php b/models/ClassDoc.php index 813b1ce7..bba82abc 100644 --- a/models/ClassDoc.php +++ b/models/ClassDoc.php @@ -115,9 +115,9 @@ public function __construct($reflector = null, $context = null, $config = []) $this->traits[] = ltrim($trait, '\\'); } foreach ($reflector->getConstants() as $constantReflector) { - $docblock = $constantReflector->getDocBlock(); - if ($docblock !== null && count($docblock->getTagsByName('event')) > 0) { - $event = new EventDoc($constantReflector); + $docBlock = $constantReflector->getDocBlock(); + if ($docBlock !== null && count($docBlock->getTagsByName('event')) > 0) { + $event = new EventDoc($constantReflector, null, null, $docBlock); $event->definedBy = $this->name; $this->events[$event->name] = $event; } else { diff --git a/models/ConstDoc.php b/models/ConstDoc.php index 2d835551..536a9120 100644 --- a/models/ConstDoc.php +++ b/models/ConstDoc.php @@ -7,6 +7,8 @@ namespace yii\apidoc\models; +use phpDocumentor\Reflection\DocBlock; + /** * Represents API documentation information for a `constant`. * @@ -23,8 +25,9 @@ class ConstDoc extends BaseDoc * @param \phpDocumentor\Reflection\ClassReflector\ConstantReflector $reflector * @param Context $context * @param array $config + * @param DocBlock $docBlock */ - public function __construct($reflector = null, $context = null, $config = []) + public function __construct($reflector = null, $context = null, $config = [], $docBlock = null) { parent::__construct($reflector, $context, $config); diff --git a/models/EventDoc.php b/models/EventDoc.php index 8ff558bf..5c570d0b 100644 --- a/models/EventDoc.php +++ b/models/EventDoc.php @@ -7,7 +7,9 @@ namespace yii\apidoc\models; -use phpDocumentor\Reflection\DocBlock\Tags\Return_; +use phpDocumentor\Reflection\DocBlock; +use phpDocumentor\Reflection\Php\Class_; +use phpDocumentor\Reflection\Php\Constant; use yii\helpers\StringHelper; /** @@ -23,11 +25,12 @@ class EventDoc extends ConstDoc /** - * @param \phpDocumentor\Reflection\ClassReflector\ConstantReflector $reflector + * @param Class_|Constant $reflector * @param Context $context * @param array $config + * @param DocBlock $docBlock */ - public function __construct($reflector = null, $context = null, $config = []) + public function __construct($reflector = null, $context = null, $config = [], $docBlock = null) { parent::__construct($reflector, $context, $config); @@ -36,14 +39,21 @@ public function __construct($reflector = null, $context = null, $config = []) } foreach ($this->tags as $i => $tag) { - if ($tag->getName() == 'event') { - $eventTag = new Return_('event', $tag->getContent(), $tag->getDocBlock(), $tag->getLocation()); - $this->type = (string) $eventTag->getType(); - $this->types = $this->splitTypes($eventTag->getType()); - $this->description = StringHelper::mb_ucfirst($eventTag->getDescription()); - $this->shortDescription = BaseDoc::extractFirstSentence($this->description); - unset($this->tags[$i]); + if ($tag->getName() != 'event') { + continue; } + + $className = explode(' ', trim($tag->getDescription()))[0]; + if (str_contains($className, '\\')) { + $this->type = $className; + } else { + $this->type = $docBlock->getContext()->getNamespace() . '\\' . $className; + } + + $this->types = [$this->type]; + $this->description = StringHelper::mb_ucfirst((string) $tag->getDescription()); + $this->shortDescription = BaseDoc::extractFirstSentence($this->description); + unset($this->tags[$i]); } } } diff --git a/models/FunctionDoc.php b/models/FunctionDoc.php index e9be496c..a586ff80 100644 --- a/models/FunctionDoc.php +++ b/models/FunctionDoc.php @@ -54,7 +54,7 @@ public function __construct($reflector = null, $context = null, $config = []) foreach ($this->tags as $i => $tag) { if ($tag instanceof Throws) { - $this->exceptions[(string) $tag->getType()->getFqsen()] = $tag->getDescription(); + $this->exceptions[implode($this->splitTypes($tag->getType()))] = $tag->getDescription(); unset($this->tags[$i]); } elseif ($tag instanceof Param) { $paramName = $tag->getVariableName(); diff --git a/models/InterfaceDoc.php b/models/InterfaceDoc.php index 65990c64..3c084ffd 100644 --- a/models/InterfaceDoc.php +++ b/models/InterfaceDoc.php @@ -7,6 +7,8 @@ namespace yii\apidoc\models; +use phpDocumentor\Reflection\Php\Interface_; + /** * Represents API documentation information for an `interface`. * @@ -21,7 +23,7 @@ class InterfaceDoc extends TypeDoc /** - * @param \phpDocumentor\Reflection\InterfaceReflector $reflector + * @param Interface_ $reflector * @param Context $context * @param array $config */ @@ -33,14 +35,17 @@ public function __construct($reflector = null, $context = null, $config = []) return; } - foreach ($reflector->getParentInterfaces() as $interface) { + foreach ($reflector->getParents() as $interface) { $this->parentInterfaces[] = ltrim($interface, '\\'); } foreach ($this->methods as $method) { $method->isAbstract = true; } + } + protected function initProperties($reflector, $context) + { // interface can not have properties $this->properties = []; } diff --git a/models/TraitDoc.php b/models/TraitDoc.php index a49bc952..b51aed64 100644 --- a/models/TraitDoc.php +++ b/models/TraitDoc.php @@ -7,6 +7,8 @@ namespace yii\apidoc\models; +use phpDocumentor\Reflection\Php\Trait_; + /** * Represents API documentation information for a `trait`. * @@ -22,7 +24,7 @@ class TraitDoc extends TypeDoc /** - * @param \phpDocumentor\Reflection\TraitReflector $reflector + * @param Trait_ $reflector * @param Context $context * @param array $config */ @@ -34,7 +36,7 @@ public function __construct($reflector = null, $context = null, $config = []) return; } - foreach ($reflector->getTraits() as $trait) { + foreach ($reflector->getUsedTraits() as $trait) { $this->traits[] = ltrim($trait, '\\'); } } diff --git a/models/TypeDoc.php b/models/TypeDoc.php index b04d7dda..2efdc4e5 100644 --- a/models/TypeDoc.php +++ b/models/TypeDoc.php @@ -249,13 +249,7 @@ public function __construct($reflector = null, $context = null, $config = []) } } - foreach ($reflector->getProperties() as $propertyReflector) { - if ($propertyReflector->getVisibility() != 'private') { - $property = new PropertyDoc($propertyReflector, $context, ['sourceFile' => $this->sourceFile]); - $property->definedBy = $this->name; - $this->properties[$property->name] = $property; - } - } + $this->initProperties($reflector, $context); foreach ($reflector->getMethods() as $methodReflector) { if ($methodReflector->getVisibility() != 'private') { @@ -265,4 +259,15 @@ public function __construct($reflector = null, $context = null, $config = []) } } } + + protected function initProperties($reflector, $context) + { + foreach ($reflector->getProperties() as $propertyReflector) { + if ($propertyReflector->getVisibility() != 'private') { + $property = new PropertyDoc($propertyReflector, $context, ['sourceFile' => $this->sourceFile]); + $property->definedBy = $this->name; + $this->properties[$property->name] = $property; + } + } + } } diff --git a/tests/TestCase.php b/tests/TestCase.php index e7a353ba..1333506a 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -16,7 +16,7 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase * Clean up after test. * By default the application created with [[mockApplication]] will be destroyed. */ - protected function tearDown() + protected function tearDown(): void { parent::tearDown(); $this->removeRuntimeDirectory(); From 01f54509ca7c38588e9e993e34586090cdc27584 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 5 Nov 2021 18:01:08 +0600 Subject: [PATCH 19/58] Make setUp compatible with parent (PHPUnit) --- tests/commands/ApiControllerTest.php | 2 +- tests/commands/GuideControllerTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/commands/ApiControllerTest.php b/tests/commands/ApiControllerTest.php index 19e00aa3..c2b33bb7 100644 --- a/tests/commands/ApiControllerTest.php +++ b/tests/commands/ApiControllerTest.php @@ -19,7 +19,7 @@ class ApiControllerTest extends TestCase /** * {@inheritdoc} */ - protected function setUp() + protected function setUp(): void { parent::setUp(); $this->mockApplication(); diff --git a/tests/commands/GuideControllerTest.php b/tests/commands/GuideControllerTest.php index 4b4236fa..9e6be3ae 100644 --- a/tests/commands/GuideControllerTest.php +++ b/tests/commands/GuideControllerTest.php @@ -19,7 +19,7 @@ class GuideControllerTest extends TestCase /** * {@inheritdoc} */ - protected function setUp() + protected function setUp(): void { parent::setUp(); $this->mockApplication(); From 63f542621e0e53d6c4b240d580c4540da6bc3d2f Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 8 Nov 2021 11:30:00 +0600 Subject: [PATCH 20/58] Fix "Undefined array key 0" error --- models/TypeDoc.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/models/TypeDoc.php b/models/TypeDoc.php index 2efdc4e5..e2b761fa 100644 --- a/models/TypeDoc.php +++ b/models/TypeDoc.php @@ -215,17 +215,11 @@ public function __construct($reflector = null, $context = null, $config = []) $params = []; foreach ($tag->getArguments() as $tagArgument) { - $argumentType = null; - - if (count($tagArgument) === 2) { - list ($argumentType, $argumentName) = $tagArgument; - } else { - $argumentName = $tagArgument[0]; - } + $argumentType = (string) $tagArgument['type']; $params[] = new ParamDoc(null, $context, [ 'sourceFile' => $this->sourceFile, - 'name' => $argumentName, + 'name' => $tagArgument['name'], 'typeHint' => $argumentType, 'type' => $argumentType, 'types' => [], From 1207b7a59808dadda0634a74c553e9fcace39730 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 8 Nov 2021 11:41:42 +0600 Subject: [PATCH 21/58] Fix "Call to undefined method Generic::getContent()" in Context --- models/Context.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/models/Context.php b/models/Context.php index 41e5219b..7e19666b 100644 --- a/models/Context.php +++ b/models/Context.php @@ -244,9 +244,11 @@ protected function inheritDocs($class) } } // descriptions will be concatenated. - $p->description = trim($p->description) . "\n\n" - . trim($inheritedProperty->description) . "\n\n" - . $inheritTag->getContent(); + $p->description = implode("\n\n", [ + trim($p->description), + trim($inheritedProperty->description), + $inheritTag->getDescription(), + ]); $p->removeTag('inheritdoc'); } @@ -275,9 +277,11 @@ protected function inheritDocs($class) } } // descriptions will be concatenated. - $m->description = trim($m->description) . "\n\n" - . trim($inheritedMethod->description) . "\n\n" - . $inheritTag->getContent(); + $m->description = implode("\n\n", [ + trim($m->description), + trim($inheritedMethod->description), + $inheritTag->getDescription(), + ]); foreach ($m->params as $i => $param) { if (!isset($inheritedMethod->params[$i])) { From fcafd6aa122986546e2c9fd20aaa20e385cb61b1 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 8 Nov 2021 12:16:17 +0600 Subject: [PATCH 22/58] Fix "Class Collection not found" error --- helpers/ApiMarkdownTrait.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/helpers/ApiMarkdownTrait.php b/helpers/ApiMarkdownTrait.php index a01cf316..1191750d 100644 --- a/helpers/ApiMarkdownTrait.php +++ b/helpers/ApiMarkdownTrait.php @@ -7,7 +7,6 @@ namespace yii\apidoc\helpers; -use phpDocumentor\Reflection\DocBlock\Type\Collection; use yii\apidoc\models\ClassDoc; use yii\apidoc\models\InterfaceDoc; use yii\apidoc\models\MethodDoc; @@ -109,8 +108,7 @@ protected function parseApiLinkForContext($offset, $object, $title, $context) $subjectName = substr($object, $pos + 2); if ($context !== null) { - // Collection resolves relative types - $typeName = (new Collection([$typeName], $context->phpDocContext))->__toString(); + $typeName = $context->phpDocContext->getNamespace() . '\\' . $typeName; } /** @var $type TypeDoc */ @@ -144,8 +142,7 @@ protected function parseApiLinkForContext($offset, $object, $title, $context) ]; } - // Collection resolves relative types - $object = (new Collection([$object], $context->phpDocContext))->__toString(); + $object = $context->phpDocContext->getNamespace() . '\\' . $object; } if (($type = static::$renderer->apiContext->getType($object)) !== null) { From f246153222b9f697a89963d6ae602fbec1be4cf4 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 8 Nov 2021 12:34:31 +0600 Subject: [PATCH 23/58] Fix regression with parentheses with MethodDoc --- models/MethodDoc.php | 5 ++++- renderers/BaseRenderer.php | 14 ++++++++++++-- templates/html/views/methodSummary.php | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/models/MethodDoc.php b/models/MethodDoc.php index 6b0d3299..226cbd03 100644 --- a/models/MethodDoc.php +++ b/models/MethodDoc.php @@ -7,6 +7,9 @@ namespace yii\apidoc\models; +use phpDocumentor\Reflection\Php\Class_; +use phpDocumentor\Reflection\Php\Method; + /** * Represents API documentation information for a `method`. * @@ -24,7 +27,7 @@ class MethodDoc extends FunctionDoc /** - * @param \phpDocumentor\Reflection\ClassReflector\MethodReflector $reflector + * @param Class_|Method $reflector * @param Context $context * @param array $config */ diff --git a/renderers/BaseRenderer.php b/renderers/BaseRenderer.php index 13935dde..b7de81c9 100644 --- a/renderers/BaseRenderer.php +++ b/renderers/BaseRenderer.php @@ -169,13 +169,23 @@ public function createTypeLink($types, $context = null, $title = null, $options public function createSubjectLink($subject, $title = null, $options = []) { if ($title === null) { - $title = $subject->name; + if ($subject instanceof MethodDoc) { + $title = $subject->name . '()'; + } else { + $title = $subject->name; + } } if (($type = $this->apiContext->getType($subject->definedBy)) === null) { return $subject->name; } - $link = $this->generateApiUrl($type->name) . '#' . $subject->name . '-detail'; + $link = $this->generateApiUrl($type->name); + if ($subject instanceof MethodDoc) { + $link .= '#' . $subject->name . '()'; + } else { + $link .= '#' . $subject->name; + } + $link .= '-detail'; return $this->generateLink($title, $link, $options); } diff --git a/templates/html/views/methodSummary.php b/templates/html/views/methodSummary.php index 170cdf13..07b150c5 100644 --- a/templates/html/views/methodSummary.php +++ b/templates/html/views/methodSummary.php @@ -37,7 +37,7 @@ foreach ($methods as $method): ?> visibility == 'protected' || !$protected && $method->visibility != 'protected'): ?> definedBy != $type->name ? ' class="inherited"' : '' ?> id="name ?>()"> - createSubjectLink($method, $method->name.'()') ?> + createSubjectLink($method, $method->name . '()') ?> shortDescription, $method->definedBy, true) ?> createTypeLink($method->definedBy, $type) ?> From 73f12e862890635cc56a2ebe132b73d78c38743a Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 8 Nov 2021 14:23:09 +0600 Subject: [PATCH 24/58] Fix property-read / property-write parsing issues --- models/TypeDoc.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/models/TypeDoc.php b/models/TypeDoc.php index e2b761fa..51bfdb5b 100644 --- a/models/TypeDoc.php +++ b/models/TypeDoc.php @@ -10,6 +10,9 @@ use phpDocumentor\Reflection\DocBlock\Tags\Author; use phpDocumentor\Reflection\DocBlock\Tags\Method; use phpDocumentor\Reflection\DocBlock\Tags\Property; +use phpDocumentor\Reflection\DocBlock\Tags\PropertyRead; +use phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite; +use phpDocumentor\Reflection\Php\Class_; use yii\helpers\StringHelper; /** @@ -175,7 +178,7 @@ private function getFilteredProperties($visibility = null, $definedBy = null) } /** - * @param \phpDocumentor\Reflection\InterfaceReflector $reflector + * @param Class_ $reflector * @param Context $context * @param array $config */ @@ -195,10 +198,10 @@ public function __construct($reflector = null, $context = null, $config = []) unset($this->tags[$i]); } - if ($tag instanceof Property) { + if ($tag instanceof Property || $tag instanceof PropertyRead || $tag instanceof PropertyWrite) { $property = new PropertyDoc(null, $context, [ 'sourceFile' => $this->sourceFile, - 'name' => $tag->getVariableName(), + 'name' => '$' . $tag->getVariableName(), 'isStatic' => false, 'visibility' => 'public', 'definedBy' => $this->name, From 3b700bdb706c769f54e59825bb8a51aa7de4ecc9 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 8 Nov 2021 17:54:00 +0600 Subject: [PATCH 25/58] Fix event description (was merged with class name and not capitalized) --- models/EventDoc.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/models/EventDoc.php b/models/EventDoc.php index 5c570d0b..054c4186 100644 --- a/models/EventDoc.php +++ b/models/EventDoc.php @@ -43,7 +43,10 @@ public function __construct($reflector = null, $context = null, $config = [], $d continue; } - $className = explode(' ', trim($tag->getDescription()))[0]; + $parts = explode(' ', trim($tag->getDescription()), 2); + $className = $parts[0]; + $this->description = StringHelper::mb_ucfirst($parts[1]); + if (str_contains($className, '\\')) { $this->type = $className; } else { @@ -51,7 +54,6 @@ public function __construct($reflector = null, $context = null, $config = [], $d } $this->types = [$this->type]; - $this->description = StringHelper::mb_ucfirst((string) $tag->getDescription()); $this->shortDescription = BaseDoc::extractFirstSentence($this->description); unset($this->tags[$i]); } From 7b3e0d3ca2a4d02f40ed2ccdad8e9e3fab14249e Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Tue, 9 Nov 2021 11:20:04 +0600 Subject: [PATCH 26/58] Fix issue when "seeAlso" partial template was not rendered --- templates/html/views/seeAlso.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/templates/html/views/seeAlso.php b/templates/html/views/seeAlso.php index 8abbf314..8d63a79a 100644 --- a/templates/html/views/seeAlso.php +++ b/templates/html/views/seeAlso.php @@ -3,17 +3,20 @@ /* @var $object yii\apidoc\models\BaseDoc */ /* @var $this yii\web\View */ -$type = $object instanceof \yii\apidoc\models\TypeDoc ? $object : $object->definedBy; +use yii\apidoc\helpers\ApiMarkdown; +use yii\apidoc\models\TypeDoc; + +$type = $object instanceof TypeDoc ? $object : $object->definedBy; $see = []; foreach ($object->tags as $tag) { - /** @var $tag phpDocumentor\Reflection\DocBlock\Tag\SeeTag */ - if (get_class($tag) == 'phpDocumentor\Reflection\DocBlock\Tag\SeeTag') { + /** @var $tag phpDocumentor\Reflection\DocBlock\Tags\See */ + if (get_class($tag) == 'phpDocumentor\Reflection\DocBlock\Tags\See') { $ref = $tag->getReference(); - if (strpos($ref, '://') === false) { + if (str_contains($ref, '://')) { $ref = '[[' . $ref . ']]'; } - $see[] = rtrim(\yii\apidoc\helpers\ApiMarkdown::process($ref . ' ' . $tag->getDescription(), $type, true), ". \r\n"); + $see[] = rtrim(ApiMarkdown::process($ref . ' ' . $tag->getDescription(), $type, true), ". \r\n"); } } if (empty($see)) { From b381335f102a5be917806390cc6c6ee376d1b1fc Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Tue, 9 Nov 2021 16:39:01 +0600 Subject: [PATCH 27/58] Revert str_contains -> strpos --- models/EventDoc.php | 2 +- templates/html/views/seeAlso.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/models/EventDoc.php b/models/EventDoc.php index 054c4186..8916d431 100644 --- a/models/EventDoc.php +++ b/models/EventDoc.php @@ -47,7 +47,7 @@ public function __construct($reflector = null, $context = null, $config = [], $d $className = $parts[0]; $this->description = StringHelper::mb_ucfirst($parts[1]); - if (str_contains($className, '\\')) { + if (strpos($className, '\\') !== false) { $this->type = $className; } else { $this->type = $docBlock->getContext()->getNamespace() . '\\' . $className; diff --git a/templates/html/views/seeAlso.php b/templates/html/views/seeAlso.php index 8d63a79a..8d19b864 100644 --- a/templates/html/views/seeAlso.php +++ b/templates/html/views/seeAlso.php @@ -13,7 +13,7 @@ /** @var $tag phpDocumentor\Reflection\DocBlock\Tags\See */ if (get_class($tag) == 'phpDocumentor\Reflection\DocBlock\Tags\See') { $ref = $tag->getReference(); - if (str_contains($ref, '://')) { + if (strpos($ref, '://') === false) { $ref = '[[' . $ref . ']]'; } $see[] = rtrim(ApiMarkdown::process($ref . ' ' . $tag->getDescription(), $type, true), ". \r\n"); From 598c5590c66e91e64a64082f930b9bbee1f91d96 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Tue, 9 Nov 2021 18:57:28 +0600 Subject: [PATCH 28/58] Handle null in aggregated type during split types --- models/BaseDoc.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/models/BaseDoc.php b/models/BaseDoc.php index 99eebb24..e39d8f8b 100644 --- a/models/BaseDoc.php +++ b/models/BaseDoc.php @@ -112,11 +112,15 @@ public function __construct($reflector = null, $context = null, $config = []) } /** - * @param Type $aggregatedType - * @return string[] + * @param Type|null $aggregatedType + * @return string[]|array */ protected function splitTypes($aggregatedType) { + if ($aggregatedType === null) { + return []; + } + $types = []; foreach ($aggregatedType as $type) { $types[] = (string) $type; From 1b432ffaefef840484e6c0c1d2d857535d216315 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Tue, 9 Nov 2021 19:01:21 +0600 Subject: [PATCH 29/58] Fix broken links --- helpers/ApiMarkdownTrait.php | 13 +++++++++++-- models/FunctionDoc.php | 2 +- models/TypeDoc.php | 2 ++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/helpers/ApiMarkdownTrait.php b/helpers/ApiMarkdownTrait.php index 1191750d..69c0cecc 100644 --- a/helpers/ApiMarkdownTrait.php +++ b/helpers/ApiMarkdownTrait.php @@ -108,9 +108,14 @@ protected function parseApiLinkForContext($offset, $object, $title, $context) $subjectName = substr($object, $pos + 2); if ($context !== null) { - $typeName = $context->phpDocContext->getNamespace() . '\\' . $typeName; + if (isset($context->phpDocContext->getNamespaceAliases()[$typeName])) { + $typeName = $context->phpDocContext->getNamespaceAliases()[$typeName]; + } else { + $typeName = $context->phpDocContext->getNamespace() . '\\' . $typeName; + } } + /** @var $type TypeDoc */ $type = static::$renderer->apiContext->getType($typeName); @@ -142,7 +147,11 @@ protected function parseApiLinkForContext($offset, $object, $title, $context) ]; } - $object = $context->phpDocContext->getNamespace() . '\\' . $object; + if (isset($context->phpDocContext->getNamespaceAliases()[$object])) { + $object = $context->phpDocContext->getNamespaceAliases()[$object]; + } else { + $object = $context->phpDocContext->getNamespace() . '\\' . $object; + } } if (($type = static::$renderer->apiContext->getType($object)) !== null) { diff --git a/models/FunctionDoc.php b/models/FunctionDoc.php index a586ff80..2ef1be88 100644 --- a/models/FunctionDoc.php +++ b/models/FunctionDoc.php @@ -57,7 +57,7 @@ public function __construct($reflector = null, $context = null, $config = []) $this->exceptions[implode($this->splitTypes($tag->getType()))] = $tag->getDescription(); unset($this->tags[$i]); } elseif ($tag instanceof Param) { - $paramName = $tag->getVariableName(); + $paramName = '$' . $tag->getVariableName(); if (!isset($this->params[$paramName]) && $context !== null) { $context->errors[] = [ 'line' => $this->startLine, diff --git a/models/TypeDoc.php b/models/TypeDoc.php index 51bfdb5b..a23bfdf6 100644 --- a/models/TypeDoc.php +++ b/models/TypeDoc.php @@ -62,6 +62,8 @@ public function findSubject($subjectName) if (empty($subjectName)) { return null; } + + $subjectName = ltrim(str_replace($this->namespace, '', $subjectName), '\\'); if ($subjectName[0] !== '$') { foreach ($this->methods as $name => $method) { if (rtrim($subjectName, '()') == $name) { From bfbe60e5312b4149e07c1267e6d172a9d39242f3 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Tue, 9 Nov 2021 19:06:13 +0600 Subject: [PATCH 30/58] Fix regression ("mixed" in params in method signature) --- models/ParamDoc.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/models/ParamDoc.php b/models/ParamDoc.php index 61db53de..cb3b0fc1 100644 --- a/models/ParamDoc.php +++ b/models/ParamDoc.php @@ -44,7 +44,12 @@ public function __construct($reflector = null, $context = null, $config = []) } $this->name = '$'. $reflector->getName(); + $this->typeHint = (string) $reflector->getType(); + if ($this->typeHint === 'mixed') { + $this->typeHint = ''; + } + $this->defaultValue = $reflector->getDefault(); $this->isOptional = $this->defaultValue !== null; $this->isPassedByReference = $reflector->isByReference(); From b9012544f68dcde3a8842cdf975701b74ea88d36 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Tue, 9 Nov 2021 19:56:00 +0600 Subject: [PATCH 31/58] This kind of refactoring can be done later (better diffs) --- models/BaseDoc.php | 134 ++++++++++++++++++++++----------------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/models/BaseDoc.php b/models/BaseDoc.php index e39d8f8b..311ed49e 100644 --- a/models/BaseDoc.php +++ b/models/BaseDoc.php @@ -44,73 +44,6 @@ class BaseDoc extends BaseObject public $tags = []; - /** - * @param Class_ $reflector - * @param Context $context - * @param array $config - */ - public function __construct($reflector = null, $context = null, $config = []) - { - parent::__construct($config); - - if ($reflector === null) { - return; - } - - // base properties - $this->fullName = trim((string) $reflector->getFqsen(), '\\()'); - - $position = strrpos($this->fullName, '::'); - $this->name = $position === false ? $this->fullName : substr($this->fullName, $position + 2); - - $this->startLine = $reflector->getLocation()->getLineNumber(); - - if (method_exists($reflector, 'getNode')) { - $this->endLine = $reflector->getNode()->getAttribute('endLine'); - } - - $docblock = $reflector->getDocBlock(); - if ($docblock !== null) { - $this->shortDescription = StringHelper::mb_ucfirst($docblock->getSummary()); - if (empty($this->shortDescription) && !($this instanceof PropertyDoc) && $context !== null && $docblock->getTagsByName('inheritdoc') === null) { - $context->warnings[] = [ - 'line' => $this->startLine, - 'file' => $this->sourceFile, - 'message' => "No short description for " . substr(StringHelper::basename(get_class($this)), 0, -3) . " '{$this->name}'", - ]; - } - $this->description = $docblock->getDescription()->render(); - - $this->phpDocContext = $docblock->getContext(); - - $this->tags = $docblock->getTags(); - foreach ($this->tags as $i => $tag) { - if ($tag instanceof Since) { - $this->since = $tag->getVersion(); - unset($this->tags[$i]); - } elseif ($tag instanceof Deprecated) { - $this->deprecatedSince = $tag->getVersion(); - $this->deprecatedReason = $tag->getDescription(); - unset($this->tags[$i]); - } - } - - if (in_array($this->shortDescription, ['{@inheritdoc}', '{@inheritDoc}', '@inheritdoc', '@inheritDoc'], true)) { - // Mock up parsing of '{@inheritdoc}' (in brackets) tag, which is not yet supported at "phpdocumentor/reflection-docblock" 2.x - // todo consider removal in case of "phpdocumentor/reflection-docblock" upgrade - $this->tags[] = new Generic('inheritdoc'); - $this->shortDescription = ''; - } - - } elseif ($context !== null) { - $context->warnings[] = [ - 'line' => $this->startLine, - 'file' => $this->sourceFile, - 'message' => "No docblock for element '{$this->name}'", - ]; - } - } - /** * @param Type|null $aggregatedType * @return string[]|array @@ -189,6 +122,73 @@ public function getPackageName() return $match[1]; } + /** + * @param Class_ $reflector + * @param Context $context + * @param array $config + */ + public function __construct($reflector = null, $context = null, $config = []) + { + parent::__construct($config); + + if ($reflector === null) { + return; + } + + // base properties + $this->fullName = trim((string) $reflector->getFqsen(), '\\()'); + + $position = strrpos($this->fullName, '::'); + $this->name = $position === false ? $this->fullName : substr($this->fullName, $position + 2); + + $this->startLine = $reflector->getLocation()->getLineNumber(); + + if (method_exists($reflector, 'getNode')) { + $this->endLine = $reflector->getNode()->getAttribute('endLine'); + } + + $docblock = $reflector->getDocBlock(); + if ($docblock !== null) { + $this->shortDescription = StringHelper::mb_ucfirst($docblock->getSummary()); + if (empty($this->shortDescription) && !($this instanceof PropertyDoc) && $context !== null && $docblock->getTagsByName('inheritdoc') === null) { + $context->warnings[] = [ + 'line' => $this->startLine, + 'file' => $this->sourceFile, + 'message' => "No short description for " . substr(StringHelper::basename(get_class($this)), 0, -3) . " '{$this->name}'", + ]; + } + $this->description = $docblock->getDescription()->render(); + + $this->phpDocContext = $docblock->getContext(); + + $this->tags = $docblock->getTags(); + foreach ($this->tags as $i => $tag) { + if ($tag instanceof Since) { + $this->since = $tag->getVersion(); + unset($this->tags[$i]); + } elseif ($tag instanceof Deprecated) { + $this->deprecatedSince = $tag->getVersion(); + $this->deprecatedReason = $tag->getDescription(); + unset($this->tags[$i]); + } + } + + if (in_array($this->shortDescription, ['{@inheritdoc}', '{@inheritDoc}', '@inheritdoc', '@inheritDoc'], true)) { + // Mock up parsing of '{@inheritdoc}' (in brackets) tag, which is not yet supported at "phpdocumentor/reflection-docblock" 2.x + // todo consider removal in case of "phpdocumentor/reflection-docblock" upgrade + $this->tags[] = new Generic('inheritdoc'); + $this->shortDescription = ''; + } + + } elseif ($context !== null) { + $context->warnings[] = [ + 'line' => $this->startLine, + 'file' => $this->sourceFile, + 'message' => "No docblock for element '{$this->name}'", + ]; + } + } + /** * Extracts first sentence out of text * @param string $text From b710ee294c6f62657a6d265bd98c465bb8eb3625 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Wed, 10 Nov 2021 20:47:12 +0600 Subject: [PATCH 32/58] Fix pretty print of default value in property --- helpers/ApiMarkdownTrait.php | 2 +- helpers/MarkdownHighlightTrait.php | 5 ++--- helpers/PrettyPrinter.php | 11 +++++++++-- models/BaseDoc.php | 2 +- models/PropertyDoc.php | 6 +++++- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/helpers/ApiMarkdownTrait.php b/helpers/ApiMarkdownTrait.php index 69c0cecc..aad3cc49 100644 --- a/helpers/ApiMarkdownTrait.php +++ b/helpers/ApiMarkdownTrait.php @@ -150,7 +150,7 @@ protected function parseApiLinkForContext($offset, $object, $title, $context) if (isset($context->phpDocContext->getNamespaceAliases()[$object])) { $object = $context->phpDocContext->getNamespaceAliases()[$object]; } else { - $object = $context->phpDocContext->getNamespace() . '\\' . $object; + $object = $context->phpDocContext->getNamespace() . '\\' . $object; } } diff --git a/helpers/MarkdownHighlightTrait.php b/helpers/MarkdownHighlightTrait.php index d149f4fe..9433769a 100644 --- a/helpers/MarkdownHighlightTrait.php +++ b/helpers/MarkdownHighlightTrait.php @@ -58,18 +58,17 @@ protected function renderCode($block) * @param string $code code to highlight * @param string $language language of the code to highlight * @return string HTML of highlighted code - * @deprecated since 2.0.5 this method is not used anymore, highlight.php is used for highlighting */ public static function highlight($code, $language) { if ($language !== 'php') { - return htmlspecialchars($code, ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8'); + return htmlspecialchars($code, ENT_NOQUOTES | ENT_SUBSTITUTE); } if (strncmp($code, 'prettyPrintExpr($value); } + + /** + * https://github.com/nikic/PHP-Parser/issues/447#issuecomment-348557940 + * @param string $string + * @return string + */ + protected function pSingleQuotedString(string $string) { + return '\'' . preg_replace("/'|\\\\(?=[\\\\']|$)/", '\\\\$0', $string) . '\''; + } } diff --git a/models/BaseDoc.php b/models/BaseDoc.php index 311ed49e..2664368d 100644 --- a/models/BaseDoc.php +++ b/models/BaseDoc.php @@ -143,7 +143,7 @@ public function __construct($reflector = null, $context = null, $config = []) $this->startLine = $reflector->getLocation()->getLineNumber(); - if (method_exists($reflector, 'getNode')) { + if (method_exists($reflector, 'getNode') && $reflector->getNode()) { $this->endLine = $reflector->getNode()->getAttribute('endLine'); } diff --git a/models/PropertyDoc.php b/models/PropertyDoc.php index 43ccf87a..cae88e4c 100644 --- a/models/PropertyDoc.php +++ b/models/PropertyDoc.php @@ -9,6 +9,7 @@ use phpDocumentor\Reflection\DocBlock\Tags\Var_; use phpDocumentor\Reflection\Php\Property; +use yii\apidoc\helpers\PrettyPrinter; use yii\helpers\StringHelper; /** @@ -65,7 +66,10 @@ public function __construct($reflector = null, $context = null, $config = []) $this->visibility = $reflector->getVisibility(); $this->isStatic = $reflector->isStatic(); - $this->defaultValue = $reflector->getDefault(); + + if ($reflector->getDefault() !== null) { + $this->defaultValue = PrettyPrinter::getRepresentationOfValue($reflector->getDefaultNode()); + } $hasInheritdoc = false; foreach ($this->tags as $tag) { From a02626940641fbe4ec358e4109c60a80f5f8ca9d Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Thu, 11 Nov 2021 20:30:01 +0600 Subject: [PATCH 33/58] Fix higlighting of backslashes (temporary solution) --- helpers/MarkdownHighlightTrait.php | 11 +++++++++++ templates/html/ApiRenderer.php | 1 - 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/helpers/MarkdownHighlightTrait.php b/helpers/MarkdownHighlightTrait.php index 9433769a..9aa59af3 100644 --- a/helpers/MarkdownHighlightTrait.php +++ b/helpers/MarkdownHighlightTrait.php @@ -61,6 +61,12 @@ protected function renderCode($block) */ public static function highlight($code, $language) { + $restoreBackslashes = false; + if (strpos($code, '/') === false) { + $code = str_replace('\\', '/', $code); + $restoreBackslashes = true; + } + if ($language !== 'php') { return htmlspecialchars($code, ENT_NOQUOTES | ENT_SUBSTITUTE); } @@ -77,6 +83,11 @@ public static function highlight($code, $language) // remove \n and tags added by php $text = substr(trim($text), 36, -16); + if ($restoreBackslashes) { + $text = str_replace('/', '\\', $text); + $text = str_replace('<\span>', '', $text); + } + return $text; } } diff --git a/templates/html/ApiRenderer.php b/templates/html/ApiRenderer.php index 59deb6cd..3fdc35d4 100644 --- a/templates/html/ApiRenderer.php +++ b/templates/html/ApiRenderer.php @@ -16,7 +16,6 @@ use yii\base\ViewContextInterface; use yii\helpers\Console; use yii\helpers\Html; -use yii\helpers\StringHelper; use yii\web\AssetManager; use yii\web\View; use Yii; From 1cf8015500f521a24ee021f3591d1f0060c462f3 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Thu, 11 Nov 2021 20:30:22 +0600 Subject: [PATCH 34/58] Use namespace aliases for event names --- models/EventDoc.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/models/EventDoc.php b/models/EventDoc.php index 8916d431..a19f9aef 100644 --- a/models/EventDoc.php +++ b/models/EventDoc.php @@ -49,6 +49,8 @@ public function __construct($reflector = null, $context = null, $config = [], $d if (strpos($className, '\\') !== false) { $this->type = $className; + } elseif (isset($docBlock->getContext()->getNamespaceAliases()[$className])) { + $this->type = $docBlock->getContext()->getNamespaceAliases()[$className]; } else { $this->type = $docBlock->getContext()->getNamespace() . '\\' . $className; } From 1d2432d8cc07701c981ef891dfc7320dd76276bb Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 12 Nov 2021 18:08:36 +0600 Subject: [PATCH 35/58] Handle regression \u -> \\u in class constants' values --- models/ConstDoc.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/models/ConstDoc.php b/models/ConstDoc.php index 536a9120..3cb5af7d 100644 --- a/models/ConstDoc.php +++ b/models/ConstDoc.php @@ -8,6 +8,8 @@ namespace yii\apidoc\models; use phpDocumentor\Reflection\DocBlock; +use phpDocumentor\Reflection\Php\Constant; +use yii\apidoc\helpers\PrettyPrinter; /** * Represents API documentation information for a `constant`. @@ -22,7 +24,7 @@ class ConstDoc extends BaseDoc /** - * @param \phpDocumentor\Reflection\ClassReflector\ConstantReflector $reflector + * @param Constant $reflector * @param Context $context * @param array $config * @param DocBlock $docBlock @@ -35,6 +37,8 @@ public function __construct($reflector = null, $context = null, $config = [], $d return; } - $this->value = $reflector->getValue(); + if ($reflector->getValue() !== null) { + $this->value = PrettyPrinter::getRepresentationOfValue($reflector->getDefaultNode()); + } } } From 54987b9db41d12d5f6e5b4afdd49b59d74c72f64 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 12 Nov 2021 20:26:59 +0600 Subject: [PATCH 36/58] Revert hacky attempt of fixing highlighting of backslashes --- helpers/MarkdownHighlightTrait.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/helpers/MarkdownHighlightTrait.php b/helpers/MarkdownHighlightTrait.php index 9aa59af3..9433769a 100644 --- a/helpers/MarkdownHighlightTrait.php +++ b/helpers/MarkdownHighlightTrait.php @@ -61,12 +61,6 @@ protected function renderCode($block) */ public static function highlight($code, $language) { - $restoreBackslashes = false; - if (strpos($code, '/') === false) { - $code = str_replace('\\', '/', $code); - $restoreBackslashes = true; - } - if ($language !== 'php') { return htmlspecialchars($code, ENT_NOQUOTES | ENT_SUBSTITUTE); } @@ -83,11 +77,6 @@ public static function highlight($code, $language) // remove \n and tags added by php $text = substr(trim($text), 36, -16); - if ($restoreBackslashes) { - $text = str_replace('/', '\\', $text); - $text = str_replace('<\span>', '', $text); - } - return $text; } } From c0362358cb527681f80bfc90d353a771b4bd020e Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 12 Nov 2021 23:00:58 +0600 Subject: [PATCH 37/58] Fix name and type hint for params in method when it's declared via tag --- models/ParamDoc.php | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/models/ParamDoc.php b/models/ParamDoc.php index cb3b0fc1..750bcb97 100644 --- a/models/ParamDoc.php +++ b/models/ParamDoc.php @@ -39,19 +39,18 @@ public function __construct($reflector = null, $context = null, $config = []) { parent::__construct($config); - if ($reflector === null) { - return; + if ($reflector !== null) { + $this->name = $reflector->getName(); + $this->typeHint = (string) $reflector->getType(); + $this->defaultValue = $reflector->getDefault(); + $this->isOptional = $this->defaultValue !== null; + $this->isPassedByReference = $reflector->isByReference(); } - $this->name = '$'. $reflector->getName(); + $this->name = '$' . $this->name ; - $this->typeHint = (string) $reflector->getType(); if ($this->typeHint === 'mixed') { $this->typeHint = ''; } - - $this->defaultValue = $reflector->getDefault(); - $this->isOptional = $this->defaultValue !== null; - $this->isPassedByReference = $reflector->isByReference(); } } From 43c814bb2d278d872a8fd7029d2234b3b0203f26 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 15 Nov 2021 12:42:19 +0600 Subject: [PATCH 38/58] Fix as many warnings as possible, check docstrings --- commands/ApiController.php | 2 +- commands/GuideController.php | 2 +- composer.json | 4 ++++ helpers/ApiMarkdownLaTeX.php | 4 ---- helpers/ApiMarkdownTrait.php | 3 +-- templates/bootstrap/layouts/main.php | 1 + 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/commands/ApiController.php b/commands/ApiController.php index 9577a92a..6687d4d9 100644 --- a/commands/ApiController.php +++ b/commands/ApiController.php @@ -160,7 +160,7 @@ protected function findFiles($path, $except = []) /** * @inheritdoc - * @return ApiRenderer + * @return ApiRenderer|false */ protected function findRenderer($template) { diff --git a/commands/GuideController.php b/commands/GuideController.php index 6b2bf6ef..317cda28 100644 --- a/commands/GuideController.php +++ b/commands/GuideController.php @@ -124,7 +124,7 @@ protected function findFiles($path, $except = []) /** * @inheritdoc - * @return GuideRenderer + * @return GuideRenderer|false */ protected function findRenderer($template) { diff --git a/composer.json b/composer.json index 0e86e01c..54da68d4 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ ], "minimum-stability": "dev", "require": { + "ext-mbstring": "*", "php": "^7.2 || ^8.0", "yiisoft/yii2": "~2.0.16", "yiisoft/yii2-bootstrap": "~2.0.0", @@ -42,6 +43,9 @@ "url": "https://github.com/arogachev/reflection" } ], + "suggest": { + "ext-json": "for outputting documentation data structures as a JSON text" + }, "autoload": { "psr-4": { "yii\\apidoc\\": "" } }, diff --git a/helpers/ApiMarkdownLaTeX.php b/helpers/ApiMarkdownLaTeX.php index 8c111952..9d3dea09 100644 --- a/helpers/ApiMarkdownLaTeX.php +++ b/helpers/ApiMarkdownLaTeX.php @@ -68,10 +68,6 @@ protected function translateBlockType($type) */ protected function renderQuote($block) { - if (isset($block['blocktype'])) { - // TODO render nice icon for different block types: note, info, warning, tip - //$class = ' class="' . $block['blocktype'] . '"'; - } return '\begin{quote}' . $this->renderAbsy($block['content']) . "\\end{quote}\n"; } diff --git a/helpers/ApiMarkdownTrait.php b/helpers/ApiMarkdownTrait.php index aad3cc49..e80df9c0 100644 --- a/helpers/ApiMarkdownTrait.php +++ b/helpers/ApiMarkdownTrait.php @@ -24,7 +24,7 @@ trait ApiMarkdownTrait */ protected function parseApiLinks($text) { - if (!preg_match('/^\[\[([\w\d\\\\\(\):$]+)(\|[^\]]*)?\]\]/', $text, $matches)) { + if (!preg_match('/^\[\[([\w\d\\\\():$]+)(\|[^]]*)?]]/', $text, $matches)) { return [['text', '[['], 2]; } @@ -115,7 +115,6 @@ protected function parseApiLinkForContext($offset, $object, $title, $context) } } - /** @var $type TypeDoc */ $type = static::$renderer->apiContext->getType($typeName); diff --git a/templates/bootstrap/layouts/main.php b/templates/bootstrap/layouts/main.php index 9235120d..1d1673e6 100644 --- a/templates/bootstrap/layouts/main.php +++ b/templates/bootstrap/layouts/main.php @@ -6,6 +6,7 @@ use yii\helpers\StringHelper; /* @var $this yii\web\View */ +/* @var $content string */ \yii\apidoc\templates\bootstrap\assets\AssetBundle::register($this); From b8d58901c4e9d61d0b8fd7ad829e3d1b3335d9ae Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 15 Nov 2021 13:47:48 +0600 Subject: [PATCH 39/58] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8578f9a6..a9a4f0a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ Yii Framework 2 apidoc extension Change Log - Bug #210: Fixed invalid attempt to scan parent class of interface with `@inheritdoc` tag on a method (bizley) - Bug #218: Extended detection of `@inheritdoc` tag in `BaseDoc` (WinterSilence) +- Bug #203: Fix PHP 8 compatibility (bizley, arogachev) +- Enh #146: Update `nikic/php-parser` version (bizley, arogachev) +- Bug #213: Fix error: "Call to undefined method `phpDocumentor\Reflection\Php\Argument::getNode()`" (arogachev) 2.1.6 May 05, 2021 From 6895aa4272b8cc2a9e07b184f2bc95a0cdbe3e51 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Tue, 23 Nov 2021 11:16:25 +0600 Subject: [PATCH 40/58] Update renderers/BaseRenderer.php Co-authored-by: Alexander Makarov --- renderers/BaseRenderer.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/renderers/BaseRenderer.php b/renderers/BaseRenderer.php index b7de81c9..6fb34672 100644 --- a/renderers/BaseRenderer.php +++ b/renderers/BaseRenderer.php @@ -180,10 +180,9 @@ public function createSubjectLink($subject, $title = null, $options = []) } $link = $this->generateApiUrl($type->name); + $link .= '#' . $subject->name; if ($subject instanceof MethodDoc) { - $link .= '#' . $subject->name . '()'; - } else { - $link .= '#' . $subject->name; + $link .= '()'; } $link .= '-detail'; From c3eadfeac75893fee0dc4067d78e61b056b8e172 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Tue, 23 Nov 2021 11:18:37 +0600 Subject: [PATCH 41/58] Simplify string concatenation even more --- renderers/BaseRenderer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/renderers/BaseRenderer.php b/renderers/BaseRenderer.php index 6fb34672..fdc0f603 100644 --- a/renderers/BaseRenderer.php +++ b/renderers/BaseRenderer.php @@ -179,11 +179,11 @@ public function createSubjectLink($subject, $title = null, $options = []) return $subject->name; } - $link = $this->generateApiUrl($type->name); - $link .= '#' . $subject->name; + $link = $this->generateApiUrl($type->name) . '#' . $subject->name; if ($subject instanceof MethodDoc) { $link .= '()'; } + $link .= '-detail'; return $this->generateLink($title, $link, $options); From 8dd32385fb594178b40ac13c87a3de17011d47ba Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Tue, 23 Nov 2021 12:24:02 +0600 Subject: [PATCH 42/58] Issue #133 is fixed too [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9a4f0a2..3c96e171 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Yii Framework 2 apidoc extension Change Log - Bug #203: Fix PHP 8 compatibility (bizley, arogachev) - Enh #146: Update `nikic/php-parser` version (bizley, arogachev) - Bug #213: Fix error: "Call to undefined method `phpDocumentor\Reflection\Php\Argument::getNode()`" (arogachev) +- Bug #133: Fix PHP Parser error with anonymous class (arogachev) 2.1.6 May 05, 2021 From bb1d46507af5c30657d6d5f823d2e0f485aa5c76 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Tue, 23 Nov 2021 12:47:32 +0600 Subject: [PATCH 43/58] Issue #155 is fixed too [skip ci] --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c96e171..96326f00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ Yii Framework 2 apidoc extension Change Log =========================================== -2.1.7 under development +3.0.0 under development ----------------------- - Bug #210: Fixed invalid attempt to scan parent class of interface with `@inheritdoc` tag on a method (bizley) @@ -10,6 +10,7 @@ Yii Framework 2 apidoc extension Change Log - Enh #146: Update `nikic/php-parser` version (bizley, arogachev) - Bug #213: Fix error: "Call to undefined method `phpDocumentor\Reflection\Php\Argument::getNode()`" (arogachev) - Bug #133: Fix PHP Parser error with anonymous class (arogachev) +- Bug #155: Fix processing of classes containing constants with visibility (arogachev) 2.1.6 May 05, 2021 From 87b8d63dda14984504c559e70b4a0ecef133e510 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Tue, 23 Nov 2021 13:05:37 +0600 Subject: [PATCH 44/58] Issue #162 is fixed too [skip ci] --- CHANGELOG.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96326f00..13f12e65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,12 @@ Yii Framework 2 apidoc extension Change Log - Bug #210: Fixed invalid attempt to scan parent class of interface with `@inheritdoc` tag on a method (bizley) - Bug #218: Extended detection of `@inheritdoc` tag in `BaseDoc` (WinterSilence) -- Bug #203: Fix PHP 8 compatibility (bizley, arogachev) -- Enh #146: Update `nikic/php-parser` version (bizley, arogachev) -- Bug #213: Fix error: "Call to undefined method `phpDocumentor\Reflection\Php\Argument::getNode()`" (arogachev) -- Bug #133: Fix PHP Parser error with anonymous class (arogachev) -- Bug #155: Fix processing of classes containing constants with visibility (arogachev) +- Bug #203: Fixed PHP 8 compatibility (bizley, arogachev) +- Enh #146: Updated `nikic/php-parser` version (bizley, arogachev) +- Bug #213: Fixed error: "Call to undefined method `phpDocumentor\Reflection\Php\Argument::getNode()`" (arogachev) +- Bug #133: Fixed PHP Parser error with anonymous class (arogachev) +- Bug #155: Fixed processing of classes containing constants with visibility (arogachev) +- Bug #162: Fixed skipping some of PHP files / classes (arogachev) 2.1.6 May 05, 2021 From 21f8055798121d5e97796c2987165471d7bbed2d Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Tue, 23 Nov 2021 13:20:37 +0600 Subject: [PATCH 45/58] Issue #179 is fixed too [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13f12e65..05598d54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Yii Framework 2 apidoc extension Change Log - Bug #133: Fixed PHP Parser error with anonymous class (arogachev) - Bug #155: Fixed processing of classes containing constants with visibility (arogachev) - Bug #162: Fixed skipping some of PHP files / classes (arogachev) +- Bug #179: Fixed incorrect output when string type hint is used in method parameters (arogachev) 2.1.6 May 05, 2021 From bcc44fa9197e15bb58c774dc8f36698742641ccb Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Tue, 23 Nov 2021 18:20:50 +0600 Subject: [PATCH 46/58] Issue #199 is fixed too [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05598d54..30d49fea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Yii Framework 2 apidoc extension Change Log - Bug #155: Fixed processing of classes containing constants with visibility (arogachev) - Bug #162: Fixed skipping some of PHP files / classes (arogachev) - Bug #179: Fixed incorrect output when string type hint is used in method parameters (arogachev) +- Bug #199: Fixed processing of nullable return types (arogachev) 2.1.6 May 05, 2021 From 7e98c647dac2ff683e8e544f5b10146f543372ef Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Tue, 23 Nov 2021 19:48:00 +0600 Subject: [PATCH 47/58] Fix issue #143 (do not include internal methods and properties) [skip ci] --- models/TypeDoc.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/models/TypeDoc.php b/models/TypeDoc.php index a23bfdf6..b7b54b9e 100644 --- a/models/TypeDoc.php +++ b/models/TypeDoc.php @@ -251,7 +251,9 @@ public function __construct($reflector = null, $context = null, $config = []) $this->initProperties($reflector, $context); foreach ($reflector->getMethods() as $methodReflector) { - if ($methodReflector->getVisibility() != 'private') { + if ($methodReflector->getVisibility() != 'private' && + $methodReflector->getDocBlock() && + !$methodReflector->getDocBlock()->hasTag('internal')) { $method = new MethodDoc($methodReflector, $context, ['sourceFile' => $this->sourceFile]); $method->definedBy = $this->name; $this->methods[$method->name] = $method; @@ -259,10 +261,16 @@ public function __construct($reflector = null, $context = null, $config = []) } } + /** + * @param Class_ $reflector + * @param Context $context + */ protected function initProperties($reflector, $context) { foreach ($reflector->getProperties() as $propertyReflector) { - if ($propertyReflector->getVisibility() != 'private') { + if ($propertyReflector->getVisibility() != 'private' && + $propertyReflector->getDocBlock() && + !$propertyReflector->getDocBlock()->hasTag('internal')) { $property = new PropertyDoc($propertyReflector, $context, ['sourceFile' => $this->sourceFile]); $property->definedBy = $this->name; $this->properties[$property->name] = $property; From 255733f8d681922b84f99c079b6ec70dbd35a2f4 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Tue, 23 Nov 2021 19:57:40 +0600 Subject: [PATCH 48/58] Add issue #180 in CHANGELOG.md[skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30d49fea..2826409f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Yii Framework 2 apidoc extension Change Log - Bug #162: Fixed skipping some of PHP files / classes (arogachev) - Bug #179: Fixed incorrect output when string type hint is used in method parameters (arogachev) - Bug #199: Fixed processing of nullable return types (arogachev) +- Enh #180: Do not include methods and properties marked as internal (arogachev) 2.1.6 May 05, 2021 From f9bef8f076ba1c0886d409e7cf020096bab46329 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Wed, 24 Nov 2021 11:55:48 +0600 Subject: [PATCH 49/58] Add issue #148 to CHANGELOG.md [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2826409f..73cf3312 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Yii Framework 2 apidoc extension Change Log - Bug #179: Fixed incorrect output when string type hint is used in method parameters (arogachev) - Bug #199: Fixed processing of nullable return types (arogachev) - Enh #180: Do not include methods and properties marked as internal (arogachev) +- Bug #148: Fixed processing of code containing uniform variable syntax (arogachev) 2.1.6 May 05, 2021 From 5b2f0b71a784165175e1c44375fe3694e807e159 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Wed, 24 Nov 2021 13:50:26 +0600 Subject: [PATCH 50/58] Fixed issue #197 [skip ci] --- CHANGELOG.md | 1 + composer.json | 1 + templates/html/GuideRenderer.php | 11 +++++++++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73cf3312..e94a0ac7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Yii Framework 2 apidoc extension Change Log - Bug #199: Fixed processing of nullable return types (arogachev) - Enh #180: Do not include methods and properties marked as internal (arogachev) - Bug #148: Fixed processing of code containing uniform variable syntax (arogachev) +- Bug #197: Adapted fixing of Markdown links for multiple links (arogachev) 2.1.6 May 05, 2021 diff --git a/composer.json b/composer.json index 54da68d4..902efe15 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ ], "minimum-stability": "dev", "require": { + "ext-dom": "*", "ext-mbstring": "*", "php": "^7.2 || ^8.0", "yiisoft/yii2": "~2.0.16", diff --git a/templates/html/GuideRenderer.php b/templates/html/GuideRenderer.php index d94c2852..d490e7ad 100644 --- a/templates/html/GuideRenderer.php +++ b/templates/html/GuideRenderer.php @@ -7,6 +7,7 @@ namespace yii\apidoc\templates\html; +use DOMDocument; use yii\apidoc\helpers\ApiMarkdown; use yii\helpers\Console; use yii\apidoc\renderers\GuideRenderer as BaseGuideRenderer; @@ -163,8 +164,14 @@ public function getGuideReferences() */ protected function fixMarkdownLinks($content) { - $content = preg_replace('/href\s*=\s*"([^"\/]+)\.md(#.*)?"/i', 'href="' . $this->guidePrefix . '\1.html\2"', $content); - return $content; + $doc = new DOMDocument(); + $doc->loadHTML($content); + + foreach ($doc->getElementsByTagName('a') as $link) { + $link->setAttribute('href', str_replace('.md', '', $link->getAttribute('href'))); + } + + return $doc->saveHTML(); } /** From 7ceaa5abb1c4561908fb718bbf9992420d0d0b1d Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Wed, 24 Nov 2021 14:19:49 +0600 Subject: [PATCH 51/58] Add prefix and suffix when fixing Markdown links --- templates/html/GuideRenderer.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/html/GuideRenderer.php b/templates/html/GuideRenderer.php index d490e7ad..8cc5f5d0 100644 --- a/templates/html/GuideRenderer.php +++ b/templates/html/GuideRenderer.php @@ -168,7 +168,8 @@ protected function fixMarkdownLinks($content) $doc->loadHTML($content); foreach ($doc->getElementsByTagName('a') as $link) { - $link->setAttribute('href', str_replace('.md', '', $link->getAttribute('href'))); + $href = $this->guidePrefix . str_replace('.md', '.html', $link->getAttribute('href')); + $link->setAttribute('href', $href); } return $doc->saveHTML(); From bb94d190e232f2913ec6bcb68f10780945141292 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Thu, 25 Nov 2021 12:11:47 +0600 Subject: [PATCH 52/58] Do not add guide prefix to non-Markdown links --- templates/html/GuideRenderer.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/templates/html/GuideRenderer.php b/templates/html/GuideRenderer.php index 8cc5f5d0..73dda71e 100644 --- a/templates/html/GuideRenderer.php +++ b/templates/html/GuideRenderer.php @@ -168,7 +168,12 @@ protected function fixMarkdownLinks($content) $doc->loadHTML($content); foreach ($doc->getElementsByTagName('a') as $link) { - $href = $this->guidePrefix . str_replace('.md', '.html', $link->getAttribute('href')); + $href = $link->getAttribute('href'); + if (strpos($href, '.md') === false) { + continue; + } + + $href = $this->guidePrefix . str_replace('.md', '.html', $href); $link->setAttribute('href', $href); } From 5b068ef287b19e15b7c1487856895d0b45d9f4a9 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Tue, 23 Nov 2021 19:48:00 +0600 Subject: [PATCH 53/58] Extract changes for issue #180 to a separate branch --- CHANGELOG.md | 1 - models/TypeDoc.php | 12 ++---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84cc6daf..3bb65aa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,6 @@ Yii Framework 2 apidoc extension Change Log - Bug #162: Fixed skipping some of PHP files / classes (arogachev) - Bug #179: Fixed incorrect output when string type hint is used in method parameters (arogachev) - Bug #199: Fixed processing of nullable return types (arogachev) -- Enh #180: Do not include methods and properties marked as internal (arogachev) - Bug #148: Fixed processing of code containing uniform variable syntax (arogachev) - Bug #197: Adapted fixing of Markdown links for multiple links (arogachev) diff --git a/models/TypeDoc.php b/models/TypeDoc.php index b7b54b9e..a23bfdf6 100644 --- a/models/TypeDoc.php +++ b/models/TypeDoc.php @@ -251,9 +251,7 @@ public function __construct($reflector = null, $context = null, $config = []) $this->initProperties($reflector, $context); foreach ($reflector->getMethods() as $methodReflector) { - if ($methodReflector->getVisibility() != 'private' && - $methodReflector->getDocBlock() && - !$methodReflector->getDocBlock()->hasTag('internal')) { + if ($methodReflector->getVisibility() != 'private') { $method = new MethodDoc($methodReflector, $context, ['sourceFile' => $this->sourceFile]); $method->definedBy = $this->name; $this->methods[$method->name] = $method; @@ -261,16 +259,10 @@ public function __construct($reflector = null, $context = null, $config = []) } } - /** - * @param Class_ $reflector - * @param Context $context - */ protected function initProperties($reflector, $context) { foreach ($reflector->getProperties() as $propertyReflector) { - if ($propertyReflector->getVisibility() != 'private' && - $propertyReflector->getDocBlock() && - !$propertyReflector->getDocBlock()->hasTag('internal')) { + if ($propertyReflector->getVisibility() != 'private') { $property = new PropertyDoc($propertyReflector, $context, ['sourceFile' => $this->sourceFile]); $property->definedBy = $this->name; $this->properties[$property->name] = $property; From d97b25c758027bed4f0833ae5efb36d0918fa7a4 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Thu, 25 Nov 2021 12:31:39 +0600 Subject: [PATCH 54/58] Extract changes for issue #197 to a separate branch --- CHANGELOG.md | 3 +-- composer.json | 1 - templates/html/GuideRenderer.php | 17 ++--------------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bb65aa1..7b4b3205 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,7 @@ Yii Framework 2 apidoc extension Change Log - Bug #162: Fixed skipping some of PHP files / classes (arogachev) - Bug #179: Fixed incorrect output when string type hint is used in method parameters (arogachev) - Bug #199: Fixed processing of nullable return types (arogachev) -- Bug #148: Fixed processing of code containing uniform variable syntax (arogachev) -- Bug #197: Adapted fixing of Markdown links for multiple links (arogachev) +- Bug #148: Fixed processing of code containing uniform variable syntax (arogachev) 2.1.6 May 05, 2021 diff --git a/composer.json b/composer.json index 902efe15..54da68d4 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,6 @@ ], "minimum-stability": "dev", "require": { - "ext-dom": "*", "ext-mbstring": "*", "php": "^7.2 || ^8.0", "yiisoft/yii2": "~2.0.16", diff --git a/templates/html/GuideRenderer.php b/templates/html/GuideRenderer.php index 73dda71e..d94c2852 100644 --- a/templates/html/GuideRenderer.php +++ b/templates/html/GuideRenderer.php @@ -7,7 +7,6 @@ namespace yii\apidoc\templates\html; -use DOMDocument; use yii\apidoc\helpers\ApiMarkdown; use yii\helpers\Console; use yii\apidoc\renderers\GuideRenderer as BaseGuideRenderer; @@ -164,20 +163,8 @@ public function getGuideReferences() */ protected function fixMarkdownLinks($content) { - $doc = new DOMDocument(); - $doc->loadHTML($content); - - foreach ($doc->getElementsByTagName('a') as $link) { - $href = $link->getAttribute('href'); - if (strpos($href, '.md') === false) { - continue; - } - - $href = $this->guidePrefix . str_replace('.md', '.html', $href); - $link->setAttribute('href', $href); - } - - return $doc->saveHTML(); + $content = preg_replace('/href\s*=\s*"([^"\/]+)\.md(#.*)?"/i', 'href="' . $this->guidePrefix . '\1.html\2"', $content); + return $content; } /** From 0ec080091109dce139b114a54bc3921a63ae1629 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Thu, 25 Nov 2021 19:07:29 +0600 Subject: [PATCH 55/58] Remove whitespace at the end of line [skip ci] --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b4b3205..d4f6b146 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ Yii Framework 2 apidoc extension Change Log - Bug #162: Fixed skipping some of PHP files / classes (arogachev) - Bug #179: Fixed incorrect output when string type hint is used in method parameters (arogachev) - Bug #199: Fixed processing of nullable return types (arogachev) -- Bug #148: Fixed processing of code containing uniform variable syntax (arogachev) +- Bug #148: Fixed processing of code containing uniform variable syntax (arogachev) 2.1.6 May 05, 2021 From e1baa3de03650ed504903334d0406981ea1eadbe Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 29 Nov 2021 11:16:05 +0600 Subject: [PATCH 56/58] Fixed extracting of first sentence from the text containing backticks --- CHANGELOG.md | 1 + models/BaseDoc.php | 7 ++++++- tests/models/BaseDocTest.php | 25 +++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/models/BaseDocTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index d4f6b146..fe8d657a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Yii Framework 2 apidoc extension Change Log - Bug #179: Fixed incorrect output when string type hint is used in method parameters (arogachev) - Bug #199: Fixed processing of nullable return types (arogachev) - Bug #148: Fixed processing of code containing uniform variable syntax (arogachev) +- Bug #128: Fixed extracting of first sentence from the text containing backticks (arogachev) 2.1.6 May 05, 2021 diff --git a/models/BaseDoc.php b/models/BaseDoc.php index 2664368d..263d0eee 100644 --- a/models/BaseDoc.php +++ b/models/BaseDoc.php @@ -200,7 +200,12 @@ public static function extractFirstSentence($text) $sentence = mb_substr($text, 0, $pos + 1, 'utf-8'); if (mb_strlen($text, 'utf-8') >= $pos + 3) { $abbrev = mb_substr($text, $pos - 1, 4, 'utf-8'); - if ($abbrev === 'e.g.' || $abbrev === 'i.e.') { // do not break sentence after abbreviation + // do not break sentence after abbreviation + if ($abbrev === 'e.g.' || + $abbrev === 'i.e.' || + mb_substr_count($sentence, '`', 'utf-8') % 2 === 1 || + mb_substr_count($text, '`', 'utf-8') % 2 === 1 + ) { $sentence .= static::extractFirstSentence(mb_substr($text, $pos + 1, mb_strlen($text, 'utf-8'), 'utf-8')); } } diff --git a/tests/models/BaseDocTest.php b/tests/models/BaseDocTest.php new file mode 100644 index 00000000..a720edfd --- /dev/null +++ b/tests/models/BaseDocTest.php @@ -0,0 +1,25 @@ +assertEquals($initialText, $firstSentence);; + } +} From e04ed05da3668e9ecb3f464e0ff482d0d59f5ba1 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Wed, 1 Dec 2021 12:29:25 +0600 Subject: [PATCH 57/58] Sync CHANGELOG.md with master --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f567b8b..ce0e02c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ Yii Framework 2 apidoc extension Change Log - Bug #218: Extended detection of `@inheritdoc` tag in `BaseDoc` (WinterSilence) - Bug #180: Fixed "All Classes" broken link (arogachev) - Bug #34: Improved highlighting of PHP templates (arogachev) -- Bug #203: Fixed PHP 8 compatibility (bizley, arogachev) +- Bug #203: Add PHP 8 compatibility, raise minimum PHP version to 7.2 (bizley, arogachev) - Enh #146: Updated `nikic/php-parser` version (bizley, arogachev) - Bug #213: Fixed error: "Call to undefined method `phpDocumentor\Reflection\Php\Argument::getNode()`" (arogachev) - Bug #133: Fixed PHP Parser error with anonymous class (arogachev) @@ -18,7 +18,6 @@ Yii Framework 2 apidoc extension Change Log - Bug #199: Fixed processing of nullable return types (arogachev) - Bug #148: Fixed processing of code containing uniform variable syntax (arogachev) - Bug #128: Fixed extracting of first sentence from the text containing backticks (arogachev) -- Bug #34: Improved highlighting of PHP templates (arogachev) 2.1.6 May 05, 2021 From 0b23716203ba2e9a8e52649bd509c11a14c63353 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Wed, 1 Dec 2021 12:31:31 +0600 Subject: [PATCH 58/58] Sync PrettyPrinter.php with master --- helpers/PrettyPrinter.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/helpers/PrettyPrinter.php b/helpers/PrettyPrinter.php index 6f26262d..bf674781 100644 --- a/helpers/PrettyPrinter.php +++ b/helpers/PrettyPrinter.php @@ -51,13 +51,4 @@ public static function getRepresentationOfValue(Expr $value) return $printer->prettyPrintExpr($value); } - - /** - * https://github.com/nikic/PHP-Parser/issues/447#issuecomment-348557940 - * @param string $string - * @return string - */ - protected function pSingleQuotedString(string $string) { - return '\'' . preg_replace("/'|\\\\(?=[\\\\']|$)/", '\\\\$0', $string) . '\''; - } }