Repository: dereuromark/cakephp-ide-helper Branch: master Commit: 2844e5a12782 Files: 478 Total size: 935.6 KB Directory structure: gitextract_wbx2q4tc/ ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── FUNDING.yml │ ├── dependabot.yml │ └── workflows/ │ ├── ci.yml │ └── deploy-docs.yml ├── .gitignore ├── LICENSE ├── README.md ├── annotate-watcher.cjs ├── composer.json ├── config/ │ └── app.example.php ├── docs/ │ ├── .vitepress/ │ │ ├── config.ts │ │ └── theme/ │ │ ├── custom.css │ │ └── index.ts │ ├── annotations/ │ │ ├── callbacks.md │ │ ├── classes.md │ │ ├── commands.md │ │ ├── controllers.md │ │ ├── custom-class-annotator.md │ │ ├── index.md │ │ ├── models.md │ │ ├── operations.md │ │ ├── templates.md │ │ └── view.md │ ├── code-completion/ │ │ └── index.md │ ├── generator/ │ │ ├── custom-tasks.md │ │ ├── index.md │ │ ├── operations.md │ │ └── tasks.md │ ├── guide/ │ │ ├── ide-support.md │ │ ├── index.md │ │ ├── installation.md │ │ ├── migration.md │ │ └── usage.md │ ├── illuminator/ │ │ └── index.md │ ├── index.md │ ├── package.json │ └── reference/ │ ├── configuration.md │ └── contributing.md ├── phpcs.xml ├── phpstan.neon ├── phpunit.xml.dist ├── src/ │ ├── Annotation/ │ │ ├── AbstractAnnotation.php │ │ ├── AnnotationFactory.php │ │ ├── AnnotationInterface.php │ │ ├── ExtendsAnnotation.php │ │ ├── LinkAnnotation.php │ │ ├── MethodAnnotation.php │ │ ├── MixinAnnotation.php │ │ ├── ParamAnnotation.php │ │ ├── PropertyAnnotation.php │ │ ├── PropertyReadAnnotation.php │ │ ├── ReplacableAnnotationInterface.php │ │ ├── SeeAnnotation.php │ │ ├── UsesAnnotation.php │ │ └── VariableAnnotation.php │ ├── Annotator/ │ │ ├── AbstractAnnotator.php │ │ ├── CallbackAnnotator.php │ │ ├── CallbackAnnotatorTask/ │ │ │ ├── AbstractCallbackAnnotatorTask.php │ │ │ ├── CallbackAnnotatorTaskInterface.php │ │ │ ├── TableCallbackAnnotatorTask.php │ │ │ └── VirtualFieldCallbackAnnotatorTask.php │ │ ├── CallbackAnnotatorTaskCollection.php │ │ ├── ClassAnnotator.php │ │ ├── ClassAnnotatorTask/ │ │ │ ├── AbstractClassAnnotatorTask.php │ │ │ ├── ClassAnnotatorTaskInterface.php │ │ │ ├── FormClassAnnotatorTask.php │ │ │ ├── MailerClassAnnotatorTask.php │ │ │ ├── ModelAwareClassAnnotatorTask.php │ │ │ ├── PathAwareClassAnnotatorTaskInterface.php │ │ │ ├── TableFindAnnotatorTask.php │ │ │ ├── TableFindNodeVisitor.php │ │ │ └── TestClassAnnotatorTask.php │ │ ├── ClassAnnotatorTaskCollection.php │ │ ├── CommandAnnotator.php │ │ ├── ComponentAnnotator.php │ │ ├── ControllerAnnotator.php │ │ ├── EntityAnnotator.php │ │ ├── HelperAnnotator.php │ │ ├── ModelAnnotator.php │ │ ├── Template/ │ │ │ └── VariableExtractor.php │ │ ├── TemplateAnnotator.php │ │ ├── Traits/ │ │ │ ├── ComponentTrait.php │ │ │ ├── DocBlockTrait.php │ │ │ ├── FileTrait.php │ │ │ ├── HelperTrait.php │ │ │ ├── ModelTrait.php │ │ │ └── UseStatementsTrait.php │ │ └── ViewAnnotator.php │ ├── CodeCompletion/ │ │ ├── CodeCompletionGenerator.php │ │ ├── Task/ │ │ │ ├── BehaviorTask.php │ │ │ ├── ControllerEventsTask.php │ │ │ ├── ModelEventsTask.php │ │ │ ├── SelectQueryTask.php │ │ │ ├── TaskInterface.php │ │ │ └── ViewEventsTask.php │ │ └── TaskCollection.php │ ├── Command/ │ │ ├── Annotate/ │ │ │ ├── AllCommand.php │ │ │ ├── CallbacksCommand.php │ │ │ ├── ClassesCommand.php │ │ │ ├── CommandsCommand.php │ │ │ ├── ComponentsCommand.php │ │ │ ├── ControllersCommand.php │ │ │ ├── HelpersCommand.php │ │ │ ├── ModelsCommand.php │ │ │ ├── TemplatesCommand.php │ │ │ └── ViewCommand.php │ │ ├── AnnotateCommand.php │ │ ├── Command.php │ │ ├── GenerateCodeCompletionCommand.php │ │ ├── GeneratePhpStormMetaCommand.php │ │ └── IlluminateCommand.php │ ├── Console/ │ │ └── Io.php │ ├── Filesystem/ │ │ └── Folder.php │ ├── Generator/ │ │ ├── Directive/ │ │ │ ├── BaseDirective.php │ │ │ ├── ExitPoint.php │ │ │ ├── ExpectedArguments.php │ │ │ ├── ExpectedReturnValues.php │ │ │ ├── Override.php │ │ │ └── RegisterArgumentsSet.php │ │ ├── GeneratorInterface.php │ │ ├── PhpstormGenerator.php │ │ ├── Task/ │ │ │ ├── BehaviorTask.php │ │ │ ├── CacheTask.php │ │ │ ├── CellTask.php │ │ │ ├── ComponentTask.php │ │ │ ├── ConfigureTask.php │ │ │ ├── ConnectionTask.php │ │ │ ├── ConsoleHelperTask.php │ │ │ ├── ConsoleTask.php │ │ │ ├── DatabaseTableColumnNameTask.php │ │ │ ├── DatabaseTableColumnTypeTask.php │ │ │ ├── DatabaseTableTask.php │ │ │ ├── DatabaseTypeTask.php │ │ │ ├── ElementTask.php │ │ │ ├── EntityTask.php │ │ │ ├── EnvTask.php │ │ │ ├── FixtureTask.php │ │ │ ├── FormHelperTask.php │ │ │ ├── HelperTask.php │ │ │ ├── LayoutTask.php │ │ │ ├── MailerTask.php │ │ │ ├── ModelTask.php │ │ │ ├── PluginTask.php │ │ │ ├── RequestTask.php │ │ │ ├── RoutePathTask.php │ │ │ ├── TableAssociationTask.php │ │ │ ├── TableFinderTask.php │ │ │ ├── TaskInterface.php │ │ │ ├── TranslationKeyTask.php │ │ │ └── ValidationTask.php │ │ └── TaskCollection.php │ ├── IdeHelperPlugin.php │ ├── Illuminator/ │ │ ├── Illuminator.php │ │ ├── Task/ │ │ │ ├── AbstractTask.php │ │ │ ├── ControllerDefaultTableTask.php │ │ │ ├── EntityFieldTask.php │ │ │ └── TableValidationLinkTask.php │ │ └── TaskCollection.php │ ├── Utility/ │ │ ├── App.php │ │ ├── AppPath.php │ │ ├── CollectionClass.php │ │ ├── ControllerActionParser.php │ │ ├── GenericString.php │ │ ├── Plugin.php │ │ ├── PluginPath.php │ │ └── TranslationParser.php │ ├── ValueObject/ │ │ ├── ArgumentsSet.php │ │ ├── ClassName.php │ │ ├── KeyValue.php │ │ ├── LiteralName.php │ │ ├── StringName.php │ │ └── ValueObjectInterface.php │ └── View/ │ └── Helper/ │ └── DocBlockHelper.php └── tests/ ├── Fixture/ │ ├── BarBarsFixture.php │ ├── CarsFixture.php │ ├── FoosFixture.php │ ├── HousesFixture.php │ ├── WheelsFixture.php │ └── WindowsFixture.php ├── TestCase/ │ ├── Annotation/ │ │ ├── AnnotationFactoryTest.php │ │ ├── ExtendsAnnotationTest.php │ │ ├── MethodAnnotationTest.php │ │ ├── MixinAnnotationTest.php │ │ ├── ParamAnnotationTest.php │ │ ├── PropertyAnnotationTest.php │ │ ├── UsesAnnotationTest.php │ │ └── VariableAnnotationTest.php │ ├── Annotator/ │ │ ├── CallbackAnnotatorTask/ │ │ │ └── VirtualFieldCallbackAnnotatorTaskTest.php │ │ ├── CallbackAnnotatorTest.php │ │ ├── ClassAnnotatorTask/ │ │ │ ├── FormClassAnnotatorTaskTest.php │ │ │ ├── MailerClassAnnotatorTaskTest.php │ │ │ ├── TableFindAnnotatorTaskTest.php │ │ │ └── TestClassAnnotatorTaskTest.php │ │ ├── ClassAnnotatorTest.php │ │ ├── CommandAnnotatorTest.php │ │ ├── ComponentAnnotatorTest.php │ │ ├── ControllerAnnotatorTest.php │ │ ├── DiffHelperTrait.php │ │ ├── EntityAnnotatorTest.php │ │ ├── HelperAnnotatorTest.php │ │ ├── ModelAnnotatorSpecificTest.php │ │ ├── ModelAnnotatorTest.php │ │ ├── Template/ │ │ │ └── VariableExtractorTest.php │ │ ├── TemplateAnnotatorTest.php │ │ └── ViewAnnotatorTest.php │ ├── CodeCompletion/ │ │ ├── CodeCompletionGeneratorTest.php │ │ ├── Task/ │ │ │ ├── BehaviorTaskTest.php │ │ │ ├── ControllerEventsTaskTest.php │ │ │ ├── ModelEventsTaskTest.php │ │ │ └── ViewEventsTaskTest.php │ │ └── TaskCollectionTest.php │ ├── Command/ │ │ ├── Annotate/ │ │ │ ├── ClassesCommandPathAwareTest.php │ │ │ ├── ClassesCommandTestCaseWalkTest.php │ │ │ └── Fixture/ │ │ │ ├── SecondTestPathAwareAnnotatorTask.php │ │ │ └── TestPathAwareAnnotatorTask.php │ │ ├── AnnotateCommandTest.php │ │ ├── GenerateCodeCompletionCommandTest.php │ │ ├── GeneratePhpstormMetaCommandTest.php │ │ └── IlluminateCommandTest.php │ ├── Console/ │ │ └── IoTest.php │ ├── Generator/ │ │ ├── Directive/ │ │ │ ├── ExitPointTest.php │ │ │ ├── ExpectedArgumentsTest.php │ │ │ ├── ExpectedReturnValuesTest.php │ │ │ ├── OverrideTest.php │ │ │ └── RegisterArgumentsSetTest.php │ │ ├── PhpstormGeneratorTest.php │ │ ├── Task/ │ │ │ ├── BehaviorTaskTest.php │ │ │ ├── CacheTaskTest.php │ │ │ ├── CellTaskTest.php │ │ │ ├── ComponentTaskTest.php │ │ │ ├── ConfigureTaskTest.php │ │ │ ├── ConnectionTaskTest.php │ │ │ ├── ConsoleHelperTaskTest.php │ │ │ ├── ConsoleTaskTest.php │ │ │ ├── DatabaseTableColumnNameTaskTest.php │ │ │ ├── DatabaseTableColumnTypeTaskTest.php │ │ │ ├── DatabaseTableTaskTest.php │ │ │ ├── DatabaseTypeTaskTest.php │ │ │ ├── ElementTaskTest.php │ │ │ ├── EntityTaskTest.php │ │ │ ├── EnvTaskTest.php │ │ │ ├── FixtureTaskTest.php │ │ │ ├── FormHelperTaskTest.php │ │ │ ├── HelperTaskTest.php │ │ │ ├── LayoutTaskTest.php │ │ │ ├── MailerTaskTest.php │ │ │ ├── ModelTaskTest.php │ │ │ ├── PluginTaskTest.php │ │ │ ├── RequestTaskTest.php │ │ │ ├── RoutePathTaskTest.php │ │ │ ├── TableAssociationTaskTest.php │ │ │ ├── TableFinderTaskTest.php │ │ │ ├── TranslationKeyTaskTest.php │ │ │ └── ValidationTaskTest.php │ │ └── TaskCollectionTest.php │ ├── Illuminator/ │ │ ├── IlluminatorTest.php │ │ └── Task/ │ │ ├── ControllerDefaultTableTaskTest.php │ │ ├── EntityFieldTaskTest.php │ │ └── TableValidationLinkTaskTest.php │ ├── Utility/ │ │ ├── AppPathTest.php │ │ ├── AppTest.php │ │ ├── GenericStringTest.php │ │ ├── PluginPathTest.php │ │ ├── PluginTest.php │ │ └── TranslationParserTest.php │ ├── ValueObject/ │ │ ├── ClassNameTest.php │ │ ├── DoubleQuoteStringNameTest.php │ │ ├── KeyValueTest.php │ │ ├── LiteralNameTest.php │ │ └── StringNameTest.php │ └── View/ │ └── Helper/ │ └── DocBlockHelperTest.php ├── bootstrap.php ├── phpstan.neon ├── schema.php ├── shim.php ├── test_app/ │ ├── locales/ │ │ ├── de/ │ │ │ ├── default.po │ │ │ └── my_plugin.po │ │ └── en/ │ │ └── default.po │ ├── plugins/ │ │ ├── Awesome/ │ │ │ ├── src/ │ │ │ │ ├── AwesomePlugin.php │ │ │ │ ├── Controller/ │ │ │ │ │ └── Admin/ │ │ │ │ │ └── AwesomeHousesController.php │ │ │ │ └── Model/ │ │ │ │ └── Table/ │ │ │ │ ├── HousesTable.php │ │ │ │ └── WindowsTable.php │ │ │ └── templates/ │ │ │ └── element/ │ │ │ └── pagination.php │ │ ├── Controllers/ │ │ │ └── src/ │ │ │ ├── Controller/ │ │ │ │ ├── GenericController.php │ │ │ │ ├── HousesController.php │ │ │ │ └── WindowsController.php │ │ │ ├── ControllersPlugin.php │ │ │ └── Model/ │ │ │ └── Table/ │ │ │ └── HousesTable.php │ │ ├── MyNamespace/ │ │ │ └── MyPlugin/ │ │ │ ├── src/ │ │ │ │ ├── Controller/ │ │ │ │ │ └── Component/ │ │ │ │ │ └── MyComponent.php │ │ │ │ ├── Model/ │ │ │ │ │ ├── Behavior/ │ │ │ │ │ │ └── MyBehavior.php │ │ │ │ │ └── Table/ │ │ │ │ │ └── MyTable.php │ │ │ │ └── MyPluginPlugin.php │ │ │ └── tests/ │ │ │ └── Fixture/ │ │ │ └── Sub/ │ │ │ └── MyFixture.php │ │ └── Relations/ │ │ └── src/ │ │ ├── Model/ │ │ │ ├── Entity/ │ │ │ │ ├── Bar.php │ │ │ │ ├── Foo.php │ │ │ │ └── User.php │ │ │ └── Table/ │ │ │ ├── BarsTable.php │ │ │ ├── FoosTable.php │ │ │ └── UsersTable.php │ │ └── RelationsPlugin.php │ ├── src/ │ │ ├── Application.php │ │ ├── Command/ │ │ │ └── MyCommand.php │ │ ├── Controller/ │ │ │ ├── AppController.php │ │ │ ├── BarController.php │ │ │ ├── Component/ │ │ │ │ ├── CheckHttpCacheComponent.php │ │ │ │ ├── MyComponent.php │ │ │ │ ├── MyControllerComponent.php │ │ │ │ └── MyOtherComponent.php │ │ │ ├── DynamicPropertiesController.php │ │ │ ├── DynamicPropertiesExistingDocblockController.php │ │ │ └── FoosController.php │ │ ├── Custom/ │ │ │ ├── CustomClass.php │ │ │ └── Nested/ │ │ │ └── NestedClass.php │ │ ├── Database/ │ │ │ └── Type/ │ │ │ └── UuidType.php │ │ ├── Generator/ │ │ │ └── Task/ │ │ │ ├── TestDatabaseTableColumnTypeTask.php │ │ │ ├── TestEnvTask.php │ │ │ └── TestFixtureTask.php │ │ ├── Mailer/ │ │ │ └── UserMailer.php │ │ ├── Model/ │ │ │ ├── Entity/ │ │ │ │ ├── BarBar.php │ │ │ │ ├── BarBarsAbstract.php │ │ │ │ ├── Callback.php │ │ │ │ ├── Car.php │ │ │ │ ├── Complex/ │ │ │ │ │ └── Wheel.php │ │ │ │ ├── Complex2/ │ │ │ │ │ └── Wheel.php │ │ │ │ ├── Foo.php │ │ │ │ ├── PHP/ │ │ │ │ │ ├── ComplexType.php │ │ │ │ │ ├── Duplicates.php │ │ │ │ │ └── Generics.php │ │ │ │ ├── PHP7/ │ │ │ │ │ └── Virtual.php │ │ │ │ ├── Virtual.php │ │ │ │ └── Wheel.php │ │ │ ├── Enum/ │ │ │ │ └── CarStatus.php │ │ │ └── Table/ │ │ │ ├── AbstractTable.php │ │ │ ├── BarBarsAbstractTable.php │ │ │ ├── BarBarsTable.php │ │ │ ├── CallbacksTable.php │ │ │ ├── CarsTable.php │ │ │ ├── CustomFinderTable.php │ │ │ ├── ExceptionsTable.php │ │ │ ├── FoosTable.php │ │ │ ├── SkipMeTable.php │ │ │ ├── SkipSomeTable.php │ │ │ ├── Specific/ │ │ │ │ ├── AbstractTable.php │ │ │ │ ├── BarBarsAbstractTable.php │ │ │ │ ├── BarBarsTable.php │ │ │ │ ├── CallbacksTable.php │ │ │ │ ├── CarsTable.php │ │ │ │ ├── CustomFinderTable.php │ │ │ │ ├── ExceptionsTable.php │ │ │ │ ├── FoosTable.php │ │ │ │ ├── SkipMeTable.php │ │ │ │ ├── SkipSomeTable.php │ │ │ │ ├── WheelsExtraTable.php │ │ │ │ └── WheelsTable.php │ │ │ ├── WheelsExtraTable.php │ │ │ └── WheelsTable.php │ │ ├── ValueObject/ │ │ │ └── DoubleQuoteStringName.php │ │ └── View/ │ │ ├── AppView.php │ │ ├── Cell/ │ │ │ └── TestCell.php │ │ ├── CustomView.php │ │ └── Helper/ │ │ ├── HtmlHelper.php │ │ ├── MyHelper.php │ │ └── MyMethodHelper.php │ ├── templates/ │ │ ├── Foos/ │ │ │ ├── anonymous.php │ │ │ ├── array.php │ │ │ ├── custom_view.php │ │ │ ├── edit.php │ │ │ ├── empty.php │ │ │ ├── existing.php │ │ │ ├── existing_strict.php │ │ │ ├── following_inline.php │ │ │ ├── inline.php │ │ │ ├── loop.php │ │ │ ├── multiline.php │ │ │ ├── outdated.php │ │ │ ├── phpline.php │ │ │ ├── string_interpolation.php │ │ │ └── vars.php │ │ ├── Helpers/ │ │ │ └── helpers.php │ │ ├── View/ │ │ │ └── view.php │ │ ├── element/ │ │ │ ├── deeply/ │ │ │ │ └── nested.php │ │ │ └── example.php │ │ └── layout/ │ │ └── ajax.php │ ├── tests/ │ │ └── Fixture/ │ │ └── SmallWindowsFixture.php │ └── vendor/ │ └── cakephp/ │ └── cakephp/ │ └── tests/ │ └── Fixture/ │ └── PostsFixture.php └── test_files/ ├── ClassAnnotation/ │ └── TableFind/ │ └── before.php ├── Command/ │ └── MyCommand.php ├── Controller/ │ ├── AppController.php │ ├── BarController.php │ ├── Component/ │ │ ├── MyComponent.php │ │ ├── MyControllerComponent.php │ │ └── MyOtherComponent.php │ ├── DynamicPropertiesController.php │ ├── DynamicPropertiesExistingDocblockController.php │ ├── FoosController.php │ ├── HousesController.php │ └── WindowsController.php ├── Custom/ │ ├── CustomClass.php │ └── Nested/ │ └── NestedClass.php ├── FormAnnotation/ │ ├── FormAnnotation.existing.php │ └── FormAnnotation.missing.php ├── MailerAnnotation/ │ ├── MailerAnnotation.existing.php │ ├── MailerAnnotation.invalid.php │ ├── MailerAnnotation.missing.php │ ├── MailerAnnotation.missing2.php │ └── MailerAnnotation.missing3.php ├── Model/ │ ├── Entity/ │ │ ├── Car.php │ │ ├── Complex/ │ │ │ └── Wheel.php │ │ ├── Constants/ │ │ │ ├── Wheel.php │ │ │ └── WheelComplex.php │ │ ├── ConstantsPartial/ │ │ │ └── Wheel.php │ │ ├── ConstantsPartialResult/ │ │ │ └── Wheel.php │ │ ├── Foo.php │ │ ├── PHP/ │ │ │ ├── ComplexType.php │ │ │ ├── Duplicates.php │ │ │ └── Generics.php │ │ ├── PHP7/ │ │ │ └── Virtual.php │ │ ├── Relations/ │ │ │ ├── Bar.php │ │ │ ├── Foo.php │ │ │ └── User.php │ │ ├── Virtual.php │ │ └── Wheel.php │ └── Table/ │ ├── BarBarsAbstractTable.php │ ├── BarBarsDetailedTable.php │ ├── BarBarsEntityTemplateTable.php │ ├── BarBarsTable.php │ ├── CallbacksTable.php │ ├── FooTable.php │ ├── SkipMeTable.php │ ├── SkipSomeTable.php │ ├── Specific/ │ │ ├── BarBarsAbstractTable.php │ │ ├── BarBarsDetailedTable.php │ │ ├── BarBarsTable.php │ │ ├── CallbacksTable.php │ │ ├── FooTable.php │ │ ├── SkipMeTable.php │ │ ├── SkipSomeTable.php │ │ ├── WheelsExtraTable.php │ │ └── WheelsTable.php │ ├── Validation/ │ │ └── IpRulesTable.php │ ├── ValidationResult/ │ │ └── IpRulesTable.php │ ├── WheelsExtraTable.php │ └── WheelsTable.php ├── View/ │ ├── AppView.php │ └── Helper/ │ ├── MyHelper.php │ └── MyMethodHelper.php ├── VirtualFieldAnnotation/ │ ├── VirtualFieldAnnotation.existing.php │ └── VirtualFieldAnnotation.missing.php ├── locales/ │ └── default.po ├── meta/ │ └── phpstorm/ │ ├── .meta.php │ ├── .meta_52.php │ └── .meta_lowest.php ├── routes/ │ ├── after/ │ │ ├── empty.php │ │ ├── existing.php │ │ └── outdated.php │ └── before/ │ ├── empty.php │ ├── existing.php │ └── outdated.php ├── templates/ │ ├── array.php │ ├── custom_view.php │ ├── edit.php │ ├── empty.php │ ├── existing.php │ ├── existing_strict.php │ ├── following_inline.php │ ├── inline.php │ ├── loop.php │ ├── multiline.php │ ├── outdated.php │ ├── phpline.php │ ├── string_interpolation.php │ └── vars.php └── tests/ ├── BarControllerTest.attribute.php ├── BarControllerTest.existing.php ├── BarControllerTest.link.php └── BarControllerTest.missing.php ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ ; This file is for unifying the coding style for different editors and IDEs. ; More information at https://editorconfig.org root = true [*] indent_style = tab indent_size = 4 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true [*.bat] end_of_line = crlf [*.yml] indent_style = space indent_size = 2 [*.md] indent_style = space ================================================ FILE: .gitattributes ================================================ # Define the line ending behavior of the different file extensions # Set default behavior, in case users don't have core.autocrlf set. * text text=auto eol=lf *.jpg binary *.png binary # Remove files for archives generated using `git archive` phpunit.xml.dist export-ignore .github export-ignore .gitignore export-ignore .gitattributes export-ignore .editorconfig export-ignore tests/test_app export-ignore tests/test_files export-ignore tests/TestCase.php export-ignore ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: dereuromark ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: "npm" directory: "/docs" schedule: interval: "weekly" ignore: # vite is a transitive dep of vitepress; vitepress 1.x pins vite ^5.4.x # and security fixes for vite require >= 6.4.2. Drop this ignore once # vitepress 2.x ships stable (currently alpha) and we can upgrade. - dependency-name: "vite" ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI on: push: pull_request: workflow_dispatch: jobs: testsuite: runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: php-version: ['8.2', '8.5'] db-type: [sqlite, mysql, pgsql] prefer-lowest: [''] include: - php-version: '8.2' db-type: 'sqlite' prefer-lowest: 'prefer-lowest' services: postgres: image: postgres ports: - 5432:5432 env: POSTGRES_PASSWORD: postgres steps: - uses: actions/checkout@v5 - name: Setup Service if: matrix.db-type == 'mysql' run: | sudo service mysql start mysql -h 127.0.0.1 -u root -proot -e 'CREATE DATABASE cakephp;' - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-version }} extensions: mbstring, intl, pdo_${{ matrix.db-type }} coverage: pcov - name: Get composer cache directory id: composercache run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies uses: actions/cache@v4 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} restore-keys: ${{ runner.os }}-composer- - name: Composer install run: | composer --version if ${{ matrix.prefer-lowest == 'prefer-lowest' }} then composer update --prefer-lowest --prefer-stable composer require --dev dereuromark/composer-prefer-lowest:dev-master else composer install --no-progress --prefer-dist --optimize-autoloader fi - name: Run PHPUnit run: | if [[ ${{ matrix.db-type }} == 'sqlite' ]]; then export DB_URL='sqlite:///:memory:'; fi if [[ ${{ matrix.db-type }} == 'mysql' ]]; then export DB_URL='mysql://root:root@127.0.0.1/cakephp'; fi if [[ ${{ matrix.db-type }} == 'pgsql' ]]; then export DB_URL='postgres://postgres:postgres@127.0.0.1/postgres'; fi if [[ ${{ matrix.php-version }} == '8.2' ]]; then vendor/bin/phpunit --coverage-clover=coverage.xml else vendor/bin/phpunit fi - name: Validate prefer-lowest if: matrix.prefer-lowest == 'prefer-lowest' run: vendor/bin/validate-prefer-lowest -m - name: Upload coverage reports to Codecov if: success() && matrix.php-version == '8.2' uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} validation: name: Coding Standard & Static Analysis runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v5 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: '8.2' extensions: mbstring, intl coverage: none - name: Get composer cache directory id: composercache run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies uses: actions/cache@v4 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} restore-keys: ${{ runner.os }}-composer- - name: Composer Setup run: composer stan-setup - name: Run phpstan run: vendor/bin/phpstan analyse --error-format=github - name: Run phpcs run: composer cs-check ================================================ FILE: .github/workflows/deploy-docs.yml ================================================ name: Deploy Documentation on: push: branches: - master paths: - 'docs/**' - '.github/workflows/deploy-docs.yml' workflow_dispatch: permissions: contents: read pages: write id-token: write concurrency: group: pages cancel-in-progress: false jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Node uses: actions/setup-node@v4 with: node-version: 20 cache: npm cache-dependency-path: docs/package-lock.json - name: Setup Pages uses: actions/configure-pages@v5 - name: Install dependencies run: npm ci working-directory: docs - name: Build with VitePress run: npm run docs:build working-directory: docs - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: path: docs/.vitepress/dist deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} needs: build runs-on: ubuntu-latest steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 ================================================ FILE: .gitignore ================================================ /phpunit.phar /.phpunit.result.cache /.phpunit.cache/ /vendor/ /tmp/ /composer.lock /composer.phar # IDE and editor specific files # ################################# /nbproject/ /.idea/ /.phpstorm.meta.php /tests/test_app/.phpstorm.meta.php/.ide-helper.meta.php .phpcs.cache # Docs /docs/node_modules/ /docs/.vitepress/dist/ /docs/.vitepress/cache/ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017 Mark Sch. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # CakePHP IdeHelper Plugin [![CI](https://github.com/dereuromark/cakephp-ide-helper/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/dereuromark/cakephp-ide-helper/actions/workflows/ci.yml?query=branch%3Amaster) [![Coverage Status](https://img.shields.io/codecov/c/github/dereuromark/cakephp-ide-helper/master.svg)](https://app.codecov.io/github/dereuromark/cakephp-ide-helper/tree/master) [![PHPStan](https://img.shields.io/badge/PHPStan-level%208-brightgreen.svg?style=flat)](https://phpstan.org/) [![Latest Stable Version](https://poser.pugx.org/dereuromark/cakephp-ide-helper/v/stable.svg)](https://packagist.org/packages/dereuromark/cakephp-ide-helper) [![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%208.2-8892BF.svg)](https://php.net/) [![License](https://poser.pugx.org/dereuromark/cakephp-ide-helper/license.svg)](LICENSE) [![Total Downloads](https://poser.pugx.org/dereuromark/cakephp-ide-helper/d/total.svg)](https://packagist.org/packages/dereuromark/cakephp-ide-helper) IdeHelper plugin for CakePHP applications. > Boost your productivity. Avoid mistakes. This branch is for use with **CakePHP 5.1+**. For details see [version map](https://github.com/dereuromark/cakephp-ide-helper/wiki#cakephp-version-map). ## Features The main idea is to improve IDE compatibility and use annotations to make the IDE understand the "magic" of CakePHP, so you can click through the class methods and object chains as well as spot obvious issues and mistakes easier. The IDE will usually mark problematic code yellow (missing, wrong method etc). This also improves compatibility with tools like [PHPStan](https://github.com/phpstan/phpstan). Those can then follow the code easier and provide more valuable help. - Add annotations to existing code (e.g. when upgrading an application) just like baking would to new code. - Can run multiple times without adding the annotations again. - It can also replace or remove outdated annotations. - Works with your application as well as any loaded plugin. - CI check support, hook it up just like the coding standards check. - Set up as watcher and have live in-real-time annotations added/updated. Supports annotations for: - Models (Tables and Entities) - Controllers (including prefixes like `Admin`) and Components - View (AppView) and Helpers - Templates (`.php` PHP template files including elements) - Commands and Tasks - ... and more ![Screenshot](docs/public/screenshot.jpg) Supports code completion help for: - Behaviors (property access on the BehaviorRegistry) Supports IDE autocomplete/typehinting of (magic)strings as well as return types/values for: - Plugins, Components, Behaviors, Helpers, Mailers - Associations, Validation - I18n Translation, Cache - Elements and layouts - Tables and their fields - Route paths, Request/ENV, Connection - ... and more (using PhpStorm meta file) Supports better IDE usage with Illuminator tasks to enhance existing code: - EntityFieldTask adds all entity fields as class constants for easier usage in IDEs ### IDE support This plugin is supposed to work with ANY IDE that supports annotations and code completion. IDEs tested so far for 100% compatibility: - **[PhpStorm](https://github.com/dereuromark/cakephp-ide-helper/wiki/PHPStorm)** (incl. meta file generator) - IntelliJ - Atom - **[VS Code](https://github.com/dereuromark/cakephp-ide-helper/wiki/Visual-Studio-Code)** - ... [Report or PR your IDE of choice here to confirm its full compatibility] See [Wiki](https://github.com/dereuromark/cakephp-ide-helper/wiki) for details and tips/settings. ### Plugin with Annotator tasks - [CakephpFixtureFactories](https://github.com/dereuromark/cakephp-fixture-factories) for Factory class usage through `@extends`. ### Plugins with meta file generator tasks The following plugins use this plugin to improve IDE compatibility around factory and magic string usage: - [Migrations](https://github.com/cakephp/migrations) for migration file writing (included in IdeHelper directly). - [Queue](https://github.com/dereuromark/cakephp-queue) for `QueuedJobsTable::createJob()` usage. - [Burzum/CakeServiceLayer](https://github.com/burzum/cakephp-service-layer) for `loadService()` usage. - ... (add yours here) ### Plugins with Illuminator tasks - [StateMachine](https://github.com/spryker/cakephp-statemachine) for syncing states from XML into PHP. - ... (add yours here) ### More More collections of useful tasks can be found in the [IdeHelperExtra plugin](https://github.com/dereuromark/cakephp-ide-helper-extra). ### Install, Setup, Usage See the **[Documentation](https://dereuromark.github.io/cakephp-ide-helper/)** for details. ### Powered by [![PhpStorm logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/PhpStorm.svg)](https://jb.gg/OpenSourceSupport) They are sponsoring my IDE for my FOSS work on this repository and beyond. ================================================ FILE: annotate-watcher.cjs ================================================ // watcher.js const chokidar = require('chokidar'); const { exec } = require('child_process'); // Helper to parse CLI arguments function getPathsFromArgs() { const arg = process.argv.find(arg => arg.startsWith('--path=')); if (!arg) return ['src/', 'templates/']; // default path const value = arg.split('=')[1]; return value.split(',').map(p => p.trim()).filter(Boolean); } const watchPaths = getPathsFromArgs(); console.log(`🔍 Watching: ${watchPaths.join(', ')}`); // Initialize watcher chokidar.watch(watchPaths, { ignored: /(^|[\/\\])\../, // ignore dotfiles persistent: true, }) .on('change', path => { if (!path.endsWith('.php')) return; // skip non-PHP files console.log(`📝 File changed: ${path}`); // Run CakePHP command exec('bin/cake annotate all --file ' + `${path}`, (error, stdout, stderr) => { if (error) { console.error(`❌ Error executing CakePHP command: ${error.message}`); return; } if (stderr) { console.error(`⚠️ STDERR: ${stderr}`); } console.log(`✅ Output:\n${stdout}`); }); }); ================================================ FILE: composer.json ================================================ { "name": "dereuromark/cakephp-ide-helper", "description": "CakePHP IdeHelper Plugin to improve auto-completion", "license": "MIT", "type": "cakephp-plugin", "keywords": [ "cakephp", "IDE", "autocomplete", "annotations", "phpstorm", "phpdoc", "dev", "cli" ], "authors": [ { "name": "Mark Scherer", "homepage": "https://www.dereuromark.de", "role": "Maintainer" }, { "name": "Other contributors", "homepage": "https://github.com/dereuromark/cakephp-ide-helper/graphs/contributors", "role": "Developer" } ], "homepage": "https://github.com/dereuromark/cakephp-ide-helper/", "support": { "issues": "https://github.com/dereuromark/cakephp-ide-helper/issues", "source": "https://github.com/dereuromark/cakephp-ide-helper/" }, "require": { "php": ">=8.2", "cakephp/bake": "^3.2.0", "cakephp/cakephp": "^5.1.5", "nikic/php-parser": "^5.7", "phpstan/phpdoc-parser": "^2.1.0", "sebastian/diff": "^6.0 || ^7.0 || ^8.0", "squizlabs/php_codesniffer": "^3.13 || ^4.0" }, "require-dev": { "dereuromark/cakephp-shim": "^3.3.0", "cakephp/migrations": "^4.5.1 || ^5.0", "cakephp/plugin-installer": "^2.0.1", "fig-r/psr2r-sniffer": "@stable", "phpunit/phpunit": "^11.5 || ^12.1 || ^13.0", "phpstan/phpstan": "^2.0" }, "minimum-stability": "stable", "prefer-stable": true, "autoload": { "psr-4": { "IdeHelper\\": "src/", "IdeHelper\\Test\\Fixture\\": "tests/Fixture/" } }, "autoload-dev": { "psr-4": { "Awesome\\": "tests/test_app/plugins/Awesome/src/", "Cake\\Test\\": "vendor/cakephp/cakephp/tests/", "Controllers\\": "tests/test_app/plugins/Controllers/src/", "IdeHelper\\PHPStan\\": "tests/PHPStan/", "IdeHelper\\Test\\": "tests/", "MyNamespace\\MyPlugin\\": "tests/test_app/plugins/MyNamespace/MyPlugin/src/", "MyNamespace\\MyPlugin\\Test\\Fixture\\": "tests/test_app/plugins/MyNamespace/MyPlugin/tests/Fixture/", "Relations\\": "tests/test_app/plugins/Relations/src/", "TestApp\\": "tests/test_app/src/" } }, "config": { "allow-plugins": { "cakephp/plugin-installer": true, "dealerdirect/phpcodesniffer-composer-installer": true } }, "scripts": { "cs-check": "phpcs --colors --parallel=16", "cs-fix": "phpcbf --colors --parallel=16", "lowest": "validate-prefer-lowest", "lowest-setup": "composer update --prefer-lowest --prefer-stable --prefer-dist --no-interaction && cp composer.json composer.backup && composer require --dev dereuromark/composer-prefer-lowest && mv composer.backup composer.json", "lowest-setup-debug": "cp composer.json composer.backup && composer require --dev phpunit/phpunit:^11.5 -W && composer update --prefer-lowest --prefer-stable --prefer-dist --no-interaction && composer require --dev dereuromark/composer-prefer-lowest && mv composer.backup composer.json", "stan": "phpstan analyze", "stan-setup": "cp composer.json composer.backup && composer require --dev phpstan/phpstan:^2.0.0 && mv composer.backup composer.json", "stan-tests": "phpstan analyze -c tests/phpstan.neon", "test": "phpunit", "test-coverage": "phpunit --log-junit tmp/coverage/unitreport.xml --coverage-html tmp/coverage --coverage-clover tmp/coverage/coverage.xml" } } ================================================ FILE: config/app.example.php ================================================ [ // Additional plugins that are not loaded, but should be included, use `-` prefix to exclude 'plugins' => [], // Controller prefixes to check for 'prefixes' => [ 'Admin', ], // Template paths to skip 'skipTemplatePaths' => [ '/templates/Bake/', ], 'templateExtensions' => [ 'php', ], // How behaviors are annotated into tables: ['mixin', 'extends', true, false, null] 'tableBehaviors' => null, // null auto-detects based CakePHP version (>= 5.2.2 for extends) and always adds mixins. 'arrayAsGenerics' => false, // Enable to have modern generics syntax (recommended) in doc blocks 'objectAsGenerics' => false, // Enable to have modern generics syntax (recommended) in doc blocks 'assocsAsGenerics' => false, // Enable to have modern generics syntax (NOT recommended yet) in doc blocks 'genericsInParam' => false, // true for basic generics, 'detailed' for fully detailed types (array, ResultSetInterface, ...) 'concreteEntitiesInParam' => false, // Enable to have specific entities in generated method param doc blocks (NOT recommended) 'tableEntityQuery' => false, // Enable to annotate `Table::find()` as returning `SelectQuery` for IDEs // Set to `false` to disable, or string if you have a custom FQCN to be used 'templateCollectionObject' => true, // Set to `false` to disable, defaults to `mixed` if enabled, you can also pass callable for logic 'autoCollect' => true, // Can be strings or `/regex/` (e.g. `'/^\_.+$/i'` for underscore prefixed variables) 'autoCollectBlacklist' => [], 'preferLinkOverUsesInTests' => true, // Prefer `@link` annotations over `@uses` in test files, prevents PHPUnit/Rector to replace them with attributes. // Custom Entity field type mapping 'typeMap' => [], // Default View class to use 'viewClass' => null, // Plugins to include for View annotations 'includedPlugins' => [], // Always add annotations/meta even if not yet needed 'preemptive' => false, // Annotator task customization 'annotators' => [], // For meta file generator 'generatorTasks' => [], // A regex pattern - for Migrations plugin DatabaseTableTask generator task 'ignoreDatabaseTables' => null, // A list of tables - for Migrations plugin DatabaseTableTask generator task 'skipDatabaseTables' => null, // For Illuminator tasks 'illuminatorTasks' => [], 'illuminatorIndentation' => "\t", // For code completion file generator 'codeCompletionTasks' => [], // If a custom directory should be used, defaults to TMP otherwise 'codeCompletionPath' => null, 'codeCompletionReturnType' => null, // Auto-detect based on controller/component, set to true/false to force one mode. ], ]; ================================================ FILE: docs/.vitepress/config.ts ================================================ import { defineConfig } from 'vitepress' export default defineConfig({ title: 'cakephp-ide-helper', description: 'Annotations, meta files, code-completion, and Illuminator tasks for CakePHP — boost IDE and static analyzer support.', base: '/cakephp-ide-helper/', head: [ ['link', { rel: 'icon', href: '/cakephp-ide-helper/favicon.svg', type: 'image/svg+xml' }], ], themeConfig: { logo: '/logo.svg', nav: [ { text: 'Guide', link: '/guide/', activeMatch: '/guide/' }, { text: 'Annotations', link: '/annotations/', activeMatch: '/annotations/' }, { text: 'Code Completion', link: '/code-completion/', activeMatch: '/code-completion/' }, { text: 'Generator', link: '/generator/', activeMatch: '/generator/' }, { text: 'Illuminator', link: '/illuminator/', activeMatch: '/illuminator/' }, { text: 'Reference', link: '/reference/configuration', activeMatch: '/reference/' }, { text: 'Links', items: [ { text: 'GitHub', link: 'https://github.com/dereuromark/cakephp-ide-helper' }, { text: 'Packagist', link: 'https://packagist.org/packages/dereuromark/cakephp-ide-helper' }, { text: 'Issues', link: 'https://github.com/dereuromark/cakephp-ide-helper/issues' }, { text: 'Wiki', link: 'https://github.com/dereuromark/cakephp-ide-helper/wiki' }, { text: 'IdeHelperExtra', link: 'https://github.com/dereuromark/cakephp-ide-helper-extra' }, ], }, ], sidebar: { '/guide/': [ { text: 'Guide', items: [ { text: 'Introduction', link: '/guide/' }, { text: 'Installation', link: '/guide/installation' }, { text: 'Usage', link: '/guide/usage' }, { text: 'IDE Support', link: '/guide/ide-support' }, { text: 'Migrating from 4.x', link: '/guide/migration' }, ], }, ], '/annotations/': [ { text: 'Annotations', items: [ { text: 'Overview', link: '/annotations/' }, { text: 'Controllers', link: '/annotations/controllers' }, { text: 'Models', link: '/annotations/models' }, { text: 'View, Components, Helpers', link: '/annotations/view' }, { text: 'Templates', link: '/annotations/templates' }, { text: 'Commands and Routes', link: '/annotations/commands' }, { text: 'Classes', link: '/annotations/classes' }, { text: 'Callbacks', link: '/annotations/callbacks' }, { text: 'Operations', link: '/annotations/operations' }, { text: 'Custom Class Annotators', link: '/annotations/custom-class-annotator' }, ], }, ], '/code-completion/': [ { text: 'Code Completion', items: [ { text: 'Overview', link: '/code-completion/' }, ], }, ], '/generator/': [ { text: 'Generator', items: [ { text: 'Overview', link: '/generator/' }, { text: 'Available Tasks', link: '/generator/tasks' }, { text: 'Custom Tasks and Directives', link: '/generator/custom-tasks' }, { text: 'Operations', link: '/generator/operations' }, ], }, ], '/illuminator/': [ { text: 'Illuminator', items: [ { text: 'Overview', link: '/illuminator/' }, ], }, ], '/reference/': [ { text: 'Reference', items: [ { text: 'Configuration', link: '/reference/configuration' }, { text: 'Contributing', link: '/reference/contributing' }, ], }, ], }, socialLinks: [ { icon: 'github', link: 'https://github.com/dereuromark/cakephp-ide-helper' }, ], search: { provider: 'local', }, editLink: { pattern: 'https://github.com/dereuromark/cakephp-ide-helper/edit/master/docs/:path', text: 'Edit this page on GitHub', }, footer: { message: 'Released under the MIT License.', copyright: 'Copyright Mark Scherer', }, }, }) ================================================ FILE: docs/.vitepress/theme/custom.css ================================================ :root { --vp-c-brand-1: #6d28d9; --vp-c-brand-2: #7c3aed; --vp-c-brand-3: #a78bfa; --vp-c-brand-soft: rgba(167, 139, 250, 0.14); --vp-home-hero-name-color: transparent; --vp-home-hero-name-background: linear-gradient(135deg, #6d28d9 0%, #0284c7 100%); --vp-home-hero-image-background-image: linear-gradient(135deg, #6d28d9 0%, #0284c7 100%); --vp-home-hero-image-filter: blur(42px); } .dark { --vp-c-brand-1: #a78bfa; --vp-c-brand-2: #8b5cf6; --vp-c-brand-3: #7c3aed; } .vp-doc table code, .vp-doc p code, .vp-doc li code { white-space: nowrap; } .vp-doc .custom-block.tip { border-color: var(--vp-c-brand-1); } ================================================ FILE: docs/.vitepress/theme/index.ts ================================================ import DefaultTheme from 'vitepress/theme' import './custom.css' export default DefaultTheme ================================================ FILE: docs/annotations/callbacks.md ================================================ # Callbacks and CallbackAnnotationTasks This is a separate annotations tool that focuses on **methods** and their doc blocks instead of classes. By default it ships with: - `TableCallbackAnnotatorTask` ## Table Callback Annotations Behaviors and generic code use the following signature: ```php /** * @param \Cake\Event\EventInterface $event Event * @param \Cake\Datasource\EntityInterface $entity Entity * @param \ArrayObject $options Options * @return void */ public function beforeSave(EventInterface $event, EntityInterface $entity, ArrayObject $options): void ``` As long as you only use methods and attributes of the `EntityInterface` contract, this is fine. But in specific Table-class code, you usually also access the entity's concrete properties. There the type hint is somewhat a lie. To please the IDE and tooling like PHPStan we can at least fix up the doc block — and that is what this task does, declaring the `Post` entity to be available and used inside. Inside the concrete `PostsTable` after running the `callbacks` command: ```php /** * @param \Cake\Event\EventInterface $event Event * @param \App\Model\Entity\Post $entity Entity * @param \ArrayObject $options Options * @return void */ public function beforeSave(EventInterface $event, EntityInterface $entity, ArrayObject $options): void ``` ## Entity Virtual Field Setter/Getter Annotations A virtual field will be "linked" to the property it handles: ```php /** * @see \App\Model\Entity\MyEntity::$expected_type * * @return int|null */ protected function _getExpectedType(): ?int ``` This way you can quick-jump from the property to the getter and vice versa within your IDE. ## Custom Tasks Create your own task class: ```php namespace App\Annotator\CallbackAnnotatorTask; use IdeHelper\Annotator\CallbackAnnotatorTask\AbstractCallbackAnnotatorTask; use IdeHelper\Annotator\CallbackAnnotatorTask\CallbackAnnotatorTaskInterface; class MyCallbackAnnotatorTask extends AbstractCallbackAnnotatorTask implements CallbackAnnotatorTaskInterface { /** * @param string $path * @return bool */ public function shouldRun(string $path): bool { // ... } /** * @param string $path * @return bool */ public function annotate(string $path): bool { // ... } } ``` Then add it to the config: ```php 'IdeHelper' => [ 'CallbackAnnotatorTasks' => [ 'MyCallbackAnnotatorTask' => \App\Annotator\CallbackAnnotatorTask\MyCallbackAnnotatorTask::class, ], ], ``` The key `'MyCallbackAnnotatorTask'` can be any string. Replacing existing tasks works the same way as for classes: use the native class name as key, your replacement as value, or `null` to disable. ================================================ FILE: docs/annotations/classes.md ================================================ # Classes and ClassAnnotationTasks In order to run certain "fixers" over all classes, class annotations and their tasks are available. Out of the box the following tasks are run. ## ModelAware Any `use ModelAwareTrait` together with `$this->loadModel(...)` calls will add the required annotation on top of the class. ## `Form::execute()` Adds a convenience inline annotation so you can quickly jump to the actual business logic: ```php use App\Form\ReleaseForm; $releaseForm = new ReleaseForm(); /** @uses \App\Form\ReleaseForm::_execute() */ $releaseForm->execute($data); ``` ## `Mailer::send()` Adds a convenience inline annotation so you can quickly jump to the actual business logic: ```php use App\Mailer\NotificationMailer; // or $notificationMailer = $this->getMailer('Notification'); /** @uses \App\Mailer\NotificationMailer::notify() */ $notificationMailer->send('notify', [$user, $details]); ``` ## Test Test classes of specific types can be annotated with the corresponding class they test. This is mainly useful for the following types, which are invoked indirectly via the integration test harness: - Controller - Command The `@link` statement helps to quick-jump to the class if needed. If your test class already has a `#[UsesClass(...)]` attribute, no annotation will be added. ::: tip Use `@uses` instead of `@link` Set the `IdeHelper.preferLinkOverUsesInTests` config key to `false` to use `@uses` instead of `@link`. ::: ## Custom Tasks Create your own task class: ```php namespace App\Annotator\ClassAnnotatorTask; use IdeHelper\Annotator\ClassAnnotatorTask\AbstractClassAnnotatorTask; use IdeHelper\Annotator\ClassAnnotatorTask\ClassAnnotatorTaskInterface; class MyClassAnnotatorTask extends AbstractClassAnnotatorTask implements ClassAnnotatorTaskInterface { /** * @param string $path * @param string $content * @return bool */ public function shouldRun(string $path, string $content): bool { // ... } /** * @param string $path * @return bool */ public function annotate(string $path): bool { // ... } } ``` Then add it to the config: ```php 'IdeHelper' => [ 'classAnnotatorTasks' => [ 'MyClassAnnotatorTask' => \App\Annotator\ClassAnnotatorTask\MyClassAnnotatorTask::class, ], ], ``` The key `'MyClassAnnotatorTask'` can be any string. For a fully worked PHP-AST example, see [Custom Class Annotators](./custom-class-annotator). ## Targeting Custom Directories By default `bin/cake annotate classes` walks `src/` (app + plugin classpaths) and `tests/TestCase/` (when `TestClassAnnotatorTask` is registered). A custom task whose subjects live elsewhere — for example test-fixture factories under `tests/Factory/`, scenario classes, or generated stubs — can opt into having those directories walked by also implementing `PathAwareClassAnnotatorTaskInterface`: ```php namespace App\Annotator\ClassAnnotatorTask; use IdeHelper\Annotator\ClassAnnotatorTask\AbstractClassAnnotatorTask; use IdeHelper\Annotator\ClassAnnotatorTask\PathAwareClassAnnotatorTaskInterface; class MyClassAnnotatorTask extends AbstractClassAnnotatorTask implements PathAwareClassAnnotatorTaskInterface { /** * @return array */ public static function scanPaths(): array { return ['tests/Factory/']; } public function shouldRun(string $path, string $content): bool { /* ... */ } public function annotate(string $path): bool { /* ... */ } } ``` Paths are project-root relative for app context, plugin-root relative when run with `-p`. They are walked recursively. Paths that do not exist on disk are silently skipped, and a path declared by multiple tasks is walked only once. ::: tip Path convention Return paths with forward slashes and a trailing slash (e.g. `'tests/Factory/'`), independent of OS. The command normalizes to the OS-native separator before walking, so the dedup key stays stable across Windows / \*nix and across tasks that disagree on style. ::: The interface is optional and additive — existing tasks that do not implement it behave unchanged. The feature is opt-in: a path-aware task is only consulted when it is registered in `IdeHelper.classAnnotatorTasks`. ## Replacing Native Tasks Using associative arrays you can swap any native task with your own implementation: ```php 'IdeHelper' => [ 'classAnnotatorTasks' => [ \IdeHelper\Annotator\ClassAnnotatorTask\ModelAwareTask::class => \App\Annotator\ClassAnnotatorTask\MyEnhancedModelAwareTask::class, ], ], ``` The native class name is the key, your replacement the value. Setting the value to `null` disables a native task entirely. ================================================ FILE: docs/annotations/commands.md ================================================ # Commands and Routes ## Commands Commands should annotate their primary model as well as all manually loaded models. ```bash bin/cake annotate commands ``` ```php /** * @var string|null */ protected ?string $defaultTable = 'Cars'; /** * @return int */ public function execute(Arguments $args, ConsoleIo $io): int { $this->fetchTable('MyPlugin.Wheels'); return static::CODE_SUCCESS; } ``` results in: ```php /** * @property \MyPlugin\Model\Table\WheelsTable $Wheels * @property \App\Model\Table\CarsTable $Cars */ ``` ## Routes Route files in 4.x are no longer required to be static. The `config/routes.php` file gets the following annotation so the `$routes` object is type-hinted for editors and analyzers: ```php /** * @var \Cake\Routing\RouteBuilder $routes */ ``` ================================================ FILE: docs/annotations/controllers.md ================================================ # Controllers All controllers should at least annotate their primary model. They should also annotate the other loaded models as well as the loaded components. ```bash bin/cake annotate controllers ``` ## Primary Model via Convention ```php Apples->...()` usage in your controllers. Use `-p PluginName` to annotate inside a plugin. ::: tip Plugin wildcards Use `*` to refer to a group of plugins, e.g. `-p SomePrefix/*` for everything under your own `plugins/` directory. You can also use `all` for all app plugins. For more than one plugin the command will not run into `vendor/` plugins, to avoid accidental modification there. ::: ## Primary Model via `$defaultTable` When defining `$defaultTable` it will be used instead: ```php findGetByUuidCalls(); $result = true; foreach ($found as $row) { $annotations = [ AnnotationFactory::createOrFail( VariableAnnotation::TAG, $row['entity'], $row['var'], ), ]; $result &= $this->annotateInlineContent($path, $this->content, $annotations, $row['line']); } return $result; } protected function findGetByUuidCalls(): array { $parser = (new ParserFactory())->createForNewestSupportedVersion(); $ast = $parser->parse($this->content); $found = []; $traverser = new NodeTraverser(); $traverser->addVisitor(new class ($found) extends NodeVisitorAbstract { protected array $found; /** * @param array $found */ public function __construct(array &$found) { $this->found = &$found; } /** * @param \PhpParser\Node $node * * @return null */ public function enterNode(Node $node): null { if ( $node instanceof Assign && $node->expr instanceof MethodCall && $node->expr->name instanceof Identifier && $node->expr->name->toString() === 'getByUuid' ) { $varName = $node->var instanceof Variable ? $node->var->name : null; $caller = $node->expr->var; if ( $caller instanceof PropertyFetch && $caller->var instanceof Variable && $caller->var->name === 'this' && $caller->name instanceof Identifier ) { $modelName = $caller->name->toString(); $entityClass = TableRegistry::getTableLocator()->get($modelName)->getEntityClass(); $this->found[] = [ 'line' => $node->getStartLine(), 'var' => '$' . $varName, 'entity' => '\\' . $entityClass, ]; } } return null; } }); $traverser->traverse($ast); return $found; } } ``` ================================================ FILE: docs/annotations/index.md ================================================ # Annotations Updater The Annotator keeps doc blocks and annotations in sync with your code without modifying functional code. Note that freshly [baking](https://github.com/cakephp/bake) your code produces similar results, but most projects already have existing code where re-baking is not an option. The annotator also keeps manually added or modified code annotated. ## Important Options to Start With The following are defined under the `IdeHelper` key in `app.php`: - `arrayAsGenerics`: Set to `true` to use modern generics syntax (`array` instead of legacy `type[]`). See the [Configuration reference](/reference/configuration) for the full list. ## Per-Class-Type Reference Each class type has its own page covering the dedicated subcommand and the options that apply only to that type: - [Controllers](./controllers) — `bin/cake annotate controllers` - [Models](./models) — `bin/cake annotate models` (Tables and Entities) - [View, Components, Helpers](./view) — `annotate view`, `annotate components`, `annotate helpers` - [Templates](./templates) — `bin/cake annotate templates` - [Commands and Routes](./commands) — `bin/cake annotate commands` - [Classes](./classes) — class-level annotators (`ModelAware`, `Form::execute()`, `Mailer::send()`, `Test`, custom) - [Callbacks](./callbacks) — method-level annotators (`TableCallbackAnnotatorTask`, custom) ## Cross-Cutting - [Operations](./operations) — running all commands, dry-run, filters, removal, file watcher, CI - [Custom Class Annotators](./custom-class-annotator) — full PHP-AST example ## Replacing Native Tasks Using associative arrays you can swap any native task with your own implementation: ```php 'IdeHelper' => [ 'annotators' => [ \IdeHelper\Annotator\EntityAnnotator::class => \App\Annotator\MyEnhancedEntityAnnotator::class, ], ], ``` The native class name is the key, your replacement the value. Setting the value to `null` disables a native task entirely. ================================================ FILE: docs/annotations/models.md ================================================ # Models Annotate Tables and their Entities: ```bash bin/cake annotate models ``` ## Tables Tables annotate their entity-related methods, their relations, and behavior mixins. A `LocationsTable` class would gain the following doc-block annotations if not already present: ```php /** * @method \App\Model\Entity\Location newEmptyEntity() * @method \App\Model\Entity\Location newEntity(array $data, array $options = []) * @method array<\App\Model\Entity\Location> newEntities(array $data, array $options = []) * @method \App\Model\Entity\Location get(mixed $primaryKey, array|string $finder = 'all', \Psr\SimpleCache\CacheInterface|string|null $cache = null, \Closure|string|null $cacheKey = null, mixed ...$args) * @method \Cake\ORM\Query\SelectQuery<\App\Model\Entity\Location> find(string $type = 'all', mixed ...$args) * @method \App\Model\Entity\Location|false save(\Cake\Datasource\EntityInterface $entity, array $options = []) * @method \App\Model\Entity\Location saveOrFail(\Cake\Datasource\EntityInterface $entity, array $options = []) * @method \App\Model\Entity\Location patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = []) * @method array<\App\Model\Entity\Location> patchEntities(iterable $entities, array $data, array $options = []) * @method \App\Model\Entity\Location findOrCreate(\Cake\ORM\Query\SelectQuery|callable|array $search, ?callable $callback = null, array $options = []) * @method \Cake\Datasource\ResultSetInterface<\App\Model\Entity\Location>|false saveMany(iterable $entities, array $options = []) * @method \Cake\Datasource\ResultSetInterface<\App\Model\Entity\Location> saveManyOrFail(iterable $entities, array $options = []) * @method \Cake\Datasource\ResultSetInterface<\App\Model\Entity\Location>|false deleteMany(iterable $entities, array $options = []) * @method \Cake\Datasource\ResultSetInterface<\App\Model\Entity\Location> deleteManyOrFail(iterable $entities, array $options = []) * * @property \Cake\ORM\Association\HasMany<\App\Model\Table\ImagesTable> $Images * @property \Cake\ORM\Association\BelongsTo<\App\Model\Table\UsersTable> $Users * * @mixin \Cake\ORM\Behavior\TimestampBehavior */ ``` ### Entity-aware `find()` return type If you want the table annotations to also expose the entity-aware `find()` return type for IDEs, enable: ```php 'IdeHelper' => [ 'tableEntityQuery' => true, ], ``` This is intentionally optional, because finder result shapes can still widen beyond plain entities. ### Detailed param types The `IdeHelper.genericsInParam` option is tri-state: - `false` (default) — bare `array` params, legacy behavior. - `true` — basic generics: `array` / `array` / `iterable`. - `'detailed'` — fully detailed types throughout, matching the richer form PHPStan and Psalm understand best. With `'detailed'`, the generated method annotations look like: ```php * @method \App\Model\Entity\User newEntity(array $data, array $options = []) * @method array<\App\Model\Entity\User> newEntities(array> $data, array $options = []) * @method \App\Model\Entity\User get(mixed $primaryKey, array|string $finder = 'all', \Psr\SimpleCache\CacheInterface|string|null $cache = null, \Closure|string|null $cacheKey = null, mixed ...$args) * @method \App\Model\Entity\User findOrCreate(\Cake\ORM\Query\SelectQuery<\App\Model\Entity\User>|callable|array $search, ?callable $callback = null, array $options = []) * @method \Cake\Datasource\ResultSetInterface|false saveMany(iterable<\App\Model\Entity\User> $entities, array $options = []) ``` Switching the value is additive — existing `true` users keep their current output, and the new `'detailed'` opt-in can be enabled at any time. ## Entities Entities annotate their properties and relations. A `Location` entity could look like this afterward: ```php /** * @property int $id * @property int $user_id * @property \App\Model\Entity\User $user * @property string $location * @property string $details * @property \Cake\I18n\DateTime $created * @property \Cake\I18n\DateTime $modified * @property string|null $virtual_property * * @property \App\Model\Entity\Image[] $images * @property \App\Model\Entity\User $user */ class Location extends Entity { } ``` ### Custom type maps Using the Configure key `'IdeHelper.typeMap'` you can set a custom array of types to be used for the field mapping. Overwriting the defaults of this plugin is also possible — to skip (reset) just set the value to `null`: ```php 'IdeHelper' => [ 'typeMap' => [ 'custom' => 'array', 'longtext' => null, // ... ], ], ``` Using the Configure key `'IdeHelper.nullableMap'` you can set a custom array of types and whether they can be nullable: ```php 'IdeHelper' => [ 'nullableMap' => [ 'custom' => false, 'longtext' => true, // ... ], ], ``` ### Virtual properties For virtual properties the annotator looks up the respective `_get...()` methods (e.g. `_getVirtualProperty()` for `$virtual_property`). It first checks the documented type in the doc block's `@return`, otherwise (given PHP 7.0+) tries to read it from the return type hint (e.g. `: ?string`). Only if that is also not present does it fall back to `mixed`. Note: You can also use the `@property-read` tag if it is a pure virtual field getter. ================================================ FILE: docs/annotations/operations.md ================================================ # Operations Cross-cutting flags and workflows that apply to every annotator subcommand. ## Running All Commands ```bash bin/cake annotate all ``` By default it runs interactively, asking you for each class type whether to continue. You can use `-i` (interactive) to enable interactive mode. It is also recommended to make the output more verbose: ```bash bin/cake annotate all -i -v ``` ::: warning Backup first Make sure you have committed or backed up all project files before running the annotator across the whole project. ::: ## Dry-Run and Diff If you want to check whether the annotator would modify any files, run it with `-d` (dry-run): ```bash bin/cake annotate all -d ``` It will output a small diff for each modification: ``` templates/Tickets -> view | + |