From b70f2b1f06df0767f68744e6934a703a4dd89a91 Mon Sep 17 00:00:00 2001 From: UndefinedOffset Date: Tue, 11 Jan 2022 14:48:23 -0400 Subject: [PATCH 1/8] Added support for pages using controller_name to define the controller used by ModelAsController --- src/Generators/ControllerTagGenerator.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/Generators/ControllerTagGenerator.php b/src/Generators/ControllerTagGenerator.php index b0bb8ed..52aebba 100644 --- a/src/Generators/ControllerTagGenerator.php +++ b/src/Generators/ControllerTagGenerator.php @@ -2,13 +2,16 @@ namespace SilverLeague\IDEAnnotator\Generators; +use Page; use ReflectionClass; use ReflectionException; use SilverStripe\CMS\Controllers\ContentController; use SilverStripe\Core\ClassInfo; +use SilverStripe\Core\Config\Config; class ControllerTagGenerator extends AbstractTagGenerator { + private static $pageClassesCache = []; /** * @return void @@ -39,6 +42,26 @@ protected function generateControllerObjectTags() if ($pageClassname !== 'Page') { $this->pushMixinTag($pageClassname); } + } else if ($this->isContentController($this->className)) { + if (empty(self::$pageClassesCache)) { + self::$pageClassesCache = ClassInfo::subclassesFor(Page::class); + } + + foreach (self::$pageClassesCache as $pageClassname) { + if (Config::inst()->get($pageClassname, 'controller_name') == $this->className) { + $pageClassname = $this->getAnnotationClassName($pageClassname); + + $this->pushPropertyTag($pageClassname . ' dataRecord'); + $this->pushMethodTag('data()', $pageClassname . ' data()'); + + // don't mixin Page, since this is a ContentController method + if ($pageClassname !== 'Page') { + $this->pushMixinTag($pageClassname); + } + + break; + } + } } } From 2a1b95d6995d5442f8508b30d687f616028bb6ba Mon Sep 17 00:00:00 2001 From: UndefinedOffset Date: Tue, 25 Jan 2022 12:24:48 -0400 Subject: [PATCH 2/8] Switched to phpdocumentor/reflection-docblock ^5.2 to address conflicts with PHPUnit 9.5 supported by Silverstripe 4.10.0 --- composer.json | 4 ++-- src/Generators/AbstractTagGenerator.php | 28 +++++++++++++++++++---- src/Generators/DocBlockGenerator.php | 26 +++++++++++++-------- src/Generators/OrmTagGenerator.php | 2 +- src/Helpers/AnnotatePermissionChecker.php | 2 +- tests/unit/DataObjectAnnotatorTest.php | 4 ++-- 6 files changed, 45 insertions(+), 21 deletions(-) diff --git a/composer.json b/composer.json index 2bdf581..e7a26b9 100644 --- a/composer.json +++ b/composer.json @@ -22,9 +22,9 @@ } ], "require": { - "php": ">=5.6.0", + "php": "^7.2 || ^8.0", "silverstripe/framework": "^4", - "phpdocumentor/reflection-docblock": "^2.0@dev" + "phpdocumentor/reflection-docblock": "^5.2" }, "require-dev": { "scriptfusion/phpunit-immediate-exception-printer": "^1", diff --git a/src/Generators/AbstractTagGenerator.php b/src/Generators/AbstractTagGenerator.php index 2eb336a..4621f46 100644 --- a/src/Generators/AbstractTagGenerator.php +++ b/src/Generators/AbstractTagGenerator.php @@ -2,15 +2,20 @@ namespace SilverLeague\IDEAnnotator\Generators; -use Generator; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\DocBlock\StandardTagFactory; use phpDocumentor\Reflection\DocBlock\Tag; -use ReflectionClass; -use ReflectionException; +use phpDocumentor\Reflection\DocBlockFactory; +use phpDocumentor\Reflection\FqsenResolver; +use phpDocumentor\Reflection\TypeResolver; use SilverLeague\IDEAnnotator\DataObjectAnnotator; use SilverStripe\Core\ClassInfo; use SilverStripe\Core\Config\Config; use SilverStripe\Core\Extension; use SilverStripe\Core\Injector\Injector; +use Generator; +use ReflectionClass; +use ReflectionException; /** * AbstractTagGenerator @@ -43,6 +48,11 @@ abstract class AbstractTagGenerator */ protected $tags = []; + /** + * @var StandardTagFactory + */ + protected $tagFactory; + /** * DocBlockTagGenerator constructor. * @@ -57,6 +67,13 @@ public function __construct($className, $existingTags) $this->reflector = new ReflectionClass($className); $this->tags = $this->getSupportedTagTypes(); + //Init the tag factory + $fqsenResolver = new FqsenResolver(); + $this->tagFactory = new StandardTagFactory($fqsenResolver); + $descriptionFactory = new DescriptionFactory($this->tagFactory); + $this->tagFactory->addService($descriptionFactory); + $this->tagFactory->addService(new TypeResolver($fqsenResolver)); + $this->generateTags(); } @@ -143,9 +160,10 @@ protected function pushMixinTag($tagString) */ protected function pushTagWithExistingComment($type, $tagString) { + $tagString = '@' . $type . ' ' . $tagString; $tagString .= $this->getExistingTagCommentByTagString($tagString); - return new Tag($type, $tagString); + return $this->tagFactory->create($tagString); } /** @@ -155,7 +173,7 @@ protected function pushTagWithExistingComment($type, $tagString) public function getExistingTagCommentByTagString($tagString) { foreach ($this->getExistingTags() as $tag) { - $content = $tag->getContent(); + $content = $tag->__toString(); // A tag should be followed by a space before it's description // This is to prevent `TestThing` and `Test` to be seen as the same, when the shorter // is after the longer name diff --git a/src/Generators/DocBlockGenerator.php b/src/Generators/DocBlockGenerator.php index 1483681..f703897 100644 --- a/src/Generators/DocBlockGenerator.php +++ b/src/Generators/DocBlockGenerator.php @@ -2,14 +2,15 @@ namespace SilverLeague\IDEAnnotator\Generators; -use InvalidArgumentException; -use LogicException; use phpDocumentor\Reflection\DocBlock; use phpDocumentor\Reflection\DocBlock\Serializer; use phpDocumentor\Reflection\DocBlock\Tag; +use phpDocumentor\Reflection\DocBlockFactory; +use SilverStripe\Control\Controller; +use InvalidArgumentException; +use LogicException; use ReflectionClass; use ReflectionException; -use SilverStripe\Control\Controller; /** * Class DocBlockGenerator @@ -34,6 +35,11 @@ class DocBlockGenerator */ protected $tagGenerator; + /** + * @var DocBlockFactory + */ + protected $docBlockFactory; + /** * DocBlockGenerator constructor. * @@ -45,6 +51,7 @@ public function __construct($className) { $this->className = $className; $this->reflector = new ReflectionClass($className); + $this->docBlockFactory = DocBlockFactory::createInstance(); $generatorClass = $this->reflector->isSubclassOf(Controller::class) ? ControllerTagGenerator::class : OrmTagGenerator::class; @@ -59,7 +66,7 @@ public function __construct($className) public function getExistingTags() { $docBlock = $this->getExistingDocBlock(); - $docBlock = new DocBlock($docBlock); + $docBlock = $this->docBlockFactory->create($docBlock); return $docBlock->getTags(); } @@ -97,15 +104,14 @@ public function getGeneratedDocBlock() */ protected function mergeGeneratedTagsIntoDocBlock($existingDocBlock) { - $docBlock = new DocBlock($this->removeExistingSupportedTags($existingDocBlock)); + $docBlock = $this->docBlockFactory->create($existingDocBlock); - if (!$docBlock->getText()) { - $docBlock->setText('Class \\' . $this->className); + $summary = $docBlock->getSummary(); + if (!$summary) { + $summary = 'Class \\' . $this->className; } - foreach ($this->getGeneratedTags() as $tag) { - $docBlock->appendTag($tag); - } + $docBlock = new DocBlock($summary, $docBlock->getDescription(), $this->getGeneratedTags()); $serializer = new Serializer(); $docBlock = $serializer->getDocComment($docBlock); diff --git a/src/Generators/OrmTagGenerator.php b/src/Generators/OrmTagGenerator.php index e1ced05..e4bb5c3 100644 --- a/src/Generators/OrmTagGenerator.php +++ b/src/Generators/OrmTagGenerator.php @@ -44,7 +44,7 @@ class OrmTagGenerator extends AbstractTagGenerator */ protected static $dbfield_tagnames = [ DBInt::class => 'int', - DBBoolean::class => 'boolean', + DBBoolean::class => 'bool', DBFloat::class => 'float', DBDecimal::class => 'float', ]; diff --git a/src/Helpers/AnnotatePermissionChecker.php b/src/Helpers/AnnotatePermissionChecker.php index 77f2a16..dbcb960 100644 --- a/src/Helpers/AnnotatePermissionChecker.php +++ b/src/Helpers/AnnotatePermissionChecker.php @@ -120,7 +120,7 @@ public function classNameIsSupported($className) */ public function moduleIsAllowed($moduleName) { - return in_array($moduleName, $this->enabledModules(), null); + return in_array($moduleName, $this->enabledModules(), false); } /** diff --git a/tests/unit/DataObjectAnnotatorTest.php b/tests/unit/DataObjectAnnotatorTest.php index bf1bb35..4bf2382 100644 --- a/tests/unit/DataObjectAnnotatorTest.php +++ b/tests/unit/DataObjectAnnotatorTest.php @@ -204,7 +204,7 @@ public function testInversePlayerRelationOfTeam() $content = $this->annotator->getGeneratedFileContent(file_get_contents($filePath), Player::class); - $this->assertContains('@property boolean $IsRetired', $content); + $this->assertContains('@property bool $IsRetired', $content); $this->assertContains('@property string $ShirtNumber', $content); $this->assertContains('@property string $Shirt', $content); $this->assertContains('@property int $FavouriteTeamID', $content); @@ -277,7 +277,7 @@ public function testShortInversePlayerRelationOfTeam() $content = $this->annotator->getGeneratedFileContent(file_get_contents($filePath), Player::class); - $this->assertContains('@property boolean $IsRetired', $content); + $this->assertContains('@property bool $IsRetired', $content); $this->assertContains('@property string $ShirtNumber', $content); $this->assertContains('@property int $FavouriteTeamID', $content); $this->assertContains('@method Team FavouriteTeam()', $content); From 35a47c179c9db645174cff4a409b763058a8fd05 Mon Sep 17 00:00:00 2001 From: UndefinedOffset Date: Fri, 4 Feb 2022 10:06:05 -0400 Subject: [PATCH 3/8] Fixed crash when generating a new doc block --- src/Generators/DocBlockGenerator.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Generators/DocBlockGenerator.php b/src/Generators/DocBlockGenerator.php index f703897..efcd1f7 100644 --- a/src/Generators/DocBlockGenerator.php +++ b/src/Generators/DocBlockGenerator.php @@ -66,6 +66,10 @@ public function __construct($className) public function getExistingTags() { $docBlock = $this->getExistingDocBlock(); + if (!$docBlock) { + return []; + } + $docBlock = $this->docBlockFactory->create($docBlock); return $docBlock->getTags(); @@ -104,7 +108,7 @@ public function getGeneratedDocBlock() */ protected function mergeGeneratedTagsIntoDocBlock($existingDocBlock) { - $docBlock = $this->docBlockFactory->create($existingDocBlock); + $docBlock = $this->docBlockFactory->create(($existingDocBlock ?: "/**\n*/")); $summary = $docBlock->getSummary(); if (!$summary) { From c9b80525cda0305419e2433df9b7189c56636c91 Mon Sep 17 00:00:00 2001 From: UndefinedOffset Date: Mon, 11 Apr 2022 11:11:07 -0300 Subject: [PATCH 4/8] Rewrote some concatenation into sprintf statements Moved pageClassesCache into the AbstractTagGenerator class and made it protected --- src/Generators/AbstractTagGenerator.php | 6 ++++-- src/Generators/ControllerTagGenerator.php | 10 ++++------ src/Generators/DocBlockGenerator.php | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Generators/AbstractTagGenerator.php b/src/Generators/AbstractTagGenerator.php index 4621f46..a52deb4 100644 --- a/src/Generators/AbstractTagGenerator.php +++ b/src/Generators/AbstractTagGenerator.php @@ -53,6 +53,8 @@ abstract class AbstractTagGenerator */ protected $tagFactory; + protected static $pageClassesCache = []; + /** * DocBlockTagGenerator constructor. * @@ -160,7 +162,7 @@ protected function pushMixinTag($tagString) */ protected function pushTagWithExistingComment($type, $tagString) { - $tagString = '@' . $type . ' ' . $tagString; + $tagString = sprintf('@%s %s', $type, $tagString); $tagString .= $this->getExistingTagCommentByTagString($tagString); return $this->tagFactory->create($tagString); @@ -209,7 +211,7 @@ protected function generateOwnerTags() if (Injector::inst()->get($this->className) instanceof Extension) { $owners = iterator_to_array($this->getOwnerClasses($className)); $owners[] = $this->className; - $tagString = '\\' . implode("|\\", array_values($owners)) . ' $owner'; + $tagString = sprintf('\\%s $owner', implode("|\\", array_values($owners))); if (DataObjectAnnotator::config()->get('use_short_name')) { foreach ($owners as $key => $owner) { $owners[$key] = $this->getAnnotationClassName($owner); diff --git a/src/Generators/ControllerTagGenerator.php b/src/Generators/ControllerTagGenerator.php index 52aebba..d10d611 100644 --- a/src/Generators/ControllerTagGenerator.php +++ b/src/Generators/ControllerTagGenerator.php @@ -11,8 +11,6 @@ class ControllerTagGenerator extends AbstractTagGenerator { - private static $pageClassesCache = []; - /** * @return void * @throws ReflectionException @@ -35,8 +33,8 @@ protected function generateControllerObjectTags() if (class_exists($pageClassname) && $this->isContentController($this->className)) { $pageClassname = $this->getAnnotationClassName($pageClassname); - $this->pushPropertyTag($pageClassname . ' dataRecord'); - $this->pushMethodTag('data()', $pageClassname . ' data()'); + $this->pushPropertyTag(sprintf('%s dataRecord', $pageClassname)); + $this->pushMethodTag('data()', sprintf('%s data()', $pageClassname)); // don't mixin Page, since this is a ContentController method if ($pageClassname !== 'Page') { @@ -51,8 +49,8 @@ protected function generateControllerObjectTags() if (Config::inst()->get($pageClassname, 'controller_name') == $this->className) { $pageClassname = $this->getAnnotationClassName($pageClassname); - $this->pushPropertyTag($pageClassname . ' dataRecord'); - $this->pushMethodTag('data()', $pageClassname . ' data()'); + $this->pushPropertyTag(sprintf('%s dataRecord', $pageClassname)); + $this->pushMethodTag('data()', sprintf('%s data()', $pageClassname)); // don't mixin Page, since this is a ContentController method if ($pageClassname !== 'Page') { diff --git a/src/Generators/DocBlockGenerator.php b/src/Generators/DocBlockGenerator.php index efcd1f7..6bfb91d 100644 --- a/src/Generators/DocBlockGenerator.php +++ b/src/Generators/DocBlockGenerator.php @@ -69,7 +69,7 @@ public function getExistingTags() if (!$docBlock) { return []; } - + $docBlock = $this->docBlockFactory->create($docBlock); return $docBlock->getTags(); @@ -112,7 +112,7 @@ protected function mergeGeneratedTagsIntoDocBlock($existingDocBlock) $summary = $docBlock->getSummary(); if (!$summary) { - $summary = 'Class \\' . $this->className; + $summary = sprintf('Class \\%s', $this->className); } $docBlock = new DocBlock($summary, $docBlock->getDescription(), $this->getGeneratedTags()); From 6e801270b96b2be064ef8c83ffaae0e35d7276f8 Mon Sep 17 00:00:00 2001 From: UndefinedOffset Date: Mon, 11 Apr 2022 11:22:20 -0300 Subject: [PATCH 5/8] Moved to pre-caching the mapping of Page type to controller when using controller_name on the page type --- src/Generators/ControllerTagGenerator.php | 59 +++++++++++++++-------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/src/Generators/ControllerTagGenerator.php b/src/Generators/ControllerTagGenerator.php index d10d611..a5b3166 100644 --- a/src/Generators/ControllerTagGenerator.php +++ b/src/Generators/ControllerTagGenerator.php @@ -2,15 +2,28 @@ namespace SilverLeague\IDEAnnotator\Generators; -use Page; -use ReflectionClass; -use ReflectionException; use SilverStripe\CMS\Controllers\ContentController; use SilverStripe\Core\ClassInfo; use SilverStripe\Core\Config\Config; +use Page; +use ReflectionClass; class ControllerTagGenerator extends AbstractTagGenerator { + /** + * ControllerTagGenerator constructor. + * + * @param string $className + * @param $existingTags + * @throws ReflectionException + */ + public function __construct($className, $existingTags) + { + $this->mapPageTypesToControllerName(); + + parent::__construct($className, $existingTags); + } + /** * @return void * @throws ReflectionException @@ -40,25 +53,15 @@ protected function generateControllerObjectTags() if ($pageClassname !== 'Page') { $this->pushMixinTag($pageClassname); } - } else if ($this->isContentController($this->className)) { - if (empty(self::$pageClassesCache)) { - self::$pageClassesCache = ClassInfo::subclassesFor(Page::class); - } - - foreach (self::$pageClassesCache as $pageClassname) { - if (Config::inst()->get($pageClassname, 'controller_name') == $this->className) { - $pageClassname = $this->getAnnotationClassName($pageClassname); + } elseif ($this->isContentController($this->className) && array_key_exists($this->className, self::$pageClassesCache)) { + $pageClassname = $this->getAnnotationClassName(self::$pageClassesCache[$this->className]); - $this->pushPropertyTag(sprintf('%s dataRecord', $pageClassname)); - $this->pushMethodTag('data()', sprintf('%s data()', $pageClassname)); - - // don't mixin Page, since this is a ContentController method - if ($pageClassname !== 'Page') { - $this->pushMixinTag($pageClassname); - } + $this->pushPropertyTag(sprintf('%s dataRecord', $pageClassname)); + $this->pushMethodTag('data()', sprintf('%s data()', $pageClassname)); - break; - } + // don't mixin Page, since this is a ContentController method + if ($pageClassname !== 'Page') { + $this->pushMixinTag($pageClassname); } } } @@ -75,4 +78,20 @@ protected function isContentController($className) return ClassInfo::exists(ContentController::class) && $reflector->isSubclassOf(ContentController::class); } + + /** + * Generates the cache of Page types to Controllers when the controller_name config is used + */ + protected function mapPageTypesToControllerName() + { + if (empty(self::$pageClassesCache)) { + $pageClasses = ClassInfo::subclassesFor(Page::class); + foreach ($pageClasses as $pageClassname) { + $controllerName = Config::inst()->get($pageClassname, 'controller_name'); + if (!empty($controllerName)) { + self::$pageClassesCache[$controllerName] = $pageClassname; + } + } + } + } } From 7d61e47bdbab3f187b6bca2e027e208a545aa084 Mon Sep 17 00:00:00 2001 From: UndefinedOffset Date: Tue, 12 Apr 2022 09:35:44 -0300 Subject: [PATCH 6/8] Added note for windows users about the line endings in the generated documentation --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 56dcde2..9aa89e6 100644 --- a/README.md +++ b/README.md @@ -86,4 +86,6 @@ For further documentation information, see the [docs](docs/en/Index.md) This module changes the content of your files and currently there is no backup functionality. PHPStorm has a Local history for files and of course you have your code version controlled... I tried to add complete UnitTests, but I can't garantuee every situation is covered. +Windows users should be aware that the PHP Docs are generated with PSR in mind and use \n for line endings rather than Window's \r\n, some editors may have a hard time with these line endings. + This module should **never** be installed on a production environment. From 6b4282ecf99364a48da6a223c4ded64a49892c12 Mon Sep 17 00:00:00 2001 From: UndefinedOffset Date: Fri, 6 May 2022 12:09:45 -0300 Subject: [PATCH 7/8] Added new ShortNameResolver that is a pass-through to allow the DataObjectAnnotator.use_short_name config to work with the new version of reflection docblock --- src/Generators/AbstractTagGenerator.php | 8 +++++++- src/Reflection/ShortNameResolver.php | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/Reflection/ShortNameResolver.php diff --git a/src/Generators/AbstractTagGenerator.php b/src/Generators/AbstractTagGenerator.php index a52deb4..aa0a40f 100644 --- a/src/Generators/AbstractTagGenerator.php +++ b/src/Generators/AbstractTagGenerator.php @@ -16,6 +16,7 @@ use Generator; use ReflectionClass; use ReflectionException; +use SilverLeague\IDEAnnotator\Reflection\ShortNameResolver; /** * AbstractTagGenerator @@ -70,7 +71,12 @@ public function __construct($className, $existingTags) $this->tags = $this->getSupportedTagTypes(); //Init the tag factory - $fqsenResolver = new FqsenResolver(); + if (DataObjectAnnotator::config()->get('use_short_name')) { + $fqsenResolver = new ShortNameResolver(); + } else { + $fqsenResolver = new FqsenResolver(); + } + $this->tagFactory = new StandardTagFactory($fqsenResolver); $descriptionFactory = new DescriptionFactory($this->tagFactory); $this->tagFactory->addService($descriptionFactory); diff --git a/src/Reflection/ShortNameResolver.php b/src/Reflection/ShortNameResolver.php new file mode 100644 index 0000000..c26f464 --- /dev/null +++ b/src/Reflection/ShortNameResolver.php @@ -0,0 +1,19 @@ + Date: Fri, 5 Aug 2022 10:03:10 -0300 Subject: [PATCH 8/8] Fixed compatibility issue with PHP 8.1 --- src/Generators/AbstractTagGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Generators/AbstractTagGenerator.php b/src/Generators/AbstractTagGenerator.php index aa0a40f..7f6d66b 100644 --- a/src/Generators/AbstractTagGenerator.php +++ b/src/Generators/AbstractTagGenerator.php @@ -113,7 +113,7 @@ abstract protected function generateTags(); */ public function getTags() { - return (array)call_user_func_array('array_merge', $this->tags); + return (array)call_user_func_array('array_merge', array_values($this->tags)); } /**