master 2844e5a12782 cached
478 files
935.6 KB
281.9k tokens
1488 symbols
1 requests
Download .txt
Showing preview only (1,060K chars total). Download the full file or copy to clipboard to get everything.
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
================================================
<?php

return [
	// Copy the following over to your project one in ROOT/config/
	'IdeHelper' => [
		// 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<string, mixed>, ResultSetInterface<int, TEntity>, ...)
		'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<TEntity>` 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<string>
     */
    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
<?php
namespace App\Controller;

class ApplesController extends AppController {
}
```

becomes:

```php
<?php
namespace App\Controller;

/**
 * @property \App\Model\Table\ApplesTable $Apples
 */
class ApplesController extends AppController {
}
```

You get autocompletion on any `$this->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
<?php
namespace App\Controller;

/**
 * @property \App\Model\Table\MyApplesTable $MyApples
 */
class ApplesController extends AppController {

    protected ?string $defaultTable = 'MyApples';

}
```

## Custom Prefixes

By default, the annotator supports any prefix for your controllers (as a
subfolder). Using the Configure key `'IdeHelper.prefixes'` you can configure a
prefix whitelist.


================================================
FILE: docs/annotations/custom-class-annotator.md
================================================
# Custom Class Annotators

Worked examples of writing custom class annotators in your CakePHP application.

## `getByUuid()` — Concrete Entity Inline Annotation

Imagine you have a method `getByUuid()` for all your tables to look up records
by their UUID instead of the AIID. The returned result would be "known" by
the IDE / PHPStan as `EntityInterface`.

You can create a custom annotator to add a concrete entity annotation
automatically. It adds an inline `@var` annotation above each `getByUuid()`
method assignment.

The example below uses PHP-AST to parse the class files and find the relevant
method calls. This is more exact than using a regex or PHPCS tokenizing, with
fewer false positives.

It then uses the existing IdeHelper code modifier that leverages PHPCS
tokenizing to add the inline annotation to the class file via
`annotateInlineContent()`.

```php
<?php

namespace App\Annotator\ClassAnnotatorTask;

use Cake\ORM\TableRegistry;
use IdeHelper\Annotation\AnnotationFactory;
use IdeHelper\Annotation\VariableAnnotation;
use IdeHelper\Annotator\ClassAnnotatorTask\AbstractClassAnnotatorTask;
use IdeHelper\Annotator\ClassAnnotatorTask\ClassAnnotatorTaskInterface;
use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Identifier;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
use PhpParser\ParserFactory;

/**
 * Usage of getByUuid() should have inline annotations added.
 */
class TableGetAnnotatorTask extends AbstractClassAnnotatorTask implements ClassAnnotatorTaskInterface
{
    /**
     * @param string $path
     * @param string $content
     *
     * @return bool
     */
    public function shouldRun(string $path, string $content): bool
    {
        if (!str_contains($path, DS . 'src' . DS)) {
            return false;
        }

        return true;
    }

    /**
     * @param string $path
     *
     * @return bool
     */
    public function annotate(string $path): bool
    {
        $found = $this->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<type>`
  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<mixed>` / `array<string, mixed>` / `iterable<TEntity>`.
- `'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<string, mixed> $data, array<string, mixed> $options = [])
 * @method array<\App\Model\Entity\User> newEntities(array<array<string, mixed>> $data, array<string, mixed> $options = [])
 * @method \App\Model\Entity\User get(mixed $primaryKey, array<string, mixed>|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<string, mixed> $search, ?callable $callback = null, array<string, mixed> $options = [])
 * @method \Cake\Datasource\ResultSetInterface<int, \App\Model\Entity\User>|false saveMany(iterable<\App\Model\Entity\User> $entities, array<string, mixed> $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
   | +<?php
   | +/**
   | + * @var \App\View\AppView $this
   | + * @var \App\Model\Entity\Ticket $ticket
   | + */
   | +?>
   |  <nav class="large-3 medium-4 columns" id="actions-sidebar">
```

::: tip Verbose dry-runs
Use `-v` together with `-d` to get more information on which files were
processed.
:::

## Quick-Filter Files

With `-f` / `--filter` you can quickly annotate only specific files. The
filter is applied to the file name. For templates it also looks at the folder
name.

## Specific Files Only

With `--file` and a comma-separated list of ROOT-relative or absolute paths,
you can limit the run to those specific files. This is useful for programmatic
tooling-based runs or file watchers.

If `--file` is set, it takes precedence over `--filter` (which is ignored).

Example:

```bash
bin/cake annotate all --file src/View/AppView.php,plugins/My/src/View/Helper/MyHelper.php
```

## Removing Outdated Annotations

With `-r` / `--remove` there is basic support for finding and removing
outdated annotations.

::: warning Alpha-quality feature
Only use this after running the normal annotation flow and committing the
result, so you can review and verify the changes. This feature is still in a
very alpha phase.
:::

You can prevent removal (just like updating) by adding a comment to your
annotation. That will skip any attempt to remove it:

```php
    @property array|null $data !
```

or:

```php
    @property array|null $data ! A manual field for testing only
```

## Skipping Annotations for a Class

Sometimes you are extending another class. In that case you can use the
`@inherit` tag in the class doc block to skip annotating:

```php
/**
 * @inheritdoc
 */
class CustomImagesTable extends ImagesTable ...
```

In this case `CustomImagesTable` extends `ImagesTable` but uses the same
`protected $_entityClass = Image::class;`, so we skip annotating.

## File Watcher Setup

You can set up a file watcher to run the annotation tool on every file
change, giving you live annotation updates while coding.

### Using Node and Chokidar

Install in your project root:

```bash
npm init -y
npm install chokidar --save
```

Run:

```bash
node vendor/dereuromark/cakephp-ide-helper/annotate-watcher.cjs
```

If necessary, you can customize the paths via `--path=src/,templates/`, for
example. You can also copy `annotate-watcher.cjs` to your app and customize it
there.

Since this is cross-platform, this is the recommended approach. It is also
the most performant — only files directly modified are touched. The trade-off
is that it might miss a few related templates that are not modified but would
get updates.

### Using watchexec

See [github.com/watchexec/watchexec](https://github.com/watchexec/watchexec).

```bash
watchexec -e php 'bin/cake annotate all'
```

Here you would run the annotator over all files. Still usually quite
performant.

## Continuous Integration

The tool can be run like the coding standards check in your CI. This way no
annotation can be forgotten when making PRs.

Use the `--ci` option along with `-d` (dry run):

```bash
bin/cake annotate all -d --ci
```

It will return error code `2` if any modification has to be done.

::: info Database setup
This needs some additional setup, like running migrations prior to the call.
The database must exist and replicate the actual DB.
:::

You can also add this into a pre-commit hook for local development. Your VCS
will then refuse to commit until annotations are all in line.

## Writing Your Own Annotators

Extend `IdeHelper\Command\AnnotateCommand` at the application level, register
your command, and create your own `Annotator` class:

```php
class MyAnnotator extends AbstractAnnotator {

    /**
     * @param string $path
     * @return bool
     */
    public function annotate(string $path): bool {
        // ...
    }
}
```

Then read a folder, iterate over it, and invoke your annotator from the
command with a specific path.

## Configure Options

For the full list of possible Configure options, see the `app.example.php`
file in the plugin's `/config/` directory. The content can be directly copied
into your project config.


================================================
FILE: docs/annotations/templates.md
================================================
# Templates

Annotate view templates and elements:

```bash
bin/cake annotate templates
```

Templates should have a `/** @var \App\View\AppView $this */` added on top if
they use any helper or access the request object. They should also annotate
entities they use.

A template such as:

```php
<h2>Some header</h2>
<?php echo $this->Form->create($user); ?>
<?php foreach ($groups as $group): ?>
<?php endforeach; ?>
<li><?= $this->Html->link(__('Edit Email'), ['action' => 'edit', $email->id]) ?> </li>
```

would get the following added on top:

```php
<?php
/**
 * @var \App\View\AppView $this
 * @var \App\Model\Entity\Email $email
 * @var \App\Model\Entity\Group[] $groups
 * @var \App\Model\Entity\User $user
 */
?>
```

## Extensions

To adjust which template extensions are processed, set
`IdeHelper.templateExtensions` via Configure. By default, all files of type
`'ctp'` and `'php'` will be checked.

::: info Twig templates
All template annotating is around PHP templates. Twig templates are not
supported. Twig usually has its own tooling — but it has serious drawbacks on
what this plugin provides: auto-complete and type-hinting as well as IDE
introspection of variable types.
:::

## Skipping Folders

Certain template folders, like Bake template generation, should be skipped.
This is done by default for `/templates/Bake/` in your app or your plugin.

If you want to adjust this, set `IdeHelper.skipTemplatePaths` via Configure:

```php
'IdeHelper' => [
    'skipTemplatePaths' => [
        // ...
    ],
],
```

## Skipping Variables

In some cases existing annotations might match different entities (e.g.
plugin vs. app namespace). To prevent them from being replaced incorrectly,
mark them to be ignored by adding any comment description:

```php
<?php
/**
 * @var \App\View\AppView $this
 * @var \My\Custom\Entity $car !
 */
?>
```

The `!` prevents the entity annotation from being replaced.

## Auto-Collecting Variables

The IdeHelper can auto-collect template variables and add them to the list
above. Set `'IdeHelper.autoCollect'` to `false` to disable this. It defaults
to `'mixed'` where the type cannot be guessed/detected.

If you need more control, configure a callable to detect or guess:

```php
'IdeHelper.autoCollect', function(array $variable) {
    if ($variable['name'] === 'date') {
        return 'Cake\I18n\DateTime';
    }
    return null;
});
```

::: tip Unique variables
For the best experience of auto-collecting, use unique variable names inside
the template(s). If you pass down a `$user` variable from the controller,
make sure you are not overwriting it in some local scope.

```php
// This will skip the other $user annotation
foreach ($role->users as $user) {}

// Use a better name instead to keep $user annotation
foreach ($role->users as $rolUser) {}
```
:::

You can use the `'IdeHelper.autoCollectBlacklist'` config to exclude certain
variables. The array accepts both strings and regexp patterns like
`'/^\_.+$/i'` for underscore-prefixed variables.

## Entity Collections

Usually, all collections (pagination, find) are object collections when passed
to the view layer. The template annotations added for them are e.g.:

```
// objectAsGenerics false
@var \App\Model\Entity\Article[]|\Cake\Collection\CollectionInterface $articles

// objectAsGenerics true
@var \Cake\Collection\CollectionInterface<\App\Model\Entity\Article> $articles
```

The config `IdeHelper.templateCollectionObject` can be set to a FQCN string if
you want to display a custom class (e.g. `\Cake\Datasource\ResultSetInterface`).
You can also set it to `iterable` (recommended) if you don't use any of the
specific interface methods (just iterating over them):

```
// templateCollectionObject set to 'iterable'
@var iterable<\App\Model\Entity\Article> $articles
```

If you always pass arrays, you can set `IdeHelper.templateCollectionObject` to
`false` to reflect this in the annotations:

```
// arrayAsGenerics false
@var \App\Model\Entity\Article[] $articles

// arrayAsGenerics true
@var array<\App\Model\Entity\Article> $articles
```

## Preemptive Annotating

Using Configure key `'IdeHelper.preemptive'` set to `true` you can be a bit
more preemptive in annotations. E.g. `@var \App\View\AppView $this` will then
always be added to view templates, even if not currently needed. This allows
immediate type-hinting once actually needed; it is recommended to enable this
setting.

## Custom View Class

Using Configure key `'IdeHelper.viewClass'` a custom class name can be set to
use instead of the default. E.g. `'App\View\MyCustomAppView'` or
`MyCustomAppView::class` (incl. `use` statement).


================================================
FILE: docs/annotations/view.md
================================================
# View, Components, Helpers

## View

The `AppView` class should annotate the helpers of the plugins and the app.

```bash
bin/cake annotate view
```

With template content like:

```php
<?php echo $this->My->foo($bar); ?>
<?php if ($this->Configure->baz()) {} ?>
```

the following would be annotated (if the `My` and `Shim.Configure` helpers are
loaded correctly):

```php
/**
 * @property \App\View\Helper\MyHelper $My
 * @property \Shim\View\Helper\ConfigureHelper $Configure
 */
class AppView extends View {
}
```

### Include plugins

Using the Configure key `'IdeHelper.includedPlugins'` you can set an array of
(loaded!) plugins to include. Those will then also be parsed and all found
helpers added to the `AppView` annotations. Setting this to `true` will
auto-include all loaded plugins.

## Components

Components should annotate any component they use.

```bash
bin/cake annotate components
```

A component containing:

```php
    /**
     * @var array
     */
    protected $components = [
        'RequestHandler',
        'Flash.Flash',
    ];
```

would get the following annotations:

```php
/**
 * @property \App\Controller\Component\CheckHttpCacheComponent $CheckHttpCache
 * @property \Flash\Controller\Component\FlashComponent $Flash
 */
```

## Helpers

Helpers should annotate any helper they use.

```bash
bin/cake annotate helpers
```

A helper containing:

```php
    /**
     * @var array
     */
    protected $helpers = [
        'Form',
    ];

    /**
     * @param \Cake\View\View $View
     * @param array $config
     */
    public function __construct(View $View, array $config = []) {
        parent::__construct($View, $config);
        $this->_View->loadHelper('Template');
    }
```

would get the following annotations:

```php
/**
 * @property \Cake\View\Helper\FormHelper $Form
 * @property \App\View\Helper\TemplateHelper $Template
 */
```


================================================
FILE: docs/code-completion/index.md
================================================
# Code Completion File Generator

In contrast to the [PhpStorm meta file generator](/generator/), this tool is
intentionally generic and IDE agnostic.

![Behavior Code Completion](/img/code_completion.png)

## Usage

This command will generate the CodeCompletion `php` files into your app's
`TMP/` directory:

```bash
bin/cake generate code_completion
```

The files should not be persisted — they will always be regenerated or
updated locally if needed.

::: tip composer hook
Set up a `post-install-cmd` hook for composer to keep them up to date
automatically.
:::

## Behaviors

```php
/** @var \Search\Manager $searchManager */
$searchManager = $this->behaviors()->Search->searchManager();
```

So far `$searchManager` required the annotation above to be type-hinted and
clickable, because the magic property access is not resolvable on its own.

With the generated code completion file this is no longer necessary. The
property `Search` is detected as `\Search\Model\Behavior\SearchBehavior`,
making `searchManager()` available in the IDE for method argument checking
and following.

## SelectQuery generics

The code completion generator also ships a `Cake\ORM\Query\SelectQuery`
helper stub for fluent query chains. This is especially useful together with
the model annotation option:

```php
'IdeHelper' => [
    'tableEntityQuery' => true,
],
```

That combination lets IDEs preserve the concrete entity type through calls
such as:

```php
$query = $this->Users->find();
$query->where(['active' => true])->all();
```

The generated stub intentionally focuses on methods where the subject type is
stable or where Cake already has a clear subject transition:

| Flow | Semantic result | ide-helper support |
| --- | --- | --- |
| `$this->Users->find()->where(...)->all()` | `User` entities | Covered via `tableEntityQuery` + `SelectQuery<TSubject>` stub |
| `$this->Users->find('active')->matching('Roles')->contain('Profiles')->all()` | `User` entities | Covered; these fluent methods preserve `TSubject` |
| `$this->Users->find()->disableHydration()->all()` | `array<string, mixed>` rows | Covered; `disableHydration()` switches the query stub to array results |
| `$this->Users->find('list')->all()` | non-entity shaped result | Not forced by default; keep this outside the entity-query assumption |
| `$this->Users->find()->formatResults(...)` | depends on formatter | Not modeled; formatter callbacks can reshape results arbitrarily |
| `$this->Users->find()->mapReduce(...)` | depends on mapper/reducer | Not modeled; map/reduce can reshape results arbitrarily |

This keeps the default helper honest: preserve the type where the query
stays subject-compatible, and avoid pretending that formatter-driven or
list-style flows are still plain entity queries.

For PhpStorm projects you can point the generated code completion files into
`.phpstorm.meta.php/` so they are indexed as local project helpers:

```php
'IdeHelper' => [
    'codeCompletionPath' => ROOT . DS . '.phpstorm.meta.php' . DS,
],
```

Then regenerate the files with:

```bash
bin/cake generate code_completion
```

## Adding Your Own Tasks

Create your own task class:

```php
namespace App\CodeCompletion\Task;

use IdeHelper\CodeCompletion\Task\TaskInterface;

class MyTask implements TaskInterface {

    const TYPE_NAMESPACE = 'Some\Namespace';

    /**
     * @return string
     */
    public function type(): string {
        return static::TYPE_NAMESPACE;
    }

    /**
     * @return string
     */
    public function create(): string {
        // ...
    }

}
```

Then add it to the config:

```php
'IdeHelper' => [
    'codeCompletionTasks' => [
        'MyTask' => \App\CodeCompletion\Task\MyTask::class,
    ],
],
```

The key `'MyTask'` can be any string.

### Replacing native tasks

Using associative arrays you can swap any native task with your own
implementation:

```php
'IdeHelper' => [
    'codeCompletionTasks' => [
        \IdeHelper\CodeCompletion\Task\BehaviorTask::class => \App\CodeCompletion\Task\MyEnhancedBehaviorTask::class,
    ],
],
```

The native class name is the key, your replacement the value. Setting the
value to `null` disables a native task entirely.

### Property example

So let's imagine you have the following magic properties you want to annotate:

```php
$alpha = $someObject->Alpha; // Returns \My\Cool\Alpha class
$beta = $someObject->Beta; // Returns \My\Cool\Beta class
```

Then make sure your task's `create()` method returns something like:

```php
abstract class SomeObject extends SomeObjectInterface {

    /**
     * Alpha class.
     *
     * @var \My\Cool\Alpha
     */
    public $Alpha;

    /**
     * Beta class.
     *
     * @var \My\Cool\Beta
     */
    public $Beta;

}
```

We use the `abstract` keyword to avoid direct implementation hinting.

### Method example

Let's imagine you have the following magic methods you want to annotate:

```php
$alpha = $someObject->alpha(); // Returns \My\Cool\Alpha class
$beta = $someObject->beta(); // Returns \My\Cool\Beta class
```

Then make sure your task's `create()` method returns something like:

```php
abstract class SomeObject extends SomeObjectInterface {

    /**
     * Alpha class.
     *
     * @var \My\Cool\Alpha
     */
    protected $alpha;

    // ...

    /**
     * @return \My\Cool\Alpha
     */
    public function search(): Alpha {
        return $this->alpha;
    }

    // ...

}
```

## Custom Path for Files

Using the Configure key `'IdeHelper.codeCompletionPath'` you can use a custom
path in your project. This way the files can be added to version control.


================================================
FILE: docs/generator/custom-tasks.md
================================================
# Custom Tasks and Directives

## Adding Your Own Tasks

Create your own task class:

```php
namespace App\Generator\Task;

use IdeHelper\Generator\Task\TaskInterface;

class MyTask implements TaskInterface {

    /**
     * @return array<\IdeHelper\Generator\Directive\BaseDirective>
     */
    public function collect(): array {
        // ...
    }

}
```

Then add it to the config:

```php
'IdeHelper' => [
    'generatorTasks' => [
        'MyTask' => \App\Generator\Task\MyTask::class,
    ],
],
```

The key `'MyTask'` can be any string, but it must be unique across all
existing tasks.

## Replacing Native Tasks

Using associative arrays you can swap any native task with your own
implementation:

```php
'IdeHelper' => [
    'generatorTasks' => [
        \IdeHelper\Generator\Task\ModelTask::class => \App\Generator\Task\MyEnhancedModelTask::class,
    ],
],
```

The native class name is the key, your replacement the value. Setting the
value to `null` disables a native task entirely.

## Available Directives

### Override

Most directives used by the built-in tasks are `Override`. They are also the
ones supported the longest. For a specific string method argument, an
`Override` returns a specific object — that covers a lot of CakePHP's
internal magic.

```php
$method = '\Namespace\PackageName\MyFactory::create(0)';
$map = [
    'alpha' => '\My\Cool\Alpha::class',
    'beta' => '\My\Cool\Beta::class',
];
$directive = new Override($method, $map);
```

You can also use the `ClassName` value object together with real `::class`
usage and imports:

```php
use IdeHelper\ValueObject\ClassName;
use My\Cool\Alpha;
use My\Cool\Beta;

$map = [
    'alpha' => ClassName::create(Alpha::class),
    'beta' => ClassName::create(Beta::class),
];
```

### ExpectedArguments

With this you can set default values to choose from for method arguments.
Specify the parameter count as a 0-based value.

```php
$method = '\Namespace\PackageName\MyFactory::create()';
$position = 0;
$list = [
    '\'alpha\'',
    '\'beta\'',
];
$directive = new ExpectedArguments($method, $position, $list);
```

Note the escaped quotes around literal string values. To make it cleaner,
use the `StringName` value object — it auto-quotes on output:

```php
use IdeHelper\ValueObject\StringName;

$list = [
    StringName::create('alpha'),
    StringName::create('beta'),
];
```

### ExpectedReturnValues

You can also set expected return types for a method:

```php
$method = '\Namespace\PackageName\MyFactory::create()';
$list = [
    '\My\Cool\Alpha::class',
    '\My\Cool\Beta::class',
];
$directive = new ExpectedReturnValues($method, $list);
```

### RegisterArgumentsSet

If you reuse the same lists for both arguments and return values, you can
register a set and reuse it in the directives above.

```php
$set = 'mySet';
$list = [
    '\My\Cool\Executer::SUCCESS',
    '\My\Cool\Executer::ERROR',
];
$directive = new RegisterArgumentsSet($set, $list);
```

Now you can use it as the list value `argumentsSet('mySet')` inside the
others. For this just pass the `$directive` object itself to the list, which
then contains only this one element.

You can also use the `LiteralName` value object for constants and anything
that does not need to be output as a string:

```php
use IdeHelper\ValueObject\LiteralName;

$list = [
    LiteralName::create('\My\Cool\Executer::SUCCESS'),
    LiteralName::create('\My\Cool\Executer::ERROR'),
];
```

If you want to reuse existing argument sets from other tasks, use the
`ArgumentsSet` value object referencing them:

```php
use IdeHelper\Generator\Directive\ExpectedArguments;
use IdeHelper\ValueObject\ArgumentsSet;

$method = '\\' . static::CLASS_FORMAT_HELPER . '::sidebarLink()';
$list = [
    ArgumentsSet::create(FormatIconFontAwesome5Task::SET_ICONS_FONTAWESOME),
];
$directive = new ExpectedArguments($method, 1, $list);
```

Just make sure those argument sets are actually available — this is not
checked for you.

### ExitPoint

This directive lets the IDE know what methods abort the current code flow.
The IDE shows an "Unreachable statement" warning and usually highlights the
following code in yellow to inform you.

```php
$directive = new ExitPoint('\My\Class::method()');
```

## Worked Example

Imagine you have the following methods you want to annotate:

```php
$alpha = MyFactory::create('alpha'); // Returns \My\Cool\Alpha class
$beta = MyFactory::create('beta'); // Returns \My\Cool\Beta class
```

Create an `Override` to get the correct class instance returned:

```php
$method = '\Namespace\PackageName\MyFactory::create(0)';
$map = [
    'alpha' => '\My\Cool\Alpha::class',
    'beta' => '\My\Cool\Beta::class',
];
$override = new Override($method, $map);
```

Note that map keys are usually always strings and output auto-quoted by
default. So you can treat them as simple/literal strings.

Now imagine you have multiple class methods that return the same set of
constants. First create the reusable set:

```php
$list = [
    '\My\Cool\Executer::SUCCESS',
    '\My\Cool\Executer::ERROR',
];
$argumentsSet = new RegisterArgumentsSet('mySet', $list);
```

Now you can use it for all methods:

```php
$method = '\My\Cool\Executer::execute()';
$list = [
    $argumentsSet,
];
$expectedReturnValues = new ExpectedReturnValues($method, $list);
```

Make sure your task's `collect()` method returns all of them:

```php
return [
    $override->key() => $override,
    $argumentsSet->key() => $argumentsSet,
    $expectedReturnValues->key() => $expectedReturnValues,
    // ...
];
```

As the key for directive values, always use their `->key()` string.

For more examples and details, see the [PhpStorm Advanced Metadata documentation](https://confluence.jetbrains.com/display/PhpStorm/PhpStorm+Advanced+Metadata).

## Literal Keys

If you really need literal string keys (no auto-quoting), use the `KeyValue`
value object:

```php
$key = ClassName::create(Bar::class);
$value = ClassName::create(Bar::class);
$keyValue = KeyValue::create($key, $value);

// Now use it as any other value
$map = [
    'thisKeyIsOnlyForSortingNow' => $keyValue,
    // ...
];
$directive = new Override('\\' . Table::class . '::returnMy(0)', $map);
```

It allows you to control the quoting of both key and value. The map key here
is only used for sorting.

::: info Override-only
This value object can only be used for the `Override` directive — that's the
one that actually makes use of associative keys.
:::


================================================
FILE: docs/generator/index.md
================================================
# Meta File Generator

![Model Typehinting](/img/model_typehinting.png)

![Model Autocomplete](/img/model_autocomplete.png)

The meta file generator produces `.phpstorm.meta.php` files that PhpStorm and
VS Code (with PHP Intelephense) use to understand factories, magic strings,
and return types throughout the CakePHP code base.

## PhpStorm

This command generates `.ide-helper.meta.php` in your app's
`ROOT/.phpstorm.meta.php/` directory:

```bash
bin/cake generate phpstorm
```

Make sure it is indexed (a restart of PhpStorm may be required).

::: info Why a directory?
We use a directory here to allow custom and manually created meta files
alongside this generated file. Any file inside this directory will be parsed
and used. Prefixing the file with a `.` is recommended so PHPCS skips it
automatically.
:::

## What's Next

- [Available Tasks](./tasks) — every built-in task and what it covers
- [Custom Tasks and Directives](./custom-tasks) — add your own tasks, available directives, examples
- [Operations](./operations) — include/exclude plugins, CI checks, reusing argument sets


================================================
FILE: docs/generator/operations.md
================================================
# Operations

Cross-cutting flags and workflows for the meta file generator.

## Include/Exclude Plugins

Many plugins do not need to be "loaded" — those would normally not be
included in the generator tasks. If you want to add some not-loaded plugins
into the list of plugins to process, use:

```php
'IdeHelper' => [
    'plugins' => [
        'MyNotLoadedPlugin',
        '-BlacklistedLoadedPlugin',
    ],
],
```

With the `-` prefix you can even exclude loaded plugins from being processed.

## CI or Pre-Commit Check

Using `-d` (dry run) you will get error code `2` if the file would need
updating:

```bash
bin/cake generate phpstorm -d
```

This way you can automate the check for CI tooling or commit hooks.

## Reusing Argument Sets

You can reuse argument sets that are present from any of the built-in or your
custom tasks.

In verbose mode the console gives you the available sets for re-use:

```bash
bin/cake generate phpstorm -v
```

You can then directly make use of them in any matching directive (for such
lists):

- `ExpectedArguments`
- `ExpectedReturnValues`


================================================
FILE: docs/generator/tasks.md
================================================
# Available Tasks

The list of built-in tasks shipped with the meta file generator.

## Plugins

In your `Application.php` you can — after composer-requiring (and refreshing
the meta file) — auto-complete the available plugins for your `addPlugin()`
calls:

```php
    public function bootstrap(): void {
        // ...
        $this->addPlugin('TypeHere');
    }
```

This is especially useful for more complex and possibly vendor-prefixed
names (e.g. `'Cake/TwigView'`, note the forward slash).

## Models

```php
/** @var \App\Model\Table\UsersTable $users */
$users = TableRegistry::getTableLocator()->get('Users');
$users->doSomething();
```

So far `$users` required the annotation above to be type-hinted and
clickable. With the generated meta file this is no longer necessary. The
static factory call is detected and `$users` is hinted as
`\App\Model\Table\UsersTable`, making `doSomething()` available in the IDE
for method argument checking and following.

This task also annotates dynamic model factory calls (e.g.
`$this->getTableLocator()->get('Users')`) and `loadModel()` usage.

![Model Autocomplete loadModel](/img/model_autocomplete_loadmodel.png)

If you prefer FQCN as the argument, you still get the benefit for the return
type:

```php
use App\Model\Table\UsersTable;

$users = TableRegistry::getTableLocator()->get(UsersTable::class);
$users->doSomething();
```

It now knows the concrete object of `$users` and can autocomplete the method
call right away. You will not be able to quickly select from a list of input
options in this form, however.

## Entities

The following are auto-completed, for example:

```php
$user->setDirty('field_name');
$user->setError('field_name');
$user->getOriginal('field_name');
// ...
```

## TableAssociations

The following are auto-completed, for example:

```php
$this->belongsTo('Authors');
$this->hasOne('Book');
$this->hasMany('Articles');
$this->belongsToMany('Tags.Tags');
```

## TableFinders

The `'threaded'` string is now auto-completed, for example:

```php
$this->Posts->find('threaded');
```

::: tip Preemptive finders
Using Configure key `'IdeHelper.preemptive'` set to `true`, you can be a bit
more verbose and include all possible custom finders, including those from
behaviors.
:::

![Model Autocomplete finder](/img/model_autocomplete_finder.png)

## Behaviors

The following are auto-completed, for example:

```php
$this->addBehavior('Tools.Slugged');
$this->removeBehavior('Slugged'); // Note the alias without plugin prefix
```

## Components

The following are auto-completed, for example:

```php
$this->loadComponent('My.Useful');
$this->components()->unload('Useful'); // Note the alias without plugin prefix
```

## Helpers

The following are auto-completed, for example:

```php
$this->loadHelper('Tools.Tree');
```

And so is `addHelper()` (added in CakePHP 4.1) on the `ViewBuilder`:

```php
$this->viewBuilder()
    ->addHelper('TinyAuth.AuthUser')
    ->addHelper('Tools.Tree');
```

## Mailers

The following is auto-completed and returns the corresponding Mailer class:

```php
$userMailer = $this->getMailer('User');
```

## Types

In your bootstrap (app or plugin) you might add additional database `Type`
classes, or reconfigure existing ones:

```php
Type::build('date')->useLocaleParser()->setLocaleFormat('d.m.Y');
Type::build('datetime')->useLocaleParser()->setLocaleFormat('d.m.Y H:i');
```

The IDE recognizes the returned type of class and allows auto-complete here,
too. Same for `Type::map()` and type strings like `integer`, `string`, etc.:

```php
Type::map('decimal', ...);
```

## Elements

Heavy users of elements in templates: tired of typing the full template name
in `$this->element('...')` calls? PhpStorm auto-completes this, including all
elements from plugins.

## Layouts

`$this->viewBuilder->setLayout(...)` is auto-completed.

## Cache

`Cache::write()`, `Cache::read()` and other methods are auto-completed for
the cache engine(s) available.

## FormHelper

`$this->Form->control()` is auto-completed for the model fields available.

## Validation

### `Validator::requirePresence()`

![Validation Autocomplete Validator::requirePresence()](/img/validation_autocomplete_validator_require_presence.png)

Now not just `bool`, but also the possible "magic strings" are type-hinted
and usable as single click/enter.

## Request params

`$this->request->getParam()` auto-completes for `prefix`, `controller` and
other common keys.

## Configure keys

![Configure Autocomplete](/img/configure_autocomplete.png)

`Configure::read()` and the other methods are auto-completed for currently
existing keys. Numeric keys are excluded as they are usually not part of an
associative array config.

## ENV keys

`env()` is auto-completed for most common and used keys.

## Translation keys

Using `__()` and `__d()` can be auto-completed based on your project's `.po`
files.

::: info Quoting limitation
PhpStorm is [not smart enough yet](https://youtrack.jetbrains.com/issue/WI-52508)
to auto-adjust any (escaped or not) quotes in your strings. So in those cases
you must use `'` as delimiters for your strings if you want auto-complete:

```php
<?php echo __('A "quoted" string'); ?>
<?php echo __('A \'literally quoted\' string'); ?>
<?php echo __('A variable \'\'{0}\'\' be replaced.', __('will')); ?>
```

Any further `'` inside will be escaped for you.
:::

## ConnectionManager

`ConnectionManager::get()` is auto-completed for the currently configured
connection aliases.

## Fixtures

`TestCase::addFixture()` is auto-completed for the currently available
fixtures from app, core, and plugins.

## Migrations plugin database tables

When using the Migrations plugin, this task comes in handy to quickly
autocomplete existing tables, their column names, and possible column types.

It excludes CakePHP internal tables and all `phinxlog` ones by default. You
can use a regex blacklist to further exclude certain tables:

```php
'IdeHelper' => [
    'skipDatabaseTables' => [
        '/customRegexPattern/',
        // ...
    ],
],
```


================================================
FILE: docs/guide/ide-support.md
================================================
# IDE Support

This plugin is intended to work with **any IDE** that supports annotations and
code completion. Below is the current state of testing and integration.

## Tested IDEs

IDEs tested for full compatibility:

- **[PhpStorm](https://github.com/dereuromark/cakephp-ide-helper/wiki/PHPStorm)** — also supports the meta file generator
- IntelliJ IDEA
- Atom
- **[VS Code](https://github.com/dereuromark/cakephp-ide-helper/wiki/Visual-Studio-Code)** — meta file works via the PHP Intelephense plugin
- Report or open a PR for your IDE on the [wiki](https://github.com/dereuromark/cakephp-ide-helper/wiki) to confirm full compatibility.

## Plugins With Meta File Generator Tasks

The following plugins ship Generator tasks that build on top of this plugin:

- [Migrations](https://github.com/cakephp/migrations) — migration file writing (included in IdeHelper directly).
- [Queue](https://github.com/dereuromark/cakephp-queue) — `QueuedJobsTable::createJob()` usage.
- [Burzum/CakeServiceLayer](https://github.com/burzum/cakephp-service-layer) — `loadService()` usage.
- [CakephpFixtureFactories](https://github.com/vierge-noire/cakephp-fixture-factories) — factory class autocomplete.

Add yours via PR.

## Plugins With Annotator Tasks

- See the [IdeHelperExtra](https://github.com/dereuromark/cakephp-ide-helper-extra) plugin for a curated collection of additional annotator tasks.

## Plugins With Illuminator Tasks

- [StateMachine](https://github.com/spryker/cakephp-statemachine) — syncs states from XML into PHP.

## Sponsorship

[![PhpStorm logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/PhpStorm.svg)](https://jb.gg/OpenSourceSupport)

JetBrains sponsors PhpStorm for the FOSS work on this repository and beyond.


================================================
FILE: docs/guide/index.md
================================================
# Introduction

`cakephp-ide-helper` improves IDE compatibility and uses annotations to make
your IDE understand the "magic" of CakePHP, so you can click through class
methods and object chains and spot obvious issues and mistakes earlier. It
also improves compatibility with tools like [PHPStan](https://github.com/phpstan/phpstan).

> Boost your productivity. Avoid mistakes.

This branch is for use with **CakePHP 5.1+**. For older versions see the
[version map](https://github.com/dereuromark/cakephp-ide-helper/wiki#cakephp-version-map).

## Two Tools, One Plugin

There are two main tools for keeping your code up to date:

| Tool | Modifies | When to use |
|------|----------|-------------|
| **Annotator** | Doc blocks and annotations only — never functional code | Keep type hints in sync as the codebase evolves |
| **Illuminator** | Functional code itself (constants, methods, etc.) | One-shot rewrites — for example, add entity field constants |

On top of those there are two stub-file generators:

| Generator | Output | Audience |
|-----------|--------|----------|
| **Meta File Generator** | `.phpstorm.meta.php/.ide-helper.meta.php` | PhpStorm and VS Code (Intelephense) |
| **Code Completion Generator** | Generic PHP stubs in `TMP/` | Any IDE that indexes PHP files |

Annotations are needed for static analyzers to understand the code; the meta
file and code completion stubs are mainly IDE autocomplete helpers.

## What's Next

1. [Installation](./installation) — Get the plugin set up
2. [Usage](./usage) — High-level commands and recommended composer scripts
3. [IDE Support](./ide-support) — Tested IDEs and plugins-of-this-plugin
4. [Annotations](/annotations/) — Per-class-type annotation reference
5. [Code Completion](/code-completion/), [Generator](/generator/), [Illuminator](/illuminator/)

If you are upgrading from 4.x see [Migrating from 4.x](./migration).


================================================
FILE: docs/guide/installation.md
================================================
# Installation

## Composer

Install as a `require-dev` dependency:

```bash
composer require --dev dereuromark/cakephp-ide-helper
```

## Plugin Loading

Enable the plugin in your `Application.php` or call:

```bash
bin/cake plugin load IdeHelper
```

::: tip Local-only loading
As a `require-dev` dependency, the plugin should only load for local
development. Wrap the registration with a check or `try`/`catch`, and ideally
also restrict it to CLI mode (`if (PHP_SAPI === 'cli')`).
:::

## Verifying the Install

Once the plugin is loaded, the following commands become available:

```bash
bin/cake annotate --help
bin/cake generate --help
bin/cake illuminate --help
```

If those resolve, you are ready to move on to [Usage](./usage).


================================================
FILE: docs/guide/migration.md
================================================
# Migrating from 4.x

A few commands were renamed when moving to 5.x:

| 4.x | 5.x |
|-----|-----|
| `bin/cake code_completion generate` | `bin/cake generate code_completion` |
| `bin/cake phpstorm generate` | `bin/cake generate phpstorm` |
| `bin/cake illuminator` | `bin/cake illuminate code` |

If you have composer scripts or CI jobs referencing the old commands, update
them accordingly.


================================================
FILE: docs/guide/usage.md
================================================
# Usage

A short tour of the high-level commands. Each section has a dedicated guide
with the full set of options.

## Annotations

Run on your app:

```bash
bin/cake annotate [type]
```

By default it prints a diff of the changes plus the number of modified lines.

Run on a loaded plugin:

```bash
bin/cake annotate [type] -p FooBar
```

The plugin is autoloaded if needed (when not manually loaded already).

Use `-v` for verbose output:

```bash
bin/cake annotate [type] -v
```

Add `-d` (`--dry-run`) to simulate the output without modifying files.

See [Annotations](/annotations/) for the full reference.

## Code Completion

The code completion file aims to be generic and to work with all IDEs.

Generate the code completion files into `TMP/`:

```bash
bin/cake generate code_completion
```

See [Code Completion](/code-completion/).

## Meta File Generator

The meta file is supported by:

- PhpStorm (2016.2+)
- VS Code with the [PHP Intelephense](https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client) plugin

Generate the app-level `.phpstorm.meta.php` file:

```bash
bin/cake generate phpstorm
```

See [Generator](/generator/).

## Illuminator

Rewrite PHP files using configured Illuminator tasks:

```bash
bin/cake illuminate code <path>
```

Use `-v` for verbose output and `-t` (`--task`) with a comma-separated list to
limit the run to specific tasks. Add `-d` (`--dry-run`) to simulate.

See [Illuminator](/illuminator/).

## Recommended composer Scripts

Group commands so they are easy to remember and easy to wire into hooks or CI:

```json
"scripts": {
    "setup": "bin/cake generate code_completion && bin/cake generate phpstorm",
    "annotate": "bin/cake annotate all && bin/cake annotate all -p Sandbox",
    "illuminate": "bin/cake illuminate code"
}
```

That way you only have to remember the wrapper commands:

- `composer setup` — also useful as a Git hook after checkout/pull
- `composer annotate` — include all your `/plugins/` (the non-vendor ones)
- `composer illuminate` — include all your `/plugins/` (the non-vendor ones)


================================================
FILE: docs/illuminator/index.md
================================================
# PHP File Illuminator

The Illuminator can modify your PHP files based on Illuminator rule sets. You
can use the pre-set tasks, or create your own to enhance your PHP files and
classes.

::: warning Modifies functional code
Unlike the [Annotator](/annotations/), which only updates doc blocks and
annotations, the Illuminator actually modifies existing code. Make sure to
back up or commit your changes before running it.
:::

Each task has its own scope defined, based on path or filename. If a task's
scope does not match a file, it is skipped.

Use `-p PluginName` to run 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.
:::

## Available Tasks

### EntityField

Your entities expose their fields either via `get()`/`set()` or as class
properties. Especially when using them through methods, you have no
type-hinting/autocomplete on those magic strings. In these cases, having
class constants is the solution.

This task adds them based on the defined property annotations in the doc
block:

```php
/**
 * @property int $id
 * @property string $brand_name
 * @property \Cake\I18n\DateTime $created
 * @property \Cake\I18n\DateTime|null $retired
 * @property \App\Model\Entity\Wheel[] $wheels
 */
class Car extends Entity {

    public const FIELD_ID = 'id';
    public const FIELD_BRAND_NAME = 'brand_name';
    // ...

}
```

This is especially useful for code like:

```php
// old
$carEntity->setDirty('wheels');

// new
$carEntity->setDirty($carEntity::FIELD_WHEELS);
```

or:

```php
// old
$query->orderByDesc('publish_date');

// new
$query->orderByDesc(Post::FIELD_PUBLISH_DATE);
```

This allows for less typing as autocomplete finds it immediately — and for
usage display (IDE → right-click → get usage). That also means refactoring is
much easier this way (via the IDE, usually a clean
one-modification-refactor across the whole project).

::: info Visibility flag
Since PHP 7.1+ this task adds the `public` visibility flag if you don't
configure it otherwise.

This task does not clean out removed or renamed fields. You should quickly
check for usage of the constant — if unused it can be safely removed.
:::

![Fields Autocomplete](/img/fields_autocomplete.png)

## Adding Your Own Tasks

Create your own task class:

```php
namespace App\Illuminator\Task;

use IdeHelper\Illuminator\Task\TaskInterface;

class MyTask implements TaskInterface {

    /**
     * @param string $path
     * @return bool
     */
    public function shouldRun(string $path): bool {
        // ...
    }

    /**
     * @param string $content
     * @param string $path
     * @return string
     */
    public function run(string $content, string $path): string {
        // ...
    }

}
```

Then add it to the config:

```php
'IdeHelper' => [
    'IlluminatorTasks' => [
        'MyTask' => \App\Illuminator\Task\MyTask::class,
    ],
],
```

The key `'MyTask'` can be any string, but it must be unique across all
existing tasks.

### Replacing native tasks

Using associative arrays you can swap any native task with your own
implementation:

```php
'IdeHelper' => [
    'IlluminatorTasks' => [
        \IdeHelper\Illuminator\Task\FooBarTask::class => \App\Illuminator\Task\MyEnhancedFooBarTask::class,
    ],
],
```

The native class name is the key, your replacement the value. Setting the
value to `null` disables a native task entirely.

## Configuration

You can specify specific settings via `app.php` config:

- `'IdeHelper.illuminatorIndentation'` as `'    '` to use spaces as indentation
  whitespace; defaults to `"\t"`.

## Important Constraint

Some tasks may be based on the results of the Annotator. Make sure to run the
Annotator first, e.g.:

```bash
bin/cake annotate all && bin/cake illuminate code <path>
```

## CI or Pre-Commit Check

Using `-d` (dry run) you will get error code `2` if the file would need
updating. This way you can automate the check for CI tooling or commit hooks.


================================================
FILE: docs/index.md
================================================
---
layout: home

hero:
  name: cakephp-ide-helper
  text: IDE Helper for CakePHP
  tagline: Annotations, meta files, code-completion stubs, and Illuminator tasks — boost IDE and static analyzer support, avoid mistakes.
  image:
    src: /logo.svg
    alt: cakephp-ide-helper
  actions:
    - theme: brand
      text: Get Started
      link: /guide/
    - theme: alt
      text: Annotations
      link: /annotations/
    - theme: alt
      text: View on GitHub
      link: https://github.com/dereuromark/cakephp-ide-helper

features:
  - icon: 📝
    title: Annotations Updater
    details: Keep doc blocks and annotations on controllers, models, entities, templates, commands, and more in sync as your code evolves.
  - icon: 🧠
    title: Meta File Generator
    details: Produce `.phpstorm.meta.php` files so PhpStorm and VS Code understand factories, magic strings, and return types.
  - icon: 💡
    title: Code Completion Stubs
    details: IDE-agnostic stub generation for behaviors and SelectQuery generics, so chained ORM calls keep their types.
  - icon: ⚙️
    title: Illuminator Tasks
    details: Rewrite source files where annotations are not enough — for example, generate entity field constants automatically.
  - icon: 🔁
    title: CI and Watcher Friendly
    details: Run as a CI check (`-d --ci`), pre-commit hook, or live file watcher with chokidar or watchexec.
  - icon: 🧱
    title: Extensible
    details: Plug in your own annotators, callback tasks, generator tasks, and Illuminator tasks — replace built-ins or add new ones.
---


================================================
FILE: docs/package.json
================================================
{
  "name": "cakephp-ide-helper-docs",
  "private": true,
  "type": "module",
  "scripts": {
    "docs:dev": "vitepress dev",
    "docs:build": "vitepress build",
    "docs:preview": "vitepress preview"
  },
  "devDependencies": {
    "vitepress": "^1.6.3",
    "vue": "^3.5.13"
  }
}


================================================
FILE: docs/reference/configuration.md
================================================
# Configuration Reference

All Configure options live under the `IdeHelper` key in `app.php`. The
canonical reference with defaults is the `app.example.php` file in the
plugin's `/config/` directory — that file is the source of truth and can be
copy-pasted into your project config.

## Common Keys at a Glance

The list below highlights the keys mentioned across this documentation. For
the full set, defaults, and the most up-to-date options, see
[`config/app.example.php`](https://github.com/dereuromark/cakephp-ide-helper/blob/master/config/app.example.php).

### Annotator

| Key | Type | Notes |
|-----|------|-------|
| `arrayAsGenerics` | `bool` | Use modern `array<type>` instead of legacy `type[]`. |
| `genericsInParam` | `false \| true \| 'detailed'` | Tri-state for table method param types. See [Models](/annotations/models). |
| `tableEntityQuery` | `bool` | Expose entity-aware `find()` return type on tables. |
| `prefixes` | `array` | Whitelist of controller subfolder prefixes. |
| `typeMap` | `array` | Map DB types → PHP types for entity property annotations. |
| `nullableMap` | `array` | Map DB types → nullability flag. |
| `includedPlugins` | `array \| true` | Plugins to include when annotating helpers in `AppView`. |
| `templateExtensions` | `array` | File extensions processed by the templates annotator. Defaults to `['ctp', 'php']`. |
| `skipTemplatePaths` | `array` | Template folders the annotator should skip. `/templates/Bake/` is skipped by default. |
| `templateCollectionObject` | `string \| false` | FQCN (or `iterable`/`false`) used for template collection annotations. |
| `autoCollect` | `bool \| callable` | Auto-collect template variables. |
| `autoCollectBlacklist` | `array` | Strings or regex patterns of variables to exclude from auto-collection. |
| `preemptive` | `bool` | Preemptive annotations (e.g. always add `@var \App\View\AppView $this` to templates). |
| `viewClass` | `string` | Custom AppView FQCN. |
| `preferLinkOverUsesInTests` | `bool` | Use `@link` (default) vs. `@uses` in test class annotations. |
| `annotators` | `array` | Replace or disable native annotators. |
| `classAnnotatorTasks` | `array` | Register or replace class-annotator tasks. |
| `CallbackAnnotatorTasks` | `array` | Register or replace callback-annotator tasks. |

### Generator

| Key | Type | Notes |
|-----|------|-------|
| `plugins` | `array` | Include not-loaded plugins or exclude loaded ones (`-` prefix). |
| `generatorTasks` | `array` | Register or replace generator tasks. |
| `skipDatabaseTables` | `array` | Regex blacklist for the Migrations-tables generator task. |

### Code Completion

| Key | Type | Notes |
|-----|------|-------|
| `codeCompletionPath` | `string` | Custom output path for code completion files (e.g. `ROOT . DS . '.phpstorm.meta.php' . DS`). |
| `codeCompletionTasks` | `array` | Register or replace code completion tasks. |

### Illuminator

| Key | Type | Notes |
|-----|------|-------|
| `illuminatorIndentation` | `string` | Indentation whitespace; defaults to `"\t"`. Use `'    '` for spaces. |
| `IlluminatorTasks` | `array` | Register or replace Illuminator tasks. |

## Replacing or Disabling Native Tasks

For each task type, the registration array uses `'CustomKey' => ClassName`
to add a task, or the native class name as the key to replace one:

```php
'IdeHelper' => [
    'annotators' => [
        // Replace a native annotator
        \IdeHelper\Annotator\EntityAnnotator::class => \App\Annotator\MyEnhancedEntityAnnotator::class,
        // Disable a native annotator
        \IdeHelper\Annotator\HelperAnnotator::class => null,
        // Add a custom annotator
        'MyCustomAnnotator' => \App\Annotator\MyCustomAnnotator::class,
    ],
],
```

The same pattern applies to `classAnnotatorTasks`, `CallbackAnnotatorTasks`,
`generatorTasks`, `codeCompletionTasks`, and `IlluminatorTasks`.


================================================
FILE: docs/reference/contributing.md
================================================
# Contributing

## Basics

See the composer scripts:

```bash
composer cs-check
composer cs-fix
composer test-setup
composer test
```

## Generator

### New meta file

Run the test with the `--debug` option to generate a new `TMP/.meta.php`:

```bash
php phpunit.phar tests/TestCase/Generator/PhpstormGeneratorTest.php --debug
```

You can then easily copy it over into the `test_files/` directory and replace
the existing one.


================================================
FILE: phpcs.xml
================================================
<?xml version="1.0"?>
<ruleset name="plugin">
	<arg name="cache" value=".phpcs.cache"/>
	<rule ref="PSR2R"/>

    <arg value="nps"/>

    <file>config/</file>
    <file>src/</file>
    <file>tests/</file>

    <exclude-pattern>/tests/test_files/</exclude-pattern>
    <exclude-pattern>/tests/test_app/</exclude-pattern>
</ruleset>


================================================
FILE: phpstan.neon
================================================
parameters:
	level: 8
	paths:
		- src/

	treatPhpDocTypesAsCertain: false
	bootstrapFiles:
		- tests/bootstrap.php
		- tests/shim.php
	ignoreErrors:
		- '#Unsafe usage of new static\(\).+#'
		- '#Parameter \#1 \$object of function get_class expects object, object\|string given.#'
		-
			message: '#(unknown class Phinx\\|invalid return type Phinx\\|undefined method Migrations\\Migrations)#'
			path: src/Generator/Task/DatabaseTableColumnTypeTask.php


================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		 colors="true"
		 bootstrap="tests/bootstrap.php"
		 xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.2/phpunit.xsd">
	<php>
		<ini name="memory_limit" value="-1"/>
		<!-- E_ALL => 32767 -->
		<!-- E_ALL & ~E_USER_DEPRECATED => 16383 -->
		<ini name="error_reporting" value="32767"/>

		<env name="FIXTURE_SCHEMA_METADATA" value="tests/schema.php"/>
	</php>

	<testsuites>
		<testsuite name="ide-helper">
			<directory>tests/TestCase/</directory>
		</testsuite>
	</testsuites>

	<extensions>
		<bootstrap class="Cake\TestSuite\Fixture\Extension\PHPUnitExtension"/>
	</extensions>

	<source>
		<include>
			<directory suffix=".php">src/</directory>
		</include>
	</source>
</phpunit>


================================================
FILE: src/Annotation/AbstractAnnotation.php
================================================
<?php

namespace IdeHelper\Annotation;

use RuntimeException;

abstract class AbstractAnnotation implements AnnotationInterface, ReplacableAnnotationInterface {

	/**
	 * @var string
	 */
	public const TAG = '';

	protected string $type;

	protected ?int $index;

	/**
	 * Needed for removing annotations
	 */
	protected bool $isInUse = false;

	/**
	 * @param string $type
	 * @param int|null $index
	 */
	public function __construct(string $type, ?int $index = null) {
		$this->type = $type;
		$this->index = $index;
	}

	/**
	 * @return string
	 */
	public function __toString(): string {
		return static::TAG . ' ' . $this->build();
	}

	/**
	 * @return string
	 */
	public function getType(): string {
		return $this->type;
	}

	/**
	 * @return bool
	 */
	public function hasIndex(): bool {
		return $this->index !== null;
	}

	/**
	 * @throws \RuntimeException
	 * @return int
	 */
	public function getIndex(): int {
		$index = $this->index;
		if ($index === null) {
			throw new RuntimeException('You cannot get an non-defined index. You can check with hasIndex() before calling this method.');
		}

		return $index;
	}

	/**
	 * @param bool $inUse
	 *
	 * @return void
	 */
	public function setInUse(bool $inUse = true): void {
		$this->isInUse = $inUse;
	}

	/**
	 * @return bool
	 */
	public function isInUse(): bool {
		return $this->isInUse;
	}

}


================================================
FILE: src/Annotation/AnnotationFactory.php
================================================
<?php

namespace IdeHelper\Annotation;

use RuntimeException;

class AnnotationFactory {

	protected string $property;

	/**
	 * @param string $tag
	 * @param string $type
	 * @param string|null $content
	 * @param int|null $index
	 * @return \IdeHelper\Annotation\AbstractAnnotation|null
	 */
	public static function create($tag, $type, $content = null, $index = null) {
		switch ($tag) {
			case PropertyAnnotation::TAG:
				return new PropertyAnnotation($type, (string)$content, $index);
			case PropertyReadAnnotation::TAG:
				return new PropertyReadAnnotation($type, (string)$content, $index);
			case MethodAnnotation::TAG:
				return new MethodAnnotation($type, (string)$content, $index);
			case VariableAnnotation::TAG:
				return new VariableAnnotation($type, (string)$content, $index);
			case MixinAnnotation::TAG:
				if ($content) {
					$type .= ' ' . $content;
				}

				return new MixinAnnotation($type, $index);
			case ParamAnnotation::TAG:
				return new ParamAnnotation($type, (string)$content, $index);
			case UsesAnnotation::TAG:
				if ($content) {
					$type .= ' ' . $content;
				}

				return new UsesAnnotation($type, $index);
			case ExtendsAnnotation::TAG:
				if ($content) {
					$type .= ' ' . $content;
				}

				return new ExtendsAnnotation($type, $index);
			case LinkAnnotation::TAG:
				if ($content) {
					$type .= ' ' . $content;
				}

				return new LinkAnnotation($type, $index);
			case SeeAnnotation::TAG:
				if ($content) {
					$type .= ' ' . $content;
				}

				return new SeeAnnotation($type, $index);
		}

		return null;
	}

	/**
	 * @param string $annotation (e.g. `@method \Foo\Bar myMethod($x, $y)`)
	 *
	 * @return \IdeHelper\Annotation\AbstractAnnotation|null
	 */
	public static function createFromString($annotation) {
		preg_match('/^@mixin (.+)\s*(.+)?$/', $annotation, $matches);
		if ($matches) {
			return static::create(MixinAnnotation::TAG, $matches[1]);
		}
		preg_match('/^@uses (.+)\s*(.+)?$/', $annotation, $matches);
		if ($matches) {
			return static::create(UsesAnnotation::TAG, $matches[1]);
		}
		preg_match('/^@extends ([^\s!]+<.*?>)(?:\s*([!]))?/', $annotation, $matches);
		if ($matches) {
			$string = implode(' ', $matches);

			return static::create(ExtendsAnnotation::TAG, $string);
		}

		// Split `@tag <type> <rest>`: the type may contain spaces inside generic
		// brackets (e.g. `Foo<int, Bar>`), so naive `[^ ]+` would cut it short.
		// Walk the type char-by-char, tracking `<>` depth, and stop at the first
		// top-level space.
		if (preg_match('/^(@property|@property-read|@method|@var|@param) (.+)$/', $annotation, $matches)) {
			$tag = $matches[1];
			$rest = $matches[2];
			$depth = 0;
			$len = strlen($rest);
			$split = -1;
			for ($i = 0; $i < $len; $i++) {
				$c = $rest[$i];
				if ($c === '<') {
					$depth++;
				} elseif ($c === '>') {
					$depth = max(0, $depth - 1);
				} elseif ($c === ' ' && $depth === 0) {
					$split = $i;

					break;
				}
			}
			if ($split <= 0) {
				return null;
			}
			$type = substr($rest, 0, $split);
			$content = substr($rest, $split + 1);

			return static::create($tag, $type, $content);
		}

		return null;
	}

	/**
	 * @param string $tag
	 * @param string $type
	 * @param string|null $content
	 * @param int|null $index
	 * @throws \RuntimeException
	 * @return \IdeHelper\Annotation\AbstractAnnotation
	 */
	public static function createOrFail($tag, $type, $content = null, $index = null) {
		$annotation = static::create($tag, $type, $content, $index);
		if (!$annotation) {
			throw new RuntimeException('Cannot create annotation for tag ' . $tag . ', type ' . $type);
		}

		return $annotation;
	}

}


================================================
FILE: src/Annotation/AnnotationInterface.php
================================================
<?php

namespace IdeHelper\Annotation;

interface AnnotationInterface {

	/**
	 * @return string
	 */
	public function getDescription(): string;

	/**
	 * @return string
	 */
	public function build(): string;

}


================================================
FILE: src/Annotation/ExtendsAnnotation.php
================================================
<?php

namespace IdeHelper\Annotation;

class ExtendsAnnotation extends AbstractAnnotation {

	/**
	 * @var string
	 */
	public const TAG = '@extends';

	protected string $description;

	/**
	 * @param string $type
	 * @param int|null $index
	 */
	public function __construct(string $type, ?int $index = null) {
		$description = '';

		$closingBracket = strrpos($type, '>');
		if ($closingBracket) {
			$description = substr($type, $closingBracket + 1);
			$description = trim($description);
			$type = substr($type, 0, $closingBracket + 1);
		}

		parent::__construct($type, $index);

		$this->description = $description;
	}

	/**
	 * @return string
	 */
	public function getDescription(): string {
		return $this->description;
	}

	/**
	 * @return string
	 */
	public function build(): string {
		$description = $this->description !== '' ? (' ' . $this->description) : '';

		return $this->type . $description;
	}

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\ExtendsAnnotation $annotation
	 *
	 * @return bool
	 */
	public function matches(AbstractAnnotation $annotation): bool {
		if (!$annotation instanceof self) {
			return false;
		}

		// Always matches as there can only be one per docblock
		return true;
	}

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\ExtendsAnnotation $annotation
	 * @return void
	 */
	public function replaceWith(AbstractAnnotation $annotation): void {
		$this->type = $annotation->getType();
	}

}


================================================
FILE: src/Annotation/LinkAnnotation.php
================================================
<?php

namespace IdeHelper\Annotation;

class LinkAnnotation extends AbstractAnnotation {

	/**
	 * @var string
	 */
	public const TAG = '@link';

	protected string $description;

	/**
	 * @param string $type
	 * @param int|null $index
	 */
	public function __construct($type, $index = null) {
		$description = '';
		if (str_contains($type, ' ')) {
			[$type, $description] = explode(' ', $type, 2);
		}

		parent::__construct($type, $index);

		if (preg_match('/^(http|https):/', $type)) {
			$this->isInUse = true;
		}

		$this->description = $description;
	}

	/**
	 * @return string
	 */
	public function getDescription(): string {
		return $this->description;
	}

	/**
	 * @return string
	 */
	public function build(): string {
		$description = $this->description !== '' ? (' ' . $this->description) : '';

		return $this->type . $description;
	}

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MixinAnnotation $annotation
	 *
	 * @return bool
	 */
	public function matches(AbstractAnnotation $annotation): bool {
		if (!$annotation instanceof self) {
			return false;
		}
		if ($annotation->getType() !== $this->type) {
			return false;
		}

		return true;
	}

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MixinAnnotation $annotation
	 * @return void
	 */
	public function replaceWith(AbstractAnnotation $annotation): void {
		$this->type = $annotation->getType();
	}

}


================================================
FILE: src/Annotation/MethodAnnotation.php
================================================
<?php

namespace IdeHelper\Annotation;

class MethodAnnotation extends AbstractAnnotation {

	/**
	 * @var string
	 */
	public const TAG = '@method';

	protected string $method;

	protected string $description;

	/**
	 * @param string $type
	 * @param string $method
	 * @param int|null $index
	 */
	public function __construct($type, $method, $index = null) {
		parent::__construct($type, $index);

		$description = '';
		$closingBrace = strrpos($method, ') ');
		if ($closingBrace !== false && $closingBrace !== strlen($method) - 1) {
			$description = substr($method, $closingBrace + 2);
			$method = substr($method, 0, $closingBrace + 1);
		}

		$this->method = $method;
		$this->description = $description;
	}

	/**
	 * @return string
	 */
	public function getMethod(): string {
		return $this->method;
	}

	/**
	 * @return string
	 */
	public function getDescription(): string {
		return $this->description;
	}

	/**
	 * @return string
	 */
	public function build(): string {
		$description = $this->description !== '' ? (' ' . $this->description) : '';

		return $this->type . ' ' . $this->method . $description;
	}

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MethodAnnotation $annotation
	 *
	 * @return bool
	 */
	public function matches(AbstractAnnotation $annotation): bool {
		if (!$annotation instanceof self) {
			return false;
		}
		$methodName = substr($annotation->getMethod(), 0, strpos($annotation->getMethod(), '(') ?: 0);
		if ($methodName !== substr($this->method, 0, strpos($this->method, '(') ?: 0)) {
			return false;
		}

		return true;
	}

	/**
	 * @param \IdeHelper\Annotation\MethodAnnotation $annotation
	 * @return void
	 */
	public function replaceWith(AbstractAnnotation $annotation): void {
		$this->type = $annotation->getType();
		$this->method = $annotation->getMethod();
	}

}


================================================
FILE: src/Annotation/MixinAnnotation.php
================================================
<?php

namespace IdeHelper\Annotation;

class MixinAnnotation extends AbstractAnnotation {

	/**
	 * @var string
	 */
	public const TAG = '@mixin';

	protected string $description;

	/**
	 * @param string $type
	 * @param int|null $index
	 */
	public function __construct($type, $index = null) {
		$description = '';
		if (str_contains($type, ' ')) {
			[$type, $description] = explode(' ', $type, 2);
		}

		parent::__construct($type, $index);

		$this->description = $description;
	}

	/**
	 * @return string
	 */
	public function getDescription(): string {
		return $this->description;
	}

	/**
	 * @return string
	 */
	public function build(): string {
		$description = $this->description !== '' ? (' ' . $this->description) : '';

		return $this->type . $description;
	}

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MixinAnnotation $annotation
	 *
	 * @return bool
	 */
	public function matches(AbstractAnnotation $annotation): bool {
		if (!$annotation instanceof self) {
			return false;
		}
		if ($annotation->getType() !== $this->type) {
			return false;
		}

		return true;
	}

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MixinAnnotation $annotation
	 * @return void
	 */
	public function replaceWith(AbstractAnnotation $annotation): void {
		$this->type = $annotation->getType();
	}

}


================================================
FILE: src/Annotation/ParamAnnotation.php
================================================
<?php

namespace IdeHelper\Annotation;

class ParamAnnotation extends AbstractAnnotation {

	/**
	 * @var string
	 */
	public const TAG = '@param';

	protected string $variable;

	protected string $description;

	/**
	 * @param string $type
	 * @param string $variable
	 * @param int|null $index
	 */
	public function __construct($type, $variable, $index = null) {
		parent::__construct($type, $index);

		$description = '';
		if (str_contains($variable, ' ')) {
			[$variable, $description] = explode(' ', $variable, 2);
		}

		$this->variable = $variable;
		$this->description = $description;
	}

	/**
	 * @return string
	 */
	public function getVariable(): string {
		return $this->variable;
	}

	/**
	 * @return string
	 */
	public function getDescription(): string {
		return $this->description;
	}

	/**
	 * @return string
	 */
	public function build(): string {
		$description = $this->description !== '' ? (' ' . $this->description) : '';

		return $this->type . ' ' . $this->variable . $description;
	}

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\ParamAnnotation $annotation
	 *
	 * @return bool
	 */
	public function matches(AbstractAnnotation $annotation): bool {
		if (!$annotation instanceof self) {
			return false;
		}
		if ($annotation->getVariable() !== $this->variable) {
			return false;
		}

		return true;
	}

	/**
	 * @param \IdeHelper\Annotation\ParamAnnotation $annotation
	 * @return void
	 */
	public function replaceWith(AbstractAnnotation $annotation): void {
		$this->type = $annotation->getType();
		$this->variable = $annotation->getVariable();
	}

}


================================================
FILE: src/Annotation/PropertyAnnotation.php
================================================
<?php

namespace IdeHelper\Annotation;

class PropertyAnnotation extends AbstractAnnotation {

	/**
	 * @var string
	 */
	public const TAG = '@property';

	protected string $property;

	protected string $description;

	/**
	 * @param string $type
	 * @param string $property
	 * @param int|null $index
	 */
	public function __construct($type, $property, $index = null) {
		parent::__construct($type, $index);

		$description = '';
		if (str_contains($property, ' ')) {
			[$property, $description] = explode(' ', $property, 2);
		}
		if (!str_starts_with($property, '$')) {
			$property = '$' . $property;
		}

		$this->property = $property;
		$this->description = $description;
	}

	/**
	 * @return string
	 */
	public function getProperty(): string {
		return $this->property;
	}

	/**
	 * @return string
	 */
	public function getDescription(): string {
		return $this->description;
	}

	/**
	 * @return string
	 */
	public function build(): string {
		$description = $this->description !== '' ? (' ' . $this->description) : '';

		return $this->type . ' ' . $this->property . $description;
	}

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\PropertyAnnotation $annotation
	 *
	 * @return bool
	 */
	public function matches(AbstractAnnotation $annotation): bool {
		if (!$annotation instanceof self) {
			return false;
		}
		if ($annotation->getProperty() !== $this->property) {
			return false;
		}

		return true;
	}

	/**
	 * @param \IdeHelper\Annotation\PropertyAnnotation $annotation
	 * @return void
	 */
	public function replaceWith(AbstractAnnotation $annotation): void {
		$this->type = $annotation->getType();
		$this->property = $annotation->getProperty();
	}

}


================================================
FILE: src/Annotation/PropertyReadAnnotation.php
================================================
<?php

namespace IdeHelper\Annotation;

class PropertyReadAnnotation extends PropertyAnnotation {

	/**
	 * @var string
	 */
	public const TAG = '@property-read';

}


================================================
FILE: src/Annotation/ReplacableAnnotationInterface.php
================================================
<?php

namespace IdeHelper\Annotation;

interface ReplacableAnnotationInterface {

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation $annotation
	 *
	 * @return bool
	 */
	public function matches(AbstractAnnotation $annotation): bool;

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation $annotation
	 *
	 * @return void
	 */
	public function replaceWith(AbstractAnnotation $annotation): void;

}


================================================
FILE: src/Annotation/SeeAnnotation.php
================================================
<?php

namespace IdeHelper\Annotation;

class SeeAnnotation extends AbstractAnnotation {

	/**
	 * @var string
	 */
	public const TAG = '@see';

	protected string $description;

	/**
	 * @param string $type
	 * @param int|null $index
	 */
	public function __construct($type, $index = null) {
		$description = '';
		if (str_contains($type, ' ')) {
			[$type, $description] = explode(' ', $type, 2);
		}

		parent::__construct($type, $index);

		$this->description = $description;
	}

	/**
	 * @return string
	 */
	public function getDescription(): string {
		return $this->description;
	}

	/**
	 * @return string
	 */
	public function build(): string {
		$description = $this->description !== '' ? (' ' . $this->description) : '';

		return $this->type . $description;
	}

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MixinAnnotation $annotation
	 *
	 * @return bool
	 */
	public function matches(AbstractAnnotation $annotation): bool {
		if (!$annotation instanceof self) {
			return false;
		}
		if ($annotation->getType() !== $this->type) {
			return false;
		}

		return true;
	}

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MixinAnnotation $annotation
	 * @return void
	 */
	public function replaceWith(AbstractAnnotation $annotation): void {
		$this->type = $annotation->getType();
	}

}


================================================
FILE: src/Annotation/UsesAnnotation.php
================================================
<?php

namespace IdeHelper\Annotation;

class UsesAnnotation extends AbstractAnnotation {

	/**
	 * @var string
	 */
	public const TAG = '@uses';

	protected string $description;

	/**
	 * @param string $type
	 * @param int|null $index
	 */
	public function __construct($type, $index = null) {
		$description = '';
		if (str_contains($type, ' ')) {
			[$type, $description] = explode(' ', $type, 2);
		}

		parent::__construct($type, $index);

		$this->description = $description;
	}

	/**
	 * @return string
	 */
	public function getDescription(): string {
		return $this->description;
	}

	/**
	 * @return string
	 */
	public function build(): string {
		$description = $this->description !== '' ? (' ' . $this->description) : '';

		return $this->type . $description;
	}

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MixinAnnotation $annotation
	 *
	 * @return bool
	 */
	public function matches(AbstractAnnotation $annotation): bool {
		if (!$annotation instanceof self) {
			return false;
		}
		if ($annotation->getType() !== $this->type) {
			return false;
		}

		return true;
	}

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MixinAnnotation $annotation
	 * @return void
	 */
	public function replaceWith(AbstractAnnotation $annotation): void {
		$this->type = $annotation->getType();
	}

}


================================================
FILE: src/Annotation/VariableAnnotation.php
================================================
<?php

namespace IdeHelper\Annotation;

class VariableAnnotation extends AbstractAnnotation {

	/**
	 * @var string
	 */
	public const TAG = '@var';

	protected string $variable;

	protected string $description;

	protected bool $guessed = false;

	/**
	 * @param string $type
	 * @param string $variable
	 * @param int|null $index
	 */
	public function __construct($type, $variable, $index = null) {
		parent::__construct($type, $index);

		$description = '';
		if (str_contains($variable, ' ')) {
			[$variable, $description] = explode(' ', $variable, 2);
		}
		$this->variable = $variable;
		$this->description = $description;
	}

	/**
	 * @return string
	 */
	public function getVariable(): string {
		return $this->variable;
	}

	/**
	 * @return string
	 */
	public function getDescription(): string {
		return $this->description;
	}

	/**
	 * @param bool $value
	 *
	 * @return $this
	 */
	public function setGuessed($value) {
		$this->guessed = $value;

		return $this;
	}

	/**
	 * @return bool
	 */
	public function getGuessed() {
		return $this->guessed;
	}

	/**
	 * @return string
	 */
	public function build(): string {
		$description = $this->description !== '' ? (' ' . $this->description) : '';

		return $this->type . ' ' . $this->variable . $description;
	}

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\VariableAnnotation $annotation
	 *
	 * @return bool
	 */
	public function matches(AbstractAnnotation $annotation): bool {
		if (!$annotation instanceof self) {
			return false;
		}
		if ($annotation->getVariable() !== $this->variable) {
			return false;
		}

		return true;
	}

	/**
	 * @param \IdeHelper\Annotation\VariableAnnotation $annotation
	 * @return void
	 */
	public function replaceWith(AbstractAnnotation $annotation): void {
		$newType = $annotation->getType();

		// Preserve |null from existing type when the new type doesn't have it
		if ($this->hasNull() && !$this->typeHasNull($newType)) {
			$newType .= '|null';
		}

		$this->type = $newType;
		$this->variable = $annotation->getVariable();
	}

	/**
	 * @return bool
	 */
	protected function hasNull(): bool {
		return $this->typeHasNull($this->type);
	}

	/**
	 * @param string $type
	 * @return bool
	 */
	protected function typeHasNull(string $type): bool {
		return str_contains($type, '|null') || str_contains($type, 'null|') || $type === 'null';
	}

}


================================================
FILE: src/Annotator/AbstractAnnotator.php
================================================
<?php

namespace IdeHelper\Annotator;

use Bake\View\Helper\DocBlockHelper;
use Cake\Core\Configure;
use Cake\Core\InstanceConfigTrait;
use Cake\View\View;
use IdeHelper\Annotation\AbstractAnnotation;
use IdeHelper\Annotation\AnnotationFactory;
use IdeHelper\Annotation\ExtendsAnnotation;
use IdeHelper\Annotation\LinkAnnotation;
use IdeHelper\Annotation\MethodAnnotation;
use IdeHelper\Annotation\MixinAnnotation;
use IdeHelper\Annotation\PropertyAnnotation;
use IdeHelper\Annotation\PropertyReadAnnotation;
use IdeHelper\Annotation\UsesAnnotation;
use IdeHelper\Annotation\VariableAnnotation;
use IdeHelper\Annotator\Traits\DocBlockTrait;
use IdeHelper\Annotator\Traits\FileTrait;
use IdeHelper\Console\Io;
use IdeHelper\Utility\App;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
use PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode;
use ReflectionClass;
use RuntimeException;
use SebastianBergmann\Diff\Differ;
use SebastianBergmann\Diff\Output\DiffOnlyOutputBuilder;

$composerVendorDir = getcwd() . DS . 'vendor';
$codesnifferDir = 'squizlabs' . DS . 'php_codesniffer';
if (!is_dir($composerVendorDir . DS . $codesnifferDir)) {
	$ideHelperDir = substr(__DIR__, 0, strpos(__DIR__, DS . 'cakephp-ide-helper') ?: 0);
	$composerVendorDir = dirname($ideHelperDir);
}
$manualAutoload = $composerVendorDir . DS . $codesnifferDir . DS . 'autoload.php';
if (!class_exists(Config::class) && file_exists($manualAutoload)) {
	require $manualAutoload;
}

abstract class AbstractAnnotator {

	use FileTrait;
	use InstanceConfigTrait;
	use DocBlockTrait;

	/**
	 * @var string
	 */
	public const CONFIG_DRY_RUN = 'dry-run';

	/**
	 * @var string
	 */
	public const CONFIG_PLUGIN = 'plugin';

	/**
	 * @var string
	 */
	public const CONFIG_NAMESPACE = 'namespace';

	/**
	 * @var string
	 */
	public const CONFIG_VERBOSE = 'verbose';

	/**
	 * @var string
	 */
	public const CONFIG_REMOVE = 'remove';

	/**
	 * @var string
	 */
	public const COUNT_REMOVED = 'removed';

	/**
	 * @var string
	 */
	public const COUNT_UPDATED = 'updated';

	/**
	 * @var string
	 */
	public const COUNT_ADDED = 'added';

	/**
	 * @var string
	 */
	public const COUNT_SKIPPED = 'skipped';

	/**
	 * @var array<string>
	 */
	public const TYPES = [
		PropertyAnnotation::TAG,
		PropertyReadAnnotation::TAG,
		VariableAnnotation::TAG,
		MethodAnnotation::TAG,
		MixinAnnotation::TAG,
		UsesAnnotation::TAG,
		LinkAnnotation::TAG,
		ExtendsAnnotation::TAG,
	];

	public static bool $output = false;

	protected Io $_io;

	/**
	 * @var array<string, mixed>
	 */
	protected array $_defaultConfig = [
		self::CONFIG_PLUGIN => null,
	];

	/**
	 * @param \IdeHelper\Console\Io $io
	 * @param array<string, mixed> $config
	 */
	public function __construct(Io $io, array $config) {
		$this->_io = $io;
		$this->setConfig($config);

		$appNamespace = Configure::read('App.namespace', 'App');
		$namespace = $this->getConfig(static::CONFIG_PLUGIN) ?: $appNamespace;
		$namespace = str_replace('/', '\\', $namespace);
		$this->setConfig(static::CONFIG_NAMESPACE, $namespace);
	}

	/**
	 * @param string $path Path to file.
	 * @return bool
	 */
	abstract public function annotate(string $path): bool;

	/**
	 * @param string $oldContent
	 * @param string $newContent
	 * @return void
	 */
	protected function displayDiff(string $oldContent, string $newContent): void {
		$differ = new Differ(new DiffOnlyOutputBuilder());
		$array = $differ->diffToArray($oldContent, $newContent);

		$begin = null;
		$end = null;
		/**
		 * @var int $key
		 */
		foreach ($array as $key => $row) {
			if ($row[1] === 0) {
				continue;
			}

			if ($begin === null) {
				$begin = $key;
			}
			$end = $key;
		}
		if ($begin === null) {
			return;
		}
		$firstLineOfOutput = $begin > 0 ? $begin - 1 : 0;
		$lastLineOfOutput = count($array) - 1 > $end ? $end + 1 : $end;

		for ($i = $firstLineOfOutput; $i <= $lastLineOfOutput; $i++) {
			$row = $array[$i];

			$char = ' ';
			$output = trim($row[0], "\n\r\0\x0B");

			if ($row[1] === 1) {
				$char = '+';
				$this->_io->info('   | ' . $char . $output);
			} elseif ($row[1] === 2) {
				$char = '-';
				$this->_io->out('<warning>' . '   | ' . $char . $output . '</warning>');
			} else {
				$this->_io->out('   | ' . $char . $output);
			}
		}
	}

	/**
	 * @param string $path
	 * @param string $contents
	 * @throws \RuntimeException When the file cannot be written.
	 * @return void
	 */
	protected function storeFile(string $path, string $contents): void {
		static::$output = true;

		if ($this->getConfig(static::CONFIG_DRY_RUN)) {
			return;
		}

		if (file_exists($path) && md5_file($path) === md5($contents)) {
			return;
		}

		if (file_put_contents($path, $contents) === false) {
			throw new RuntimeException(sprintf('Failed to write file `%s`.', $path));
		}
	}

	/**
	 * @var array<string, int>
	 */
	protected array $_counter = [];

	/**
	 * @param string $path
	 * @param string $content
	 * @param array<\IdeHelper\Annotation\AbstractAnnotation> $annotations
	 *
	 * @return bool
	 */
	protected function annotateContent(string $path, string $content, array $annotations): bool {
		if (!count($annotations)) {
			return false;
		}

		$file = $this->getFile($path);

		$classOrTraitIndex = $file->findNext([T_CLASS, T_TRAIT], 0);
		if (!$classOrTraitIndex) {
			return false;
		}
		$beginningOfLineIndex = $this->beginningOfLine($file, $classOrTraitIndex);
		$beginningOfLineIndex = $this->skipOverAttributes($file, $beginningOfLineIndex);

		$closeTagIndex = $this->findDocBlockCloseTagIndex($file, $beginningOfLineIndex);
		$this->resetCounter();
		if ($closeTagIndex && $this->shouldSkip($file, $closeTagIndex)) {
			return false;
		}

		if ($closeTagIndex && !$this->isInlineDocBlock($file, $closeTagIndex)) {
			$newContent = $this->appendToExistingDocBlock($file, $closeTagIndex, $annotations);
		} else {
			$newContent = $this->addNewDocBlock($file, $beginningOfLineIndex, $annotations);
		}

		if ($newContent === $content) {
			$this->reportSkipped($path);

			return false;
		}

		if (!$this->getConfig('verbose')) {
			$this->_io->out('-> ' . str_replace(ROOT . DS, '', $path));
		}

		$this->displayDiff($content, $newContent);
		$this->storeFile($path, $newContent);

		$this->report();

		return true;
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param int $index First functional code after docblock
	 *
	 * @return int|null
	 */
	protected function findDocBlockCloseTagIndex(File $file, int $index): ?int {
		$prevCode = $file->findPrevious(Tokens::$emptyTokens, $index - 1, null, true);
		if (!$prevCode) {
			return null;
		}

		return $file->findPrevious(T_DOC_COMMENT_CLOSE_TAG, $index - 1, $prevCode) ?: null;
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param int $docBlockCloseIndex
	 * @param array<\IdeHelper\Annotation\AbstractAnnotation> $annotations
	 *
	 * @throws \RuntimeException
	 *
	 * @return string
	 */
	protected function appendToExistingDocBlock(File $file, int $docBlockCloseIndex, array &$annotations): string {
		$existingAnnotations = $this->parseExistingAnnotations($file, $docBlockCloseIndex);

		$generatedTags = array_unique(array_map(static function ($annotation) {
			return $annotation::TAG;
		}, $annotations));

		$replacingAnnotations = [];
		$addingAnnotations = [];
		foreach ($annotations as $key => $annotation) {
			if (!is_object($annotation)) {
				throw new RuntimeException('Must be object: ' . print_r($annotation, true));
			}
			if ($this->exists($annotation, $existingAnnotations)) {
				unset($annotations[$key]);

				continue;
			}

			if (!$this->allowsReplacing($annotation, $existingAnnotations)) {
				unset($annotations[$key]);
				$this->_counter[static::COUNT_SKIPPED]++;

				continue;
			}

			$toBeReplaced = $this->needsReplacing($annotation, $existingAnnotations);
			if (!$toBeReplaced) {
				$addingAnnotations[] = $annotation;

				continue;
			}

			$replacingAnnotations[] = $toBeReplaced;
		}

		$tokens = $file->getTokens();
		$lastTagIndexOfPreviousLine = $docBlockCloseIndex;
		while ($tokens[$lastTagIndexOfPreviousLine]['line'] === $tokens[$docBlockCloseIndex]['line']) {
			$lastTagIndexOfPreviousLine--;
		}

		$needsNewline = $this->needsNewLineInDocBlock($file, $lastTagIndexOfPreviousLine);

		$fixer = $this->getFixer($file);

		$fixer->beginChangeset();

		foreach ($replacingAnnotations as $annotation) {
			$fixer->replaceToken($annotation->getIndex(), $annotation->build());
			$this->_counter[static::COUNT_UPDATED]++;
		}

		if (count($addingAnnotations)) {
			$annotationString = $needsNewline ? ' *' . "\n" : '';
			foreach ($addingAnnotations as $annotation) {
				$annotationString .= ' * ' . $annotation . "\n";
				$this->_counter[static::COUNT_ADDED]++;
			}

			$fixer->addContent($lastTagIndexOfPreviousLine, $annotationString);
		}

		if ($this->getConfig(static::CONFIG_REMOVE)) {
			foreach ($existingAnnotations as $key => $existingAnnotation) {
				if ($existingAnnotation->isInUse()) {
					unset($existingAnnotations[$key]);

					continue;
				}

				if ($existingAnnotation->getDescription() !== '') {
					$this->_counter[static::COUNT_SKIPPED]++;
					unset($existingAnnotations[$key]);

					continue;
				}

				if ($generatedTags && !in_array($existingAnnotation::TAG, $generatedTags, true)) {
					unset($existingAnnotations[$key]);
				}
			}

			$removingAnnotations = $existingAnnotations;
			foreach ($removingAnnotations as $annotation) {
				$lastWhitespaceOfPreviousLine = $this->getLastWhitespaceOfPreviousLine($tokens, $annotation->getIndex());
				$index = $annotation->getIndex();
				for ($i = $lastWhitespaceOfPreviousLine; $i <= $index; $i++) {
					$fixer->replaceToken($i, '');
				}
				$this->_counter[static::COUNT_REMOVED]++;
			}
		}

		$fixer->endChangeset();

		return $fixer->getContents();
	}

	/**
	 * @param array<array<string, mixed>> $tokens
	 * @param int $index
	 *
	 * @return int
	 */
	protected function getLastWhitespaceOfPreviousLine(array $tokens, int $index): int {
		$currentLine = $tokens[$index]['line'];
		$index--;
		while ($tokens[$index]['line'] === $currentLine) {
			$index--;
		}

		return $index;
	}

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation $annotation
	 * @param array<\IdeHelper\Annotation\AbstractAnnotation> $existingAnnotations
	 * @return bool
	 */
	protected function exists(AbstractAnnotation $annotation, array &$existingAnnotations): bool {
		foreach ($existingAnnotations as $key => $existingAnnotation) {
			if ($existingAnnotation->build() === $annotation->build()) {
				unset($existingAnnotations[$key]);

				return true;
			}

			if ($annotation instanceof PropertyAnnotation && $existingAnnotation instanceof PropertyAnnotation) {
				if ($annotation->getProperty() === $existingAnnotation->getProperty() && $annotation->getType() === $existingAnnotation->getType()) {
					unset($existingAnnotations[$key]);

					return true;
				}
			}

			// Let's skip on existing ones that are only guessed.
			if ($annotation instanceof VariableAnnotation && $existingAnnotation instanceof VariableAnnotation) {
				if ($annotation->getVariable() === $existingAnnotation->getVariable() && $annotation->getGuessed()) {
					unset($existingAnnotations[$key]);

					return true;
				}
				// Special case: $this variable should not be duplicated if types also match
				if ($annotation->getVariable() === '$this' && $existingAnnotation->getVariable() === '$this' && $annotation->getType() === $existingAnnotation->getType()) {
					unset($existingAnnotations[$key]);

					return true;
				}
			}
		}

		return false;
	}

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation $annotation
	 * @param array<\IdeHelper\Annotation\AbstractAnnotation> $existingAnnotations
	 * @return \IdeHelper\Annotation\AbstractAnnotation|null
	 */
	protected function needsReplacing(AbstractAnnotation $annotation, array &$existingAnnotations) {
		foreach ($existingAnnotations as $key => $existingAnnotation) {
			if ($existingAnnotation->matches($annotation)) {
				$newAnnotation = clone $existingAnnotation;
				$newAnnotation->replaceWith($annotation);

				unset($existingAnnotations[$key]);

				return $newAnnotation;
			}
		}

		return null;
	}

	/**
	 * @param \IdeHelper\Annotation\AbstractAnnotation $annotation
	 * @param array<\IdeHelper\Annotation\AbstractAnnotation> $existingAnnotations
	 * @return bool
	 */
	protected function allowsReplacing(AbstractAnnotation $annotation, array &$existingAnnotations): bool {
		foreach ($existingAnnotations as $key => $existingAnnotation) {
			if ($existingAnnotation->matches($annotation) && $existingAnnotation->getDescription() !== '') {
				unset($existingAnnotations[$key]);

				return false;
			}
		}

		return true;
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param int $closeTagIndex
	 * @param array<string> $types
	 *
	 * @return array<\IdeHelper\Annotation\AbstractAnnotation>
	 */
	protected function parseExistingAnnotations(File $file, int $closeTagIndex, array $types = self::TYPES): array {
		$tokens = $file->getTokens();

		/** @var int $startTagIndex */
		$startTagIndex = $tokens[$closeTagIndex]['comment_opener'];

		$annotations = [];
		for ($i = $startTagIndex + 1; $i < $closeTagIndex; $i++) {
			if ($tokens[$i]['type'] !== 'T_DOC_COMMENT_TAG') {
				continue;
			}
			if (!in_array($tokens[$i]['content'], $types, true)) {
				continue;
			}

			$classNameIndex = $i + 2;

			if ($tokens[$classNameIndex]['type'] !== 'T_DOC_COMMENT_STRING') {
				continue;
			}

			$content = $tokens[$classNameIndex]['content'];

			/** @var \PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode|\PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode $valueNode */
			$valueNode = static::getValueNode($tokens[$i]['content'], $content);
			if ($valueNode instanceof InvalidTagValueNode) {
				$multilineFixed = false;
				for ($p = $i + 3; $p < $closeTagIndex; $p++) {
					if ($tokens[$p]['type'] === 'T_DOC_COMMENT_TAG') {
						break;
					}

					if ($tokens[$p]['type'] !== 'T_DOC_COMMENT_STRING') {
						continue;
					}

					$content .= $tokens[$p]['content'];
					/** @var \PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode|\PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode $valueNode */
					$valueNode = static::getValueNode($tokens[$i]['content'], $content);
					if (!($valueNode instanceof InvalidTagValueNode)) {
						$multilineFixed = true;

						break;
					}
				}

				if (!$multilineFixed || $valueNode instanceof InvalidTagValueNode) {
					continue;
				}
			}

			$returnTypes = $this->valueNodeParts($valueNode);
			$typeString = $this->renderUnionTypes($returnTypes);

			$tag = $tokens[$i]['content'];
			$variablePos = strpos($content, ' $');
			if (in_array($tag, [VariableAnnotation::TAG, PropertyAnnotation::TAG]) && $variablePos) {
				$content = mb_substr($content, $variablePos + 1);
			} else {
				$content = mb_substr($content, mb_strlen($typeString) + 1);
			}

			$annotation = AnnotationFactory::createOrFail($tag, $typeString, $content, $classNameIndex);
			if ($this->getConfig(static::CONFIG_REMOVE) && $tag === VariableAnnotation::TAG && $this->varInUse($tokens, $closeTagIndex, $content)) {
				$annotation->setInUse();
			}
			if ($this->getConfig(static::CONFIG_REMOVE) && $tag === PropertyAnnotation::TAG && $this->propertyInUse($tokens, $closeTagIndex, $content)) {
				$annotation->setInUse();
			}
			if ($this->getConfig(static::CONFIG_REMOVE) && $tag === PropertyReadAnnotation::TAG && $this->propertyInUse($tokens, $closeTagIndex, $content)) {
				$annotation->setInUse();
			}
			if ($this->getConfig(static::CONFIG_REMOVE) && $tag === MethodAnnotation::TAG && $this->methodInUse($tokens, $closeTagIndex, $content)) {
				$annotation->setInUse();
			}

			$annotations[] = $annotation;
		}

		return $annotations;
	}

	/**
	 * Checks the var token for existence.
	 *
	 * T_VARIABLE ..., content=`$variable`
	 *
	 * @param array<array<string, mixed>> $tokens
	 * @param int $closeTagIndex
	 * @param string $variable
	 *
	 * @return bool
	 */
	protected function varInUse(array $tokens, int $closeTagIndex, string $variable): bool {
		if ($variable === '$this') {
			return false;
		}

		$i = $closeTagIndex + 1;
		while (isset($tokens[$i])) {
			if ($tokens[$i]['code'] === T_VARIABLE && $tokens[$i]['content'] === $variable) {
				return true;
			}
			$i++;
		}

		return false;
	}

	/**
	 * Checks the property token chain for existence.
	 *
	 * T_VARIABLE ..., content=`$this`
	 * T_OBJECT_OPERATOR ..., content=`->`
	 * T_STRING ..., content=`PropertyName`
	 * T_OBJECT_OPERATOR ..., content=`->`
	 *
	 * @param array<array<string, mixed>> $tokens
	 * @param int $closeTagIndex
	 * @param string $variable
	 *
	 * @return bool
	 */
	protected function propertyInUse(array $tokens, int $closeTagIndex, string $variable): bool {
		$property = substr($variable, 1);

		$i = $closeTagIndex + 1;
		while (isset($tokens[$i])) {
			if ($tokens[$i]['code'] !== T_VARIABLE || $tokens[$i]['content'] !== '$this') {
				$i++;

				continue;
			}
			$i++;
			if (!isset($tokens[$i])) {
				break;
			}
			if ($tokens[$i]['code'] !== T_OBJECT_OPERATOR) {
				$i++;

				continue;
			}
			$i++;
			if (!isset($tokens[$i])) {
				break;
			}
			if ($tokens[$i]['code'] !== T_STRING || $tokens[$i]['content'] !== $property) {
				$i++;

				continue;
			}
			$i++;
			if (!isset($tokens[$i])) {
				break;
			}
			if ($tokens[$i]['code'] !== T_OBJECT_OPERATOR) {
				$i++;

				continue;
			}

			return true;
		}

		return false;
	}

	/**
	 * Checks the property token chain for existence.
	 *
	 * T_VARIABLE ..., content=`$this`
	 * T_OBJECT_OPERATOR ..., content=`->`
	 * T_STRING ..., content=`method`
	 * T_OPEN_PARENTHESIS ..., content=`(`
	 *
	 * @param array<array<string, mixed>> $tokens
	 * @param int $closeTagIndex
	 * @param string $method
	 *
	 * @return bool
	 */
	protected function methodInUse(array $tokens, int $closeTagIndex, string $method): bool {
		if (!preg_match('#^(\w+)\(#', $method, $matches)) {
			return false;
		}
		$method = $matches[1];

		$i = $closeTagIndex + 1;
		while (isset($tokens[$i])) {
			if ($tokens[$i]['code'] !== T_VARIABLE || $tokens[$i]['content'] !== '$this') {
				$i++;

				continue;
			}
			$i++;
			if (!isset($tokens[$i])) {
				break;
			}
			if ($tokens[$i]['code'] !== T_OBJECT_OPERATOR) {
				$i++;

				continue;
			}
			$i++;
			if (!isset($tokens[$i])) {
				break;
			}
			if ($tokens[$i]['code'] !== T_STRING || $tokens[$i]['content'] !== $method) {
				$i++;

				continue;
			}
			$i++;
			if (!isset($tokens[$i])) {
				break;
			}
			if ($tokens[$i]['code'] !== T_OPEN_PARENTHESIS) {
				$i++;

				continue;
			}

			return true;
		}

		return false;
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param int $lastTagIndexOfPreviousLine
	 *
	 * @return bool
	 */
	protected function needsNewLineInDocBlock(File $file, int $lastTagIndexOfPreviousLine): bool {
		$tokens = $file->getTokens();

		$line = $tokens[$lastTagIndexOfPreviousLine]['line'];
		$index = $lastTagIndexOfPreviousLine - 1;

		while ($tokens[$index]['line'] === $line) {
			if ($tokens[$index]['code'] === T_DOC_COMMENT_TAG || $tokens[$index]['code'] === T_DOC_COMMENT_OPEN_TAG) {
				return false;
			}
			$index--;
		}

		return true;
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param int $index
	 * @param array<\IdeHelper\Annotation\AbstractAnnotation>|array<string> $annotations
	 *
	 * @return string
	 */
	protected function addNewDocBlock(File $file, int $index, array $annotations) {
		$tokens = $file->getTokens();

		foreach ($annotations as $key => $annotation) {
			if (is_string($annotation)) {
				continue;
			}
			$annotations[$key] = (string)$annotation;
		}

		$helper = new DocBlockHelper(new View());
		$annotationString = $helper->classDescription('', '', $annotations);
		if (PHP_EOL !== "\n") {
			$annotationString = str_replace("\n", PHP_EOL, $annotationString);
		}

		$fixer = $this->getFixer($file);

		$docBlock = $annotationString . PHP_EOL;
		$fixer->replaceToken($index, $docBlock . $tokens[$index]['content']);

		$contents = $fixer->getContents();

		$this->_counter[static::COUNT_ADDED] = count($annotations);

		return $contents;
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param int $docBlockCloseIndex
	 *
	 * @return bool
	 */
	protected function isInlineDocBlock(File $file, int $docBlockCloseIndex): bool {
		$tokens = $file->getTokens();

		$docBlockOpenIndex = $tokens[$docBlockCloseIndex]['comment_opener'];

		return $tokens[$docBlockCloseIndex]['line'] === $tokens[$docBlockOpenIndex]['line'];
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param int $docBlockCloseIndex
	 * @return bool
	 */
	protected function shouldSkip(File $file, int $docBlockCloseIndex): bool {
		$tokens = $file->getTokens();
		$docBlockOpenIndex = $tokens[$docBlockCloseIndex]['comment_opener'];

		for ($i = $docBlockOpenIndex + 1; $i < $docBlockCloseIndex; $i++) {
			if ($tokens[$i]['code'] !== T_DOC_COMMENT_TAG) {
				continue;
			}
			if (mb_strtolower($tokens[$i]['content']) === '@inheritdoc') {
				return true;
			}
		}

		return false;
	}

	/**
	 * @param array<string> $usedModels
	 * @param string $content
	 * @return array<\IdeHelper\Annotation\AbstractAnnotation>
	 */
	protected function getModelAnnotations(array $usedModels, string $content): array {
		$annotations = [];

		foreach ($usedModels as $usedModel) {
			$className = App::className($usedModel, 'Model/Table', 'Table');
			if (!$className) {
				$className = 'Cake\ORM\Table';
			}
			[, $name] = pluginSplit($usedModel);

			$annotations[] = AnnotationFactory::createOrFail(PropertyAnnotation::TAG, '\\' . $className, '$' . $name);
		}

		return $annotations;
	}

	/**
	 * Gets protected/private property of a class.
	 *
	 * So
	 *   $this->invokeProperty($object, '_foo');
	 * is equal to
	 *   $object->_foo
	 * (assuming the property was directly publicly accessible)
	 *
	 * @param object $object Instantiated object that we want the property off.
	 * @param string $name Property name to fetch.
	 *
	 * @return mixed Property value.
	 */
	protected function invokeProperty(&$object, string $name) {
		$reflection = new ReflectionClass(get_class($object));
		if (!$reflection->hasProperty($name)) {
			return null;
		}
		$property = $reflection->getProperty($name);

		return $property->getValue($object);
	}

	/**
	 * @return void
	 */
	protected function report(): void {
		$out = [];

		$added = !empty($this->_counter[static::COUNT_ADDED]) ? $this->_counter[static::COUNT_ADDED] : 0;
		if ($added) {
			$out[] = $added . ' ' . ($added === 1 ? 'annotation' : 'annotations') . ' added';
		}
		$updated = !empty($this->_counter[static::COUNT_UPDATED]) ? $this->_counter[static::COUNT_UPDATED] : 0;
		if ($updated) {
			$out[] = $updated . ' ' . ($updated === 1 ? 'annotation' : 'annotations') . ' updated';
		}
		$removed = !empty($this->_counter[static::COUNT_REMOVED]) ? $this->_counter[static::COUNT_REMOVED] : 0;
		if ($removed) {
			$out[] = $removed . ' ' . ($removed === 1 ? 'annotation' : 'annotations') . ' removed';
		}
		$skipped = !empty($this->_counter[static::COUNT_SKIPPED]) ? $this->_counter[static::COUNT_SKIPPED] : 0;
		if ($skipped) {
			$out[] = $skipped . ' ' . ($skipped === 1 ? 'annotation' : 'annotations') . ' skipped';
		}

		if (!$out) {
			return;
		}

		$this->_io->success('   -> ' . implode(', ', $out) . '.');
	}

	/**
	 * @param string $path
	 * @return void
	 */
	protected function reportSkipped(string $path): void {
		$out = [];

		$skipped = !empty($this->_counter[static::COUNT_SKIPPED]) ? $this->_counter[static::COUNT_SKIPPED] : 0;
		if ($skipped) {
			$out[] = $skipped . ' ' . ($skipped === 1 ? 'annotation' : 'annotations') . ' skipped';
		}

		if (!$out) {
			return;
		}

		if (!$this->getConfig('verbose')) {
			$this->_io->out('-> ' . str_replace(ROOT . DS, '', $path));
		}

		$this->_io->out('   -> ' . implode(', ', $out));
	}

	/**
	 * @return void
	 */
	protected function resetCounter(): void {
		$this->_counter = [
			static::COUNT_ADDED => 0,
			static::COUNT_UPDATED => 0,
			static::COUNT_REMOVED => 0,
			static::COUNT_SKIPPED => 0,
		];
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param int $classOrTraitIndex
	 *
	 * @return int
	 */
	protected function beginningOfLine(File $file, $classOrTraitIndex) {
		$tokens = $file->getTokens();

		$line = $tokens[$classOrTraitIndex]['line'];
		$beginningOfLineIndex = $classOrTraitIndex;
		while ($tokens[$beginningOfLineIndex - 1]['line'] === $line) {
			$beginningOfLineIndex--;
		}

		return $beginningOfLineIndex;
	}

	/**
	 * @param class-string<object> $className
	 *
	 * @return bool
	 */
	protected function _isAbstract($className) {
		$reflection = new ReflectionClass($className);

		return $reflection->isAbstract();
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param int $index
	 *
	 * @return int
	 */
	protected function skipOverAttributes(File $file, int $index): int {
		$prevCode = $file->findPrevious(Tokens::$emptyTokens, $index - 1, null, true);
		if (!$prevCode) {
			return $index;
		}

		$tokens = $file->getTokens();
		while ($prevCode && $tokens[$prevCode]['code'] === T_ATTRIBUTE_END) {
			$beginningOfLine = $this->beginningOfLine($file, $prevCode);
			$prevCode = $file->findPrevious(Tokens::$emptyTokens, $beginningOfLine - 1, null, true);
		}

		return $beginningOfLine ?? $index;
	}

}


================================================
FILE: src/Annotator/CallbackAnnotator.php
================================================
<?php

namespace IdeHelper\Annotator;

use RuntimeException;

class CallbackAnnotator extends AbstractAnnotator {

	/**
	 * @param string $path Path to file.
	 * @return bool
	 */
	public function annotate(string $path): bool {
		$content = file_get_contents($path);
		if ($content === false) {
			throw new RuntimeException('Cannot read file');
		}

		$this->invokeTasks($path, $content);

		return true;
	}

	/**
	 * @param string $path
	 * @param string $content
	 *
	 * @return void
	 */
	protected function invokeTasks(string $path, string $content): void {
		$tasks = $this->getTasks($path, $content);

		foreach ($tasks as $task) {
			if (!$task->shouldRun($path)) {
				continue;
			}

			$task->annotate($path);
		}
	}

	/**
	 * @param string $path
	 * @param string $content
	 * @return array<\IdeHelper\Annotator\CallbackAnnotatorTask\CallbackAnnotatorTaskInterface>
	 */
	protected function getTasks(string $path, string $content): array {
		$taskCollection = new CallbackAnnotatorTaskCollection();

		return $taskCollection->tasks($this->_io, $this->_config, $path, $content);
	}

}


================================================
FILE: src/Annotator/CallbackAnnotatorTask/AbstractCallbackAnnotatorTask.php
================================================
<?php

namespace IdeHelper\Annotator\CallbackAnnotatorTask;

use IdeHelper\Annotator\AbstractAnnotator;
use IdeHelper\Console\Io;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;

abstract class AbstractCallbackAnnotatorTask extends AbstractAnnotator {

	protected string $path;

	protected string $content;

	/**
	 * @param \IdeHelper\Console\Io $io
	 * @param array<string, mixed> $config
	 * @param string $path
	 * @param string $content
	 */
	public function __construct(Io $io, array $config, $path, $content) {
		parent::__construct($io, $config);

		$this->path = $path;
		$this->content = $content;
	}

	/**
	 * For testing only
	 *
	 * @return string
	 */
	public function getContent(): string {
		return $this->content;
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 *
	 * @return array<array<string, mixed>>
	 */
	protected function getMethods(File $file) {
		$methods = [];
		$currentIndex = 0;
		while (true) {
			$methodIndex = $file->findNext(T_FUNCTION, $currentIndex);
			if (!$methodIndex) {
				break;
			}
			$methods[$methodIndex] = $this->parseMethod($file, $methodIndex);

			$currentIndex = $methodIndex + 1;
		}

		return $methods;
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param int $index
	 * @return array<string, mixed>
	 */
	protected function parseMethod(File $file, int $index) {
		$tokens = $file->getTokens();
		$nameIndex = $file->findNext(Tokens::$emptyTokens, $index + 1, null, true);

		$closeTagIndex = $this->findCloseTagIndex($file, $index);

		$result = [
			'name' => $tokens[$nameIndex]['content'],
			'docBlockStart' => $closeTagIndex ? $tokens[$closeTagIndex]['comment_opener'] : null,
			'docBlockEnd' => $closeTagIndex ?: null,
		];

		return $result;
	}

	/**
	 * @param string $path
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param array<array<string, mixed>> $methods
	 * @return bool
	 */
	protected function annotateMethods(string $path, File $file, array $methods): bool {
		$this->resetCounter();

		$fixer = $this->getFixer($file);

		$fixer->beginChangeset();

		foreach ($methods as $method) {
			/** @var array<\IdeHelper\Annotation\ParamAnnotation> $replacingAnnotations */
			$replacingAnnotations = $method['annotations'];
			foreach ($replacingAnnotations as $annotation) {
				$fixer->replaceToken($annotation->getIndex(), $annotation->build());
				$this->_counter[static::COUNT_UPDATED]++;
			}
		}

		$fixer->endChangeset();

		$newContent = $fixer->getContents();

		if ($newContent === $this->content) {
			$this->reportSkipped($path);

			return false;
		}

		$this->displayDiff($this->content, $newContent);
		$this->storeFile($path, $newContent);
		$this->content = $newContent;

		$this->report();

		return true;
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param int $index
	 * @return int|null
	 */
	protected function findCloseTagIndex(File $file, int $index): ?int {
		$tokens = $file->getTokens();

		$beginningOfLineIndex = $index;
		while ($tokens[$beginningOfLineIndex - 1]['line'] === $tokens[$index]['line']) {
			$beginningOfLineIndex--;
		}

		$prevCodeIndex = $file->findPrevious(Tokens::$emptyTokens, $beginningOfLineIndex - 1, null, true);
		$closeTagIndex = $file->findPrevious(T_DOC_COMMENT_CLOSE_TAG, $index - 1, $prevCodeIndex ?: null);

		return $closeTagIndex ?: null;
	}

}


================================================
FILE: src/Annotator/CallbackAnnotatorTask/CallbackAnnotatorTaskInterface.php
================================================
<?php

namespace IdeHelper\Annotator\CallbackAnnotatorTask;

interface CallbackAnnotatorTaskInterface {

	/**
	 * @param string $path Path to file.
	 * @return bool
	 */
	public function shouldRun(string $path): bool;

	/**
	 * @param string $path Path to file.
	 * @return bool
	 */
	public function annotate(string $path): bool;

}


================================================
FILE: src/Annotator/CallbackAnnotatorTask/TableCallbackAnnotatorTask.php
================================================
<?php

namespace IdeHelper\Annotator\CallbackAnnotatorTask;

use Cake\ORM\TableRegistry;
use IdeHelper\Annotation\ParamAnnotation;
use PHP_CodeSniffer\Files\File;
use Throwable;

/**
 * Fix up generic *(EventInterface $event, EntityInterface $entity, ...) hook methods to have at least the $entity documented as concrete class.
 */
class TableCallbackAnnotatorTask extends AbstractCallbackAnnotatorTask implements CallbackAnnotatorTaskInterface {

	/**
	 * @var array<string, string>
	 */
	protected array $callbacks = [
		'afterMarshal' => 'afterMarshal',
		'beforeRules' => 'beforeRules',
		'afterRules' => 'afterRules',
		'beforeSave' => 'beforeSave',
		'afterSave' => 'afterSave',
		'afterSaveCommit' => 'afterSaveCommit',
		'beforeDelete' => 'beforeDelete',
		'afterDelete' => 'afterDelete',
		'afterDeleteCommit' => 'afterDeleteCommit',
	];

	/**
	 * @var string|null
	 */
	protected $entityClassName;

	/**
	 * @param string $path
	 * @return bool
	 */
	public function shouldRun(string $path): bool {
		$className = pathinfo($path, PATHINFO_FILENAME);
		if ($className === 'Table' || !str_ends_with($className, 'Table')) {
			return false;
		}

		$modelName = substr($className, 0, -5);
		$plugin = $this->getConfig(static::CONFIG_PLUGIN);

		try {
			$table = TableRegistry::getTableLocator()->get($plugin ? ($plugin . '.' . $modelName) : $modelName);
		} catch (Throwable $e) {
			if ($this->getConfig(static::CONFIG_VERBOSE)) {
				$this->_io->warn('   Skipping table: ' . $e->getMessage());
			}

			return false;
		}

		$entityClassName = $table->getEntityClass();
		$this->entityClassName = $entityClassName;

		if (!preg_match('#\bfunction (' . $this->generatePattern() . ')\b#', $this->content)) {
			return false;
		}

		return true;
	}

	/**
	 * @param string $path
	 * @return bool
	 */
	public function annotate(string $path): bool {
		$file = $this->getFile($path, $this->content);

		$methods = $this->getMethods($file);

		foreach ($methods as $index => $method) {
			if (!in_array($method['name'], $this->callbacks, true)) {
				unset($methods[$index]);

				continue;
			}

			if (!$this->needsUpdate($file, $index, $method)) {
				unset($methods[$index]);

				continue;
			}

			$methods[$index] = $method;
		}

		return $this->annotateMethods($path, $file, $methods);
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param int $index
	 * @param array<string, mixed> $method
	 * @return bool
	 */
	protected function needsUpdate(File $file, int $index, array &$method): bool {
		if (empty($method['docBlockStart']) || empty($method['docBlockEnd'])) {
			return false;
		}

		$annotations = $this->parseExistingAnnotations($file, $method['docBlockEnd'], ['@param']);

		if (empty($annotations[1])) {
			return false;
		}

		/** @var \IdeHelper\Annotation\ParamAnnotation $currentAnnotation */
		$currentAnnotation = $annotations[1];
		$expectedAnnotation = new ParamAnnotation('\\' . $this->entityClassName, $currentAnnotation->getVariable());

		if ($currentAnnotation->getType() === $expectedAnnotation->getType()) {
			return false;
		}

		$currentAnnotation->replaceWith($expectedAnnotation);

		$method['annotations'] = [
			$currentAnnotation,
		];

		return true;
	}

	/**
	 * @return string
	 */
	protected function generatePattern(): string {
		$pattern = [];
		foreach ($this->callbacks as $key => $v) {
			$pattern[] = preg_quote($key . '(');
		}

		return implode('|', $pattern);
	}

}


================================================
FILE: src/Annotator/CallbackAnnotatorTask/VirtualFieldCallbackAnnotatorTask.php
================================================
<?php

namespace IdeHelper\Annotator\CallbackAnnotatorTask;

use Cake\Core\Exception\CakeException;
use Cake\Utility\Inflector;
use IdeHelper\Annotation\SeeAnnotation;
use PHP_CodeSniffer\Files\File;

/**
 * Fix up entity virtual field methods to have at least the $property linked.
 */
class VirtualFieldCallbackAnnotatorTask extends AbstractCallbackAnnotatorTask implements CallbackAnnotatorTaskInterface {

	/**
	 * @var array<string, string>
	 */
	protected array $methods = [
		'_get' => '#_get[A-Z]\w+#',
		'_set' => '#_set[A-Z]\w+#',
	];

	/**
	 * @param string $path
	 * @return bool
	 */
	public function shouldRun(string $path): bool {
		if (!preg_match('#/Model/Entity/\w+\.php$#', $path)) {
			return false;
		}

		return true;
	}

	/**
	 * @param string $path
	 * @return bool
	 */
	public function annotate(string $path): bool {
		$file = $this->getFile($path, $this->content);

		$methods = $this->getMethods($file);
		$namespace = $this->getNamespace($file);
		$class = $this->getClass($file);

		foreach ($methods as $index => $method) {
			if (!$this->isVirtualField($method)) {
				unset($methods[$index]);

				continue;
			}

			$method['see'] = $namespace . '\\' . $class . '::$' . Inflector::underscore(substr($method['name'], 4));

			if (!$this->needsUpdate($file, $index, $method)) {
				unset($methods[$index]);

				continue;
			}

			$methods[$index] = $method;
		}

		if (!$methods) {
			return false;
		}

		return $this->annotateMethods($path, $file, $methods);
	}

	/**
	 * @param string $path
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param array<array<string, mixed>> $methods
	 * @return bool
	 */
	protected function annotateMethods(string $path, File $file, array $methods): bool {
		$this->resetCounter();

		$fixer = $this->getFixer($file);

		$fixer->beginChangeset();

		foreach ($methods as $method) {
			/** @var array<\IdeHelper\Annotation\ParamAnnotation> $replacingAnnotations */
			$replacingAnnotations = $method['annotations'] ?? [];
			foreach ($replacingAnnotations as $annotation) {
				$fixer->replaceToken($annotation->getIndex(), $annotation->build());
				$this->_counter[static::COUNT_UPDATED]++;
			}

			/** @var \IdeHelper\Annotation\LinkAnnotation|null $addingAnnotation */
			$addingAnnotation = $method['annotation'] ?? null;
			if ($addingAnnotation) {
				$endIndex = $method['docBlockEnd'];

				$indentation = $this->indentation($file, $endIndex);
				$fixer->addContentBefore($endIndex, '* @see ' . $addingAnnotation->build() . PHP_EOL . $indentation);
			}
		}

		$fixer->endChangeset();

		$newContent = $fixer->getContents();

		if ($newContent === $this->content) {
			$this->reportSkipped($path);

			return false;
		}

		$this->displayDiff($this->content, $newContent);
		$this->storeFile($path, $newContent);
		$this->content = $newContent;

		$this->report();

		return true;
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param int $index
	 * @param array<string, mixed> $method
	 * @return bool
	 */
	protected function needsUpdate(File $file, int $index, array &$method): bool {
		if (empty($method['docBlockStart']) || empty($method['docBlockEnd'])) {
			return false;
		}

		$annotations = $this->parseExistingAnnotations($file, $method['docBlockEnd'], ['@see']);

		$expectedAnnotation = new SeeAnnotation($method['see']);
		if (!$annotations || $annotations[0]->getType() !== $method['see']) {
			$method['annotation'] = $expectedAnnotation;

			return true;
		}

		/** @var \IdeHelper\Annotation\LinkAnnotation $currentAnnotation */
		$currentAnnotation = $annotations[0];
		if ($currentAnnotation->getType() === $expectedAnnotation->getType()) {
			return false;
		}

		$currentAnnotation->replaceWith($expectedAnnotation);
		$currentAnnotation->setInUse();

		$method['annotations'] = [
			$currentAnnotation,
		];

		return true;
	}

	/**
	 * @param array<string, mixed> $method
	 *
	 * @return bool
	 */
	protected function isVirtualField(array $method): bool {
		foreach ($this->methods as $regex) {
			if (preg_match($regex, $method['name'])) {
				return true;
			}
		}

		return false;
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 *
	 * @return string
	 */
	protected function getNamespace(File $file): string {
		$namespaceIndex = $file->findNext(T_NAMESPACE, 0);
		$startIndex = $file->findNext(T_WHITESPACE, $namespaceIndex + 1, null, true);
		$endIndex = $file->findNext(T_SEMICOLON, $namespaceIndex + 1);

		if (!$namespaceIndex || !$startIndex || !$endIndex) {
			throw new CakeException('File does not seem to be a valid entity class');
		}

		$tokens = $file->getTokens();
		$elements = ['\\'];
		for ($i = $startIndex; $i < $endIndex; $i++) {
			$elements[] = $tokens[$i]['content'];
		}

		return implode('', $elements);
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 *
	 * @return string
	 */
	protected function getClass(File $file): string {
		$classIndex = $file->findNext(T_CLASS, 0);
		$classNameIndex = $file->findNext(T_WHITESPACE, $classIndex + 1, null, true);

		$tokens = $file->getTokens();
		if (!$classNameIndex || empty($tokens[$classNameIndex])) {
			throw new CakeException('File does not seem to be a valid entity class');
		}

		return $tokens[$classNameIndex]['content'];
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param int $endIndex
	 *
	 * @return string
	 */
	protected function indentation(File $file, int $endIndex): string {
		$tokens = $file->getTokens();
		$indentationElements = [];
		for ($i = $endIndex - 1; $i > 0; $i--) {
			if ($tokens[$i]['line'] !== $tokens[$endIndex]['line']) {
				break;
			}

			$indentationElements[] = $tokens[$i]['content'];
		}

		return implode('', $indentationElements);
	}

}


================================================
FILE: src/Annotator/CallbackAnnotatorTaskCollection.php
================================================
<?php

namespace IdeHelper\Annotator;

use Cake\Core\Configure;
use IdeHelper\Annotator\CallbackAnnotatorTask\TableCallbackAnnotatorTask;
use IdeHelper\Annotator\CallbackAnnotatorTask\VirtualFieldCallbackAnnotatorTask;
use IdeHelper\Console\Io;

class CallbackAnnotatorTaskCollection {

	/**
	 * @var array<class-string<\IdeHelper\Annotator\CallbackAnnotatorTask\CallbackAnnotatorTaskInterface>, class-string<\IdeHelper\Annotator\CallbackAnnotatorTask\CallbackAnnotatorTaskInterface>>
	 */
	protected array $defaultTasks = [
		TableCallbackAnnotatorTask::class => TableCallbackAnnotatorTask::class,
		VirtualFieldCallbackAnnotatorTask::class => VirtualFieldCallbackAnnotatorTask::class,
	];

	/**
	 * @var array<class-string<\IdeHelper\Annotator\CallbackAnnotatorTask\CallbackAnnotatorTaskInterface>>
	 */
	protected array $tasks;

	/**
	 * @param array<class-string<\IdeHelper\Annotator\CallbackAnnotatorTask\CallbackAnnotatorTaskInterface>> $tasks
	 */
	public function __construct(array $tasks = []) {
		$defaultTasks = $this->defaultTasks();
		$tasks += $defaultTasks;

		foreach ($tasks as $task) {
			if (!$task) {
				continue;
			}

			$this->tasks = $tasks;
		}
	}

	/**
	 * @return array<class-string<\IdeHelper\Annotator\CallbackAnnotatorTask\CallbackAnnotatorTaskInterface>>
	 */
	public function defaultTasks(): array {
		$tasks = (array)Configure::read('IdeHelper.callbackAnnotatorTasks') + $this->defaultTasks;

		foreach ($tasks as $k => $v) {
			if (is_numeric($k)) {
				$tasks[$v] = $v;
				unset($tasks[$k]);
			}
		}

		return $tasks;
	}

	/**
	 * @param \IdeHelper\Console\Io $io
	 * @param array<string, mixed> $config
	 * @param string $path
	 * @param string $content
	 * @return array<\IdeHelper\Annotator\CallbackAnnotatorTask\CallbackAnnotatorTaskInterface>
	 */
	public function tasks(Io $io, array $config, string $path, string $content): array {
		$tasks = $this->tasks;

		$collection = [];
		foreach ($tasks as $task) {
			/** @var \IdeHelper\Annotator\CallbackAnnotatorTask\CallbackAnnotatorTaskInterface $object */
			$object = new $task($io, $config, $path, $content);
			$collection[] = $object;
		}

		return $collection;
	}

}


================================================
FILE: src/Annotator/ClassAnnotator.php
================================================
<?php

namespace IdeHelper\Annotator;

use RuntimeException;

class ClassAnnotator extends AbstractAnnotator {

	/**
	 * @param string $path Path to file.
	 * @return bool
	 */
	public function annotate(string $path): bool {
		$content = file_get_contents($path);
		if ($content === false) {
			throw new RuntimeException('Cannot read file');
		}

		$this->invokeTasks($path, $content);

		return true;
	}

	/**
	 * @param string $path
	 * @param string $content
	 *
	 * @return void
	 */
	protected function invokeTasks(string $path, string $content): void {
		$tasks = $this->getTasks($content);

		foreach ($tasks as $task) {
			if (!$task->shouldRun($path, $content)) {
				continue;
			}

			$task->annotate($path);
		}
	}

	/**
	 * @param string $content
	 * @return array<\IdeHelper\Annotator\ClassAnnotatorTask\ClassAnnotatorTaskInterface>
	 */
	protected function getTasks(string $content): array {
		$taskCollection = new ClassAnnotatorTaskCollection();

		return $taskCollection->tasks($this->_io, $this->_config, $content);
	}

}


================================================
FILE: src/Annotator/ClassAnnotatorTask/AbstractClassAnnotatorTask.php
================================================
<?php

namespace IdeHelper\Annotator\ClassAnnotatorTask;

use IdeHelper\Annotator\AbstractAnnotator;
use IdeHelper\Console\Io;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
use RuntimeException;

abstract class AbstractClassAnnotatorTask extends AbstractAnnotator {

	protected string $content;

	/**
	 * @param \IdeHelper\Console\Io $io
	 * @param array<string, mixed> $config
	 * @param string $content
	 */
	public function __construct(Io $io, array $config, $content) {
		parent::__construct($io, $config);

		$this->content = $content;
	}

	/**
	 * For testing only
	 *
	 * @return string
	 */
	public function getContent(): string {
		return $this->content;
	}

	/**
	 * @param string $path
	 * @param string $content
	 * @param array<\IdeHelper\Annotation\AbstractAnnotation> $annotations
	 *
	 * @return bool
	 */
	protected function annotateContent(string $path, string $content, array $annotations): bool {
		if (!count($annotations)) {
			return false;
		}

		$file = $this->getFile($path, $content);

		$classOrTraitIndex = $file->findNext([T_CLASS, T_TRAIT], 0);
		if (!$classOrTraitIndex) {
			return false;
		}
		$beginningOfLineIndex = $this->beginningOfLine($file, $classOrTraitIndex);

		$attributeTokens = [
			T_ATTRIBUTE => T_ATTRIBUTE,
			T_ATTRIBUTE_END => T_ATTRIBUTE_END,
			T_NS_SEPARATOR => T_NS_SEPARATOR,
			T_STRING => T_STRING,
		];
		$prevCode = $file->findPrevious(Tokens::$emptyTokens + $attributeTokens, $beginningOfLineIndex - 1, null, true);
		if ($prevCode === false) {
			return false;
		}

		$closeTagIndex = $file->findPrevious(T_DOC_COMMENT_CLOSE_TAG, $beginningOfLineIndex - 1, $prevCode);
		$this->resetCounter();
		if ($closeTagIndex && $this->shouldSkip($file, $closeTagIndex)) {
			return false;
		}
		if ($closeTagIndex && !$this->isInlineDocBlock($file, $closeTagIndex)) {
			$newContent = $this->appendToExistingDocBlock($file, $closeTagIndex, $annotations);
		} else {
			$newContent = $this->addNewDocBlock($file, $beginningOfLineIndex, $annotations);
		}

		if ($newContent === $content) {
			$this->reportSkipped($path);

			return false;
		}

		$this->displayDiff($content, $newContent);
		$this->storeFile($path, $newContent);
		$this->content = $newContent;

		$this->report();

		return true;
	}

	/**
	 * @param string $path
	 * @param string $content
	 * @param array<\IdeHelper\Annotation\AbstractAnnotation> $annotations
	 * @param int $line
	 *
	 * @return bool
	 */
	protected function annotateInlineContent(string $path, string $content, array $annotations, int $line): bool {
		if (!count($annotations)) {
			return false;
		}

		$file = $this->getFile($path, $content);

		$beginningOfLineIndex = $this->findFirstTokenOfLine($file, $line);
		if (!$beginningOfLineIndex) {
			return false;
		}

		$prevCode = $file->findPrevious(Tokens::$emptyTokens, $beginningOfLineIndex - 1, null, true);
		if ($prevCode === false) {
			return false;
		}

		$closeTagIndex = $file->findPrevious(T_DOC_COMMENT_CLOSE_TAG, $beginningOfLineIndex - 1, $prevCode);
		$this->resetCounter();
		if ($closeTagIndex && $this->shouldSkip($file, $closeTagIndex)) {
			return false;
		}
		if ($closeTagIndex) {
			// Skip as there seems to be already one
			$newContent = $content;
		} else {
			$newContent = $this->addNewInlineDocBlock($file, $beginningOfLineIndex, $annotations);
		}

		if ($newContent === $content) {
			$this->reportSkipped($path);

			return false;
		}

		$this->displayDiff($content, $newContent);
		$this->storeFile($path, $newContent);
		$this->content = $newContent;

		$this->report();

		return true;
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param int $index
	 * @param array<\IdeHelper\Annotation\AbstractAnnotation>|array<string> $annotations
	 *
	 * @return string
	 */
	protected function addNewInlineDocBlock(File $file, int $index, array $annotations) {
		$tokens = $file->getTokens();

		if (count($annotations) !== 1) {
			throw new RuntimeException('Cannot work with annotation count != 1 right now');
		}

		$annotation = reset($annotations);
		$annotationString = '/** ' . (string)$annotation . ' */';
		if (PHP_EOL !== "\n") {
			$annotationString = str_replace("\n", PHP_EOL, $annotationString);
		}
		$indentation = $this->detectIndentation($file, $index);

		$fixer = $this->getFixer($file);

		$docBlock = $indentation . $annotationString . PHP_EOL;
		$fixer->replaceToken($index, $docBlock . $tokens[$index]['content']);

		$contents = $fixer->getContents();

		$this->_counter[static::COUNT_ADDED] = count($annotations);

		return $contents;
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param int $line
	 *
	 * @return int|null
	 */
	protected function findFirstTokenOfLine(File $file, int $line): ?int {
		$tokens = $file->getTokens();
		foreach ($tokens as $index => $token) {
			if ($token['line'] === $line) {
				return $index;
			}
		}

		return null;
	}

	/**
	 * @param \PHP_CodeSniffer\Files\File $file
	 * @param int $index
	 *
	 * @return string
	 */
	protected function detectIndentation(File $file, int $index): string {
		$nextIndex = $file->findNext(T_WHITESPACE, $index + 1, null, true);
		if (!$nextIndex) {
			return '';
		}

		$tokens = $file->getTokens();
		$whitespace = '';
		for ($i = $index; $i < $nextIndex; $i++) {
			$whitespace .= $tokens[$i]['content'];
		}

		return $whitespace;
	}

}


================================================
FILE: src/Annotator/ClassAnnotatorTask/ClassAnnotatorTaskInterface.php
================================================
<?php

namespace IdeHelper\Annotator\ClassAnnotatorTask;

interface ClassAnnotatorTaskInterface {

	/**
	 * Deprecated: $content, use $this->content instead.
	 *
	 * @param string $path
	 * @param string $content
	 * @return bool
	 */
	public function shouldRun(string $path, string $content): bool;

	/**
	 * @param string $path Path to file.
	 * @return bool
	 */
	public function annotate(string $path): bool;

}


================================================
FILE: src/Annotator/ClassAnnotatorTask/FormClassAnnotatorTask.php
================================================
<?php

namespace IdeHelper\Annotator\ClassAnnotatorTask;

use Cake\Core\Configure;
use IdeHelper\Annotation\AnnotationFactory;
use IdeHelper\Annotation\UsesAnnotation;

/**
 * Form classes should automatically have `@uses` annotated for method invocation.
 */
class FormClassAnnotatorTask extends AbstractClassAnnotatorTask implements ClassAnnotatorTaskInterface {

	/**
	 * Deprecated: $content, use $this->content instead.
	 *
	 * @param string $path
	 * @param string $content
	 * @return bool
	 */
	public function shouldRun(string $path, string $content): bool {
		if (!str_contains($path, DS . 'src' . DS)) {
			return false;
		}

		$appNamespace = Configure::read('App.namespace') ?: 'App';
		if (!preg_match('#\buse (\w+)\\\\Form\\\\(.+)Form\b#', $content, $matches)) {
			return false;
		}

		$varName = lcfirst($matches[2]) . 'Form';
		if (!preg_match('#\$' . preg_quote($varName, '#') . '->execute\(#', $content)) {
			return false;
		}

		return true;
	}

	/**
	 * @param string $path
	 * @return bool
	 */
	public function annotate(string $path): bool {
		preg_match('#\buse (\w+)\\\\Form\\\\(.+)Form\b#', $this->content, $matches);
		if (empty($matches[1]) || empty($matches[2])) {
			return false;
		}

		$appNamespace = $matches[1];
		$name = $matches[2] . 'Form';

		$varName = lcfirst($name);
		$rows = explode(PHP_EOL, $this->content);
		$rowToAnnotate = null;
		foreach ($rows as $i => $row) {
			if (!preg_match('#\$' . preg_quote($varName, '#') . '->execute\(#', $row)) {
				continue;
			}
			$rowToAnnotate = $i + 1;

			break;
		}

		if (!$rowToAnnotate) {
			return false;
		}

		$method = $appNamespace . '\\Form\\' . $name . '::_execute()';
		$annotations = $this->buildUsesAnnotations([$method]);

		return $this->annotateInlineContent($path, $this->content, $annotations, $rowToAnnotate);
	}

	/**
	 * @param array<string> $classes
	 * @return array<\IdeHelper\Annotation\AbstractAnnotation>
	 */
	protected function buildUsesAnnotations(array $classes): array {
		$annotations = [];

		foreach ($classes as $className) {
			$annotations[] = AnnotationFactory::createOrFail(UsesAnnotation::TAG, '\\' . $className);
		}

		return $annotations;
	}

}


================================================
FILE: src/Annotator/ClassAnnotatorTask/MailerClassAnnotatorTask.php
================================================
<?php

namespace IdeHelper\Annotator\ClassAnnotatorTask;

use Cake\Core\Configure;
use IdeHelper\Annotation\AnnotationFactory;
use IdeHelper\Annotation\UsesAnnotation;

/**
 * Mailer classes should automatically have `@uses` annotated for method invocation.
 */
class MailerClassAnnotatorTask extends AbstractClassAnnotatorTask implements ClassAnnotatorTaskInterface {

	/**
	 * Deprecated: $content, use $this->content instead.
	 *
	 * @param string $path
	 * @param string $content
	 * @return bool
	 */
	public function shouldRun(string $path, string $content): bool {
		if (!str_contains($path, DS . 'src' . DS)) {
			return false;
		}

		if (preg_match('#namespace \w+\\\\Mailer;#', $content)) {
			return false;
		}

		preg_match('#\buse (\w+)\\\\Mailer\\\\(\w+)Mailer\b#', $content, $useMatches);
		preg_match('#\$\w+\s*=\s*\$this-\>getMailer\(\'([\w\.]+)\'\)#', $content, $callMatches);
		$singleLine = false;
		if (!$callMatches) {
			$singleLine = true;
			preg_match('#\$this->getMailer\(\s*\'([\w\.]+)\'\s*\)\s*->\s*send\(\s*\'([\w\.]+)\'#msu', $content, $callMatches);
		}
		if (!$useMatches && !$callMatches) {
			return false;
		}

		if ($useMatches) {
			$varName = lcfirst($useMatches[2]) . 'Mailer';
		} else {
			$class = $callMatches[1];
			[$plugin, $name] = pluginSplit($class);
			$varName = lcfirst($name) . 'Mailer';
		}

		if ($singleLine && !empty($callMatches)) {
			return true;
		}

		if (!preg_match('#\$' . preg_quote($varName, '#') . '->send\(\'\w+\'#', $content)) {
			return false;
		}

		return true;
	}

	/**
	 * @param string $path
	 * @return bool
	 */
	public function annotate(string $path): bool {
		preg_match('#\buse (\w+)\\\\Mailer\\\\(\w+)Mailer\b#', $this->content, $useMatches);

		$singleCall = false;
		$singleCallAction = null;
		if (!$useMatches) {
			preg_match('#\$\w+\s*=\s*\$this->getMailer\(\'([\w.]+)\'\)#', $this->content, $callMatches);
			if (!$callMatches) {
				preg_match('#\$this->getMailer\(\s*\'([\w\.]+)\'\s*\)\s*->\s*send\(\s*\'([\w\.]+)\'#msu', $this->content, $callMatches);
				if (!$callMatches) {
					return false;
				}

				$singleCall = true;
				$singleCallAction = $callMatches[2];
			}
		}

		if ($useMatches) {
			$appNamespace = $useMatches[1];
			$name = $useMatches[2] . 'Mailer';
		} else {
			[$plugin, $name] = pluginSplit($callMatches[1]);
			$appNamespace = $plugin ?: (Configure::read('App.namespace') ?: 'App');
			$name = $name . 'Mailer';
		}

		$action = null;
		if (!$singleCall) {
			$varName = lcfirst($name);
			$rows = explode(PHP_EOL, $this->content);
			$rowToAnnotate = null;
			$rowMatches = null;
			foreach ($rows as $i => $row) {
				if (!preg_match('#\$' . preg_quote($varName, '#') . '->send\(\'(\w+)\'#', $row, $rowMatches)) {
					continue;
				}
				$rowToAnnotate = $i + 1;
				$action = $rowMatches[1];

				break;
			}
		} else {
			assert($singleCallAction !== null);
			$rows = explode(PHP_EOL, $this->content);
			$rowToAnnotate = null;
			$rowMatches = null;
			$multiLine = str_contains($callMatches[0], PHP_EOL);
			foreach ($rows as $i => $row) {
				if (
					$multiLine
					&& preg_match('#\$this->getMailer\(\s*\'' . $callMatches[1] . '\'\s*\)#msu', $row, $rowMatches)
					&& !empty($rows[$i + 1])
					&& preg_match('#->\s*send\(\s*\'' . $singleCallAction . '\'#msu', $rows[$i + 1], $rowMatches)
				) {
					$rowToAnnotate = $i;
					$action = $singleCallAction;

					break;
				}

				if (!preg_match('#\$this->getMailer\(\s*\'' . $callMatches[1] . '\'\s*\)\s*->\s*send\(\s*\'' . $singleCallAction . '\'#msu', $row, $rowMatches)) {
					continue;
				}
				$rowToAnnotate = $i + 1;
				$action = $singleCallAction;

				break;
			}
		}

		if (!$rowToAnnotate) {
			return false;
		}

		$method = $appNamespace . '\\Mailer\\' . $name . '::' . $action . '()';
		$annotations = $this->buildUsesAnnotations([$method]);

		return $this->annotateInlineContent($path, $this->content, $annotations, $rowToAnnotate);
	}

	/**
	 * @param array<string> $classes
	 * @return array<\IdeHelper\Annotation\AbstractAnnotation>
	 */
	protected function buildUsesAnnotations(array $classes): array {
		$annotations = [];

		foreach ($classes as $className) {
			$annotations[] = AnnotationFactory::createOrFail(UsesAnnotation::TAG, '\\' . $className);
		}

		return $annotations;
	}

}


================================================
FILE: src/Annotator/ClassAnnotatorTask/ModelAwareClassAnnotatorTask.php
================================================
<?php

namespace IdeHelper\Annotator\ClassAnnotatorTask;

use ReflectionClass;
use Throwable;

/**
 * Classes that use ModelAwareTrait should automatically have used tables - via fetchModel() call - annotated.
 */
class ModelAwareClassAnnotatorTask extends AbstractClassAnnotatorTask implements ClassAnnotatorTaskInterface {

	/**
	 * Deprecated: $content, use $this->content instead.
	 *
	 * @param string $path
	 * @param string $content
	 * @return bool
	 */
	public function shouldRun(string $path, string $content): bool {
		if (!str_contains($path, DS . 'src' . DS)) {
			return false;
		}
		if (preg_match('#\buse ModelAwareTrait\b#', $content)) {
			return true;
		}

		/** @var class-string|null $className */
		$className = $this->getClassName($path, $content);
		if (!$className) {
			return false;
		}

		try {
			return (new ReflectionClass($className))->hasMethod('fetchModel');
		} catch (Throwable $exception) {
			return false;
		}
	}

	/**
	 * @param string $path
	 * @return bool
	 */
	public function annotate(string $path): bool {
		$models = $this->getUsedModels($this->content);

		$annotations = $this->getModelAnnotations($models, $this->content);

		return $this->annotateContent($path, $this->content, $annotations);
	}

	/**
	 * @param string $content
	 *
	 * @return array<string>
	 */
	protected function getUsedModels(string $content): array {
		preg_match_all('/\$this-\>fetchModel\(\'([a-z.]+)\'/i', $content, $matches);
		if (empty($matches[1])) {
			return [];
		}

		$models = $matches[1];

		return array_unique($models);
	}

	/**
	 * @param string $path
	 * @param string $content
	 *
	 * @return string|null
	 */
	protected function getClassName(string $path, string $content): ?string {
		preg_match('#^namespace (.+)\b#m', $content, $matches);
		if (!$matches) {
			return null;
		}

		$className = pathinfo($path, PATHINFO_FILENAME);

		return $matches[1] . '\\' . $className;
	}

}


================================================
FILE: src/Annotator/ClassAnnotatorTask/PathAwareClassAnnotatorTaskInterface.php
================================================
<?php

namespace IdeHelper\Annotator\ClassAnnotatorTask;

/**
 * Optional interface that a class annotator task can implement to declare
 * extra directories that the `bin/cake annotate classes` command should walk
 * in addition to its default `src/` (app + plugin classpaths) and
 * `tests/TestCase/` scans.
 *
 * The intended use is third-party packages whose subjects live outside the
 * conventional source tree — e.g. a test-fixture factory plugin whose
 * subclasses live under `tests/Factory/`. By declaring the path on the
 * task itself, the package does not need to ship its own bake/annotate
 * subcommand: registering the task in `IdeHelper.classAnnotatorTasks` is
 * enough for the existing command to reach the relevant files.
 *
 * `scanPaths()` is `static` so the command can query a task's paths
 * without first instantiating it with an `Io` and per-file content. Paths
 * are project-root relative for app context, and plugin-root relative
 * when the command is run with `-p <plugin>` (or `-p all`). Paths are
 * walked recursively.
 *
 * 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 is stable
 * when two tasks declare the same path.
 */
interface PathAwareClassAnnotatorTaskInterface extends ClassAnnotatorTaskInterface {

	/**
	 * @return array<string>
	 */
	public static function scanPaths(): array;

}


================================================
FILE: src/Annotator/ClassAnnotatorTask/TableFindAnnotatorTask.php
================================================
<?php

namespace IdeHelper\Annotator\ClassAnnotatorTask;

use Cake\Core\Configure;
use IdeHelper\Annotation\AnnotationFactory;
use IdeHelper\Annotation\VariableAnnotation;
use PhpParser\NodeTraverser;
use PhpParser\ParserFactory;
use Throwable;

/**
 * Usage of first() or firstOrFail() on table finders should have inline @var annotations added.
 *
 * Detects patterns like:
 * - $entity = $this->TableName->find()->first();
 * - $entity = $this->TableName->find()->firstOrFail();
 */
class TableFindAnnotatorTask extends AbstractClassAnnotatorTask implements ClassAnnotatorTaskInterface {

	/**
	 * Deprecated: $content, use $this->content instead.
	 *
	 * @param string $path
	 * @param string $content
	 * @return bool
	 */
	public function shouldRun(string $path, string $content): bool {
		if (!str_contains($path, DS . 'src' . DS)) {
			return false;
		}

		if (!preg_match('#->(first|firstOrFail)\(\)#', $content)) {
			return false;
		}

		return true;
	}

	/**
	 * @param string $path
	 * @return bool
	 */
	public function annotate(string $path): bool {
		$findings = $this->findTableFinderCalls();
		if (!$findings) {
			return false;
		}

		// Process from bottom to top to avoid line number shifts
		usort($findings, fn ($a, $b) => $b['line'] <=> $a['line']);

		$annotated = false;
		foreach ($findings as $finding) {
			$annotation = $this->buildVarAnnotation($finding['entityClass'], $finding['varName'], $finding['nullable']);
			$result = $this->annotateInlineContent($path, $this->content, [$annotation], $finding['line']);
			if ($result) {
				$annotated = true;
			}
		}

		return $annotated;
	}

	/**
	 * Find all table finder calls that end with first() or firstOrFail().
	 *
	 * @return array<array{line: int, varName: string, tableName: string, entityClass: string, nullable: bool}>
	 */
	protected function findTableFinderCalls(): array {
		$parser = (new ParserFactory())->createForHostVersion();
		try {
			$ast = $parser->parse($this->content);
		} catch (Throwable $e) {
			return [];
		}

		if ($ast === null) {
			return [];
		}

		$appNamespace = Configure::read('App.namespace') ?: 'App';

		$visitor = new TableFindNodeVisitor($appNamespace);
		$traverser = new NodeTraverser();
		$traverser->addVisitor($visitor);
		$traverser->traverse($ast);

		return $visitor->getFindings();
	}

	/**
	 * Build a @var annotation for the entity type.
	 *
	 * @param string $entityClass
	 * @param string $varName
	 * @param bool $nullable
	 * @return \IdeHelper\Annotation\VariableAnnotation
	 */
	protected function buildVarAnnotation(string $entityClass, string $varName, bool $nullable): VariableAnnotation {
		$type = $entityClass . ($nullable ? '|null' : '');

		/** @var \IdeHelper\Annotation\VariableAnnotation */
		return AnnotationFactory::createOrFail(VariableAnnotation::TAG, $type, '$' . $varName);
	}

}


================================================
FILE: src/Annotator/ClassAnnotatorTask/TableFindNodeVisitor.php
================================================
<?php

namespace IdeHelper\Annotator\ClassAnnotatorTask;

use Cake\Utility\Inflector;
use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\NodeVisitorAbstract;

/**
 * Node visitor that finds table finder calls ending with first() or firstOrFail().
 */
class TableFindNodeVisitor extends NodeVisitorAbstract {

	private string $appNamespace;

	/**
	 * @var array<array{line: int, varName: string, tableName: string, entityClass: string, nullable: bool}>
	 */
	private array $findings = [];

	/**
	 * @param string $appNamespace
	 */
	public function __construct(string $appNamespace) {
		$this->appNamespace = $appNamespace;
	}

	/**
	 * @return array<array{line: int, varName: string, tableName: string, entityClass: string, nullable: bool}>
	 */
	public function getFindings(): array {
		return $this->findings;
	}

	/**
	 * @param \PhpParser\Node $node
	 * @return int|null
	 */
	public function enterNode(Node $node): ?int {
		if (!$node instanceof Assign || !$node->expr instanceof Node\Expr\MethodCall) {
			return null;
		}

		$methodCall = $node->expr;

		// Check for ->first() or ->firstOrFail()
		if (
			!$methodCall->name instanceof Node\Identifier ||
			!in_array($methodCall->name->toString(), ['first', 'firstOrFail'], true)
		) {
			return null;
		}

		$method = $methodCall->name->toString();
		$nullable = $method === 'first';

		// Get the variable name being assigned
		if (!$node->var instanceof Node\Expr\Variable || !is_string($node->var->name)) {
			return null;
		}
		$varName = $node->var->name;

		// Traverse back through the method chain to find $this->TableName
		$tableName = $this->extractTableName($methodCall->var);
		if ($tableName === null) {
			return null;
		}

		$entityName = Inflector::singularize($tableName);
		$entityClass = '\\' . $this->appNamespace . '\\Model\\Entity\\' . $entityName;

		$this->findings[] = [
			'line' => $node->getStartLine(),
			'varName' => $varName,
			'tableName' => $tableName,
			'entityClass' => $entityClass,
			'nullable' => $nullable,
		];

		return null;
	}

	/**
	 * Extract table name from method chain like $this->TableName->find()->...
	 *
	 * @param \PhpParser\Node\Expr $expr
	 * @return string|null
	 */
	private function extractTableName(Node\Expr $expr): ?string {
		// Walk back through the method chain
		while ($expr instanceof Node\Expr\MethodCall) {
			$expr = $expr->var;
		}

		// Should now be at $this->TableName
		if (
			$expr instanceof Node\Expr\PropertyFetch &&
			$expr->var instanceof Node\Expr\Variable &&
			$expr->var->name === 'this' &&
			$expr->name instanceof Node\Identifier
		) {
			return $expr->name->toString();
		}

		return null;
	}

}


================================================
FILE: src/Annotator/ClassAnnotatorTask/TestClassAnnotatorTask.php
================================================
<?php

namespace IdeHelper\Annotator\ClassAnnotatorTask;

use Cake\Core\Configure;
use IdeHelper\Annotation\AnnotationFactory;
use IdeHelper\Annotation\LinkAnnotation;
use IdeHelper\Annotation\UsesAnnotation;

/**
 * Classes that test a class in a magic-call way should automatically have `@link` annotated.
 * By default:
 * - Controller tests
 * - Command tests
 *
 * Use Configure key `IdeHelper.testClassPatterns` to add more types and their regex pattern.
 */
class TestClassAnnotatorTask extends AbstractClassAnnotatorTask implements PathAwareClassAnnotatorTaskInterface {

	/**
	 * @return array<string>
	 */
	public static function scanPaths(): array {
		return ['tests/TestCase/'];
	}

	/**
	 * Deprecated: $content, use $this->content instead.
	 *
	 * @param string $path
	 * @param string $content
	 * @return bool
	 */
	public function shouldRun(string $path, string $content): bool {
		if (!str_contains($path, DS . 'tests' . DS . 'TestCase' . DS)) {
			return false;
		}

		$defaultTypes = (array)Configure::read('IdeHelper.testClassPatterns');
		$types = $defaultTypes + [
			'Controller' => '#\bclass .+ControllerTest extends\b#',
			'Command' => '#\bclass .+CommandTest extends\b#',
		];
		$typeList = implode('|', array_keys($types));

		if (!preg_match('#^namespace .+\\\\Test\\\\TestCase\\\\(' . $typeList . ')\b#m', $content)) {
			return false;
		}
		if (!$this->matchesType($content, $types)) {
			return false;
		}

		return true;
	}

	/**
	 * @param string $content
	 * @param array<string> $types
	 * @return bool
	 */
	protected function matchesType(string $content, array $types): bool {
		foreach ($types as $type => $pattern) {
			if (preg_match($pattern, $content)) {
				return true;
			}
		}

		return false;
	}

	/**
	 * @param string $path
	 * @return bool
	 */
	public function annotate(string $path): bool {
		$class = $this->getTestedClass($this->content);
		if (!$class) {
			return false;
		}

		if ($this->hasUsesClassAttribute($this->content, $class)) {
			return false;
		}

		if ($this->hasLinkAnnotation($this->content, $class)) {
			return false;
		}

		$annotations = $this->buildLinkAnnotations([$class]);

		return $this->annotateContent($path, $this->content, $annotations);
	}

	/**
	 * @param string $content
	 *
	 * @return string|null
	 */
	protected function getTestedClass(string $content): ?string {
		preg_match('#namespace (.+);#', $content, $matches);
		if (!$matches) {
			return null;
		}

		$namespace = str_replace('\\Test\\TestCase\\', '\\', $matches[1]);

		preg_match('#\bclass (.+)Test extends#', $content, $matches);
		if (!$matches) {
			return null;
		}
		$className = $matches[1];

		$fullClassName = $namespace . '\\' . $className;
		if (!class_exists($fullClassName)) {
			return null;
		}

		return $fullClassName;
	}

	/**
	 * @param array<string> $classes
	 * @return array<\IdeHelper\Annotation\AbstractAnnotation>
	 */
	protected function buildLinkAnnotations(array $classes): array {
		$annotations = [];

		$tag = UsesAnnotation::TAG;
		if (Configure::read('IdeHelper.preferLinkOverUsesInTests') ?? true) {
			$tag = LinkAnnotation::TAG;
		}

		foreach ($classes as $className) {
			$annotations[] = AnnotationFactory::createOrFail($tag, '\\' . $className);
		}

		return $annotations;
	}

	/**
	 * @param string $content
	 * @param string $class
	 * @return bool
	 */
	protected function hasUsesClassAttribute(string $content, string $class): bool {
		$shortClassName = substr(strrchr($class, '\\') ?: $class, 1);
		$full = preg_quote($class, '#');
		$short = preg_quote($shortClassName, '#');
		$pattern = '#\#\[UsesClass\(\s*\\\\?(?:' . $full . '|' . $short . ')::class\s*\)\]#';

		return (bool)preg_match($pattern, $content);
	}

	/**
	 * @param string $content
	 * @param string $class
	 * @return bool
	 */
	protected function hasLinkAnnotation(string $content, string $class): bool {
		$shortClassName = substr(strrchr($class, '\\') ?: $class, 1);
		$full = preg_quote($class, '#');
		$short = preg_quote($shortClassName, '#');
		$pattern = '#@(uses|link)\s+\\\\?(?:' . $full . '|' . $short . ')\b#';

		return (bool)preg_match($pattern, $content);
	}

}


================================================
FILE: src/Annotator/ClassAnnotatorTaskCollection.php
================================================
<?php

namespace IdeHelper\Annotator;

use Cake\Core\Configure;
use IdeHelper\Annotator\ClassAnnotatorTask\FormClassAnnotatorTask;
use IdeHelper\Annotator\ClassAnnotatorTask\MailerClassAnnotatorTask;
use IdeHelper\Annotator\ClassAnnotatorTask\ModelAwareClassAnnotatorTask;
use IdeHelper\Annotator\ClassAnnotatorTask\TestClassAnnotatorTask;
use IdeHelper\Console\Io;

class ClassAnnotatorTaskCollection {

	/**
	 * @var array<class-string<\IdeHelper\Annotator\ClassAnnotatorTask\ClassAnnotatorTaskInterface>, class-string<\IdeHelper\Annotator\ClassAnnotatorTask\ClassAnnotatorTaskInterface>>
	 */
	protected array $defaultTasks = [
		ModelAwareClassAnnotatorTask::class => ModelAwareClassAnnotatorTask::class,
		FormClassAnnotatorTask::class => FormClassAnnotatorTask::class,
		MailerClassAnnotatorTask::class => MailerClassAnnotatorTask::class,
		TestClassAnnotatorTask::class => TestClassAnnotatorTask::class,
	];

	/**
	 * @var array<class-string<\IdeHelper\Annotator\ClassAnnotatorTask\ClassAnnotatorTaskInterface>>
	 */
	protected array $tasks;

	/**
	 * @param array<class-string<\IdeHelper\Annotator\ClassAnnotatorTask\ClassAnnotatorTaskInterface>> $tasks
	 */
	public function __construct(array $tasks = []) {
		$defaultTasks = $this->defaultTasks();
		$tasks += $defaultTasks;
		$this->tasks = $tasks;
	}

	/**
	 * @return array<class-string<\IdeHelper\Annotator\ClassAnnotatorTask\ClassAnnotatorTaskInterface>>
	 */
	public function defaultTasks(): array {
		$tasks = (array)Configure::read('IdeHelper.classAnnotatorTasks') + $this->defaultTasks;

		foreach ($tasks as $k => $v) {
			if (is_numeric($k)) {
				$tasks[$v] = $v;
				unset($tasks[$k]);

				continue;
			}

			if (!$v) {
				unset($tasks[$k]);
			}
		}

		return $tasks;
	}

	/**
	 * @param \IdeHelper\Console\Io $io
	 * @param array<string, mixed> $config
	 * @param string $content
	 * @return array<\IdeHelper\Annotator\ClassAnnotatorTask\ClassAnnotatorTaskInterface>
	 */
	public function tasks(Io $io, array $config, string $content): array {
		$tasks = $this->tasks;

		$collection = [];
		foreach ($tasks as $task) {
			/** @var \IdeHelper\Annotator\ClassAnnotatorTask\ClassAnnotatorTaskInterface $object */
			$object = new $task($io, $config, $content);
			$collection[] = $object;
		}

		return $collection;
	}

}


================================================
FILE: src/Annotator/CommandAnnotator.php
================================================
<?php

namespace IdeHelper\Annotator;

use IdeHelper\Annotator\Traits\ModelTrait;
use RuntimeException;

class CommandAnnotator extends AbstractAnnotator {

	use ModelTrait;

	/**
	 * @param string $path Path to file.
	 * @return bool
	 */
	public function annotate(string $path): bool {
		$className = pathinfo($path, PATHINFO_FILENAME);
		if ($className === 'Command' || !str_ends_with($className, 'Command')) {
			return false;
		}

		$content = file_get_contents($path);
		if ($content === false) {
			throw new RuntimeException('Cannot read file');
		}
		$primaryModelClass = $this->getPrimaryModelClass($content);
		$usedModels = $this->getUsedModels($content);
		if ($primaryModelClass) {
			$usedModels[] = $primaryModelClass;
		}
		$usedModels = array_unique($usedModels);

		$annotations = $this->getModelAnnotations($usedModels, $content);

		return $this->annotateContent($path, $content, $annotations);
	}

	/**
	 * @param string $content
	 *
	 * @return string|null
	 */
	protected function getPrimaryModelClass(string $content): ?string {
		if (!preg_match('/\bprotected \?string \$defaultTable = \'([a-z.\/]+)\'/i', $content, $matches)) {
			return null;
		}

		$modelName = $matches[1];

		return $modelName;
	}

}


================================================
FILE: src/Annotator/ComponentAnnotator.php
================================================
<?php

namespace IdeHelper\Annotator;

use Cake\Controller\ComponentRegistry;
use Cake\Controller\Controller;
use Cake\Core\Configure;
use Cake\Http\ServerRequest;
use IdeHelper\Annotation\AnnotationFactory;
use IdeHelper\Annotation\MethodAnnotation;
use IdeHelper\Annotation\PropertyAnnotation;
use IdeHelper\Annotator\Traits\ComponentTrait;
use IdeHelper\Utility\App;
use RuntimeException;
use Throwable;

class ComponentAnnotator extends AbstractAnnotator {

	use ComponentTrait;

	/**
	 * @param string $path Path to file.
	 * @return bool
	 */
	public function annotate(string $path): bool {
		$name = pathinfo($path, PA
Download .txt
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
Download .txt
SYMBOL INDEX (1488 symbols across 384 files)

FILE: annotate-watcher.cjs
  function getPathsFromArgs (line 6) | function getPathsFromArgs() {

FILE: src/Annotation/AbstractAnnotation.php
  class AbstractAnnotation (line 7) | abstract class AbstractAnnotation implements AnnotationInterface, Replac...
    method __construct (line 27) | public function __construct(string $type, ?int $index = null) {
    method __toString (line 35) | public function __toString(): string {
    method getType (line 42) | public function getType(): string {
    method hasIndex (line 49) | public function hasIndex(): bool {
    method getIndex (line 57) | public function getIndex(): int {
    method setInUse (line 71) | public function setInUse(bool $inUse = true): void {
    method isInUse (line 78) | public function isInUse(): bool {

FILE: src/Annotation/AnnotationFactory.php
  class AnnotationFactory (line 7) | class AnnotationFactory {
    method create (line 18) | public static function create($tag, $type, $content = null, $index = n...
    method createFromString (line 70) | public static function createFromString($annotation) {
    method createOrFail (line 128) | public static function createOrFail($tag, $type, $content = null, $ind...

FILE: src/Annotation/AnnotationInterface.php
  type AnnotationInterface (line 5) | interface AnnotationInterface {
    method getDescription (line 10) | public function getDescription(): string;
    method build (line 15) | public function build(): string;

FILE: src/Annotation/ExtendsAnnotation.php
  class ExtendsAnnotation (line 5) | class ExtendsAnnotation extends AbstractAnnotation {
    method __construct (line 18) | public function __construct(string $type, ?int $index = null) {
    method getDescription (line 36) | public function getDescription(): string {
    method build (line 43) | public function build(): string {
    method matches (line 54) | public function matches(AbstractAnnotation $annotation): bool {
    method replaceWith (line 67) | public function replaceWith(AbstractAnnotation $annotation): void {

FILE: src/Annotation/LinkAnnotation.php
  class LinkAnnotation (line 5) | class LinkAnnotation extends AbstractAnnotation {
    method __construct (line 18) | public function __construct($type, $index = null) {
    method getDescription (line 36) | public function getDescription(): string {
    method build (line 43) | public function build(): string {
    method matches (line 54) | public function matches(AbstractAnnotation $annotation): bool {
    method replaceWith (line 69) | public function replaceWith(AbstractAnnotation $annotation): void {

FILE: src/Annotation/MethodAnnotation.php
  class MethodAnnotation (line 5) | class MethodAnnotation extends AbstractAnnotation {
    method __construct (line 21) | public function __construct($type, $method, $index = null) {
    method getMethod (line 38) | public function getMethod(): string {
    method getDescription (line 45) | public function getDescription(): string {
    method build (line 52) | public function build(): string {
    method matches (line 63) | public function matches(AbstractAnnotation $annotation): bool {
    method replaceWith (line 79) | public function replaceWith(AbstractAnnotation $annotation): void {

FILE: src/Annotation/MixinAnnotation.php
  class MixinAnnotation (line 5) | class MixinAnnotation extends AbstractAnnotation {
    method __construct (line 18) | public function __construct($type, $index = null) {
    method getDescription (line 32) | public function getDescription(): string {
    method build (line 39) | public function build(): string {
    method matches (line 50) | public function matches(AbstractAnnotation $annotation): bool {
    method replaceWith (line 65) | public function replaceWith(AbstractAnnotation $annotation): void {

FILE: src/Annotation/ParamAnnotation.php
  class ParamAnnotation (line 5) | class ParamAnnotation extends AbstractAnnotation {
    method __construct (line 21) | public function __construct($type, $variable, $index = null) {
    method getVariable (line 36) | public function getVariable(): string {
    method getDescription (line 43) | public function getDescription(): string {
    method build (line 50) | public function build(): string {
    method matches (line 61) | public function matches(AbstractAnnotation $annotation): bool {
    method replaceWith (line 76) | public function replaceWith(AbstractAnnotation $annotation): void {

FILE: src/Annotation/PropertyAnnotation.php
  class PropertyAnnotation (line 5) | class PropertyAnnotation extends AbstractAnnotation {
    method __construct (line 21) | public function __construct($type, $property, $index = null) {
    method getProperty (line 39) | public function getProperty(): string {
    method getDescription (line 46) | public function getDescription(): string {
    method build (line 53) | public function build(): string {
    method matches (line 64) | public function matches(AbstractAnnotation $annotation): bool {
    method replaceWith (line 79) | public function replaceWith(AbstractAnnotation $annotation): void {

FILE: src/Annotation/PropertyReadAnnotation.php
  class PropertyReadAnnotation (line 5) | class PropertyReadAnnotation extends PropertyAnnotation {

FILE: src/Annotation/ReplacableAnnotationInterface.php
  type ReplacableAnnotationInterface (line 5) | interface ReplacableAnnotationInterface {
    method matches (line 12) | public function matches(AbstractAnnotation $annotation): bool;
    method replaceWith (line 19) | public function replaceWith(AbstractAnnotation $annotation): void;

FILE: src/Annotation/SeeAnnotation.php
  class SeeAnnotation (line 5) | class SeeAnnotation extends AbstractAnnotation {
    method __construct (line 18) | public function __construct($type, $index = null) {
    method getDescription (line 32) | public function getDescription(): string {
    method build (line 39) | public function build(): string {
    method matches (line 50) | public function matches(AbstractAnnotation $annotation): bool {
    method replaceWith (line 65) | public function replaceWith(AbstractAnnotation $annotation): void {

FILE: src/Annotation/UsesAnnotation.php
  class UsesAnnotation (line 5) | class UsesAnnotation extends AbstractAnnotation {
    method __construct (line 18) | public function __construct($type, $index = null) {
    method getDescription (line 32) | public function getDescription(): string {
    method build (line 39) | public function build(): string {
    method matches (line 50) | public function matches(AbstractAnnotation $annotation): bool {
    method replaceWith (line 65) | public function replaceWith(AbstractAnnotation $annotation): void {

FILE: src/Annotation/VariableAnnotation.php
  class VariableAnnotation (line 5) | class VariableAnnotation extends AbstractAnnotation {
    method __construct (line 23) | public function __construct($type, $variable, $index = null) {
    method getVariable (line 37) | public function getVariable(): string {
    method getDescription (line 44) | public function getDescription(): string {
    method setGuessed (line 53) | public function setGuessed($value) {
    method getGuessed (line 62) | public function getGuessed() {
    method build (line 69) | public function build(): string {
    method matches (line 80) | public function matches(AbstractAnnotation $annotation): bool {
    method replaceWith (line 95) | public function replaceWith(AbstractAnnotation $annotation): void {
    method hasNull (line 110) | protected function hasNull(): bool {
    method typeHasNull (line 118) | protected function typeHasNull(string $type): bool {

FILE: src/Annotator/AbstractAnnotator.php
  class AbstractAnnotator (line 43) | abstract class AbstractAnnotator {
    method __construct (line 123) | public function __construct(Io $io, array $config) {
    method annotate (line 137) | abstract public function annotate(string $path): bool;
    method displayDiff (line 144) | protected function displayDiff(string $oldContent, string $newContent)...
    method storeFile (line 193) | protected function storeFile(string $path, string $contents): void {
    method annotateContent (line 221) | protected function annotateContent(string $path, string $content, arra...
    method findDocBlockCloseTagIndex (line 271) | protected function findDocBlockCloseTagIndex(File $file, int $index): ...
    method appendToExistingDocBlock (line 289) | protected function appendToExistingDocBlock(File $file, int $docBlockC...
    method getLastWhitespaceOfPreviousLine (line 394) | protected function getLastWhitespaceOfPreviousLine(array $tokens, int ...
    method exists (line 409) | protected function exists(AbstractAnnotation $annotation, array &$exis...
    method needsReplacing (line 449) | protected function needsReplacing(AbstractAnnotation $annotation, arra...
    method allowsReplacing (line 469) | protected function allowsReplacing(AbstractAnnotation $annotation, arr...
    method parseExistingAnnotations (line 488) | protected function parseExistingAnnotations(File $file, int $closeTagI...
    method varInUse (line 581) | protected function varInUse(array $tokens, int $closeTagIndex, string ...
    method propertyInUse (line 611) | protected function propertyInUse(array $tokens, int $closeTagIndex, st...
    method methodInUse (line 669) | protected function methodInUse(array $tokens, int $closeTagIndex, stri...
    method needsNewLineInDocBlock (line 722) | protected function needsNewLineInDocBlock(File $file, int $lastTagInde...
    method addNewDocBlock (line 745) | protected function addNewDocBlock(File $file, int $index, array $annot...
    method isInlineDocBlock (line 779) | protected function isInlineDocBlock(File $file, int $docBlockCloseInde...
    method shouldSkip (line 792) | protected function shouldSkip(File $file, int $docBlockCloseIndex): bo...
    method getModelAnnotations (line 813) | protected function getModelAnnotations(array $usedModels, string $cont...
    method invokeProperty (line 843) | protected function invokeProperty(&$object, string $name) {
    method report (line 856) | protected function report(): void {
    method reportSkipped (line 887) | protected function reportSkipped(string $path): void {
    method resetCounter (line 909) | protected function resetCounter(): void {
    method beginningOfLine (line 924) | protected function beginningOfLine(File $file, $classOrTraitIndex) {
    method _isAbstract (line 941) | protected function _isAbstract($className) {
    method skipOverAttributes (line 953) | protected function skipOverAttributes(File $file, int $index): int {

FILE: src/Annotator/CallbackAnnotator.php
  class CallbackAnnotator (line 7) | class CallbackAnnotator extends AbstractAnnotator {
    method annotate (line 13) | public function annotate(string $path): bool {
    method invokeTasks (line 30) | protected function invokeTasks(string $path, string $content): void {
    method getTasks (line 47) | protected function getTasks(string $path, string $content): array {

FILE: src/Annotator/CallbackAnnotatorTask/AbstractCallbackAnnotatorTask.php
  class AbstractCallbackAnnotatorTask (line 10) | abstract class AbstractCallbackAnnotatorTask extends AbstractAnnotator {
    method __construct (line 22) | public function __construct(Io $io, array $config, $path, $content) {
    method getContent (line 34) | public function getContent(): string {
    method getMethods (line 43) | protected function getMethods(File $file) {
    method parseMethod (line 64) | protected function parseMethod(File $file, int $index) {
    method annotateMethods (line 85) | protected function annotateMethods(string $path, File $file, array $me...
    method findCloseTagIndex (line 125) | protected function findCloseTagIndex(File $file, int $index): ?int {

FILE: src/Annotator/CallbackAnnotatorTask/CallbackAnnotatorTaskInterface.php
  type CallbackAnnotatorTaskInterface (line 5) | interface CallbackAnnotatorTaskInterface {
    method shouldRun (line 11) | public function shouldRun(string $path): bool;
    method annotate (line 17) | public function annotate(string $path): bool;

FILE: src/Annotator/CallbackAnnotatorTask/TableCallbackAnnotatorTask.php
  class TableCallbackAnnotatorTask (line 13) | class TableCallbackAnnotatorTask extends AbstractCallbackAnnotatorTask i...
    method shouldRun (line 39) | public function shouldRun(string $path): bool {
    method annotate (line 72) | public function annotate(string $path): bool {
    method needsUpdate (line 102) | protected function needsUpdate(File $file, int $index, array &$method)...
    method generatePattern (line 133) | protected function generatePattern(): string {

FILE: src/Annotator/CallbackAnnotatorTask/VirtualFieldCallbackAnnotatorTask.php
  class VirtualFieldCallbackAnnotatorTask (line 13) | class VirtualFieldCallbackAnnotatorTask extends AbstractCallbackAnnotato...
    method shouldRun (line 27) | public function shouldRun(string $path): bool {
    method annotate (line 39) | public function annotate(string $path): bool {
    method annotateMethods (line 77) | protected function annotateMethods(string $path, File $file, array $me...
    method needsUpdate (line 127) | protected function needsUpdate(File $file, int $index, array &$method)...
    method isVirtualField (line 162) | protected function isVirtualField(array $method): bool {
    method getNamespace (line 177) | protected function getNamespace(File $file): string {
    method getClass (line 200) | protected function getClass(File $file): string {
    method indentation (line 218) | protected function indentation(File $file, int $endIndex): string {

FILE: src/Annotator/CallbackAnnotatorTaskCollection.php
  class CallbackAnnotatorTaskCollection (line 10) | class CallbackAnnotatorTaskCollection {
    method __construct (line 28) | public function __construct(array $tasks = []) {
    method defaultTasks (line 44) | public function defaultTasks(): array {
    method tasks (line 64) | public function tasks(Io $io, array $config, string $path, string $con...

FILE: src/Annotator/ClassAnnotator.php
  class ClassAnnotator (line 7) | class ClassAnnotator extends AbstractAnnotator {
    method annotate (line 13) | public function annotate(string $path): bool {
    method invokeTasks (line 30) | protected function invokeTasks(string $path, string $content): void {
    method getTasks (line 46) | protected function getTasks(string $content): array {

FILE: src/Annotator/ClassAnnotatorTask/AbstractClassAnnotatorTask.php
  class AbstractClassAnnotatorTask (line 11) | abstract class AbstractClassAnnotatorTask extends AbstractAnnotator {
    method __construct (line 20) | public function __construct(Io $io, array $config, $content) {
    method getContent (line 31) | public function getContent(): string {
    method annotateContent (line 42) | protected function annotateContent(string $path, string $content, arra...
    method annotateInlineContent (line 100) | protected function annotateInlineContent(string $path, string $content...
    method addNewInlineDocBlock (line 151) | protected function addNewInlineDocBlock(File $file, int $index, array ...
    method findFirstTokenOfLine (line 183) | protected function findFirstTokenOfLine(File $file, int $line): ?int {
    method detectIndentation (line 200) | protected function detectIndentation(File $file, int $index): string {

FILE: src/Annotator/ClassAnnotatorTask/ClassAnnotatorTaskInterface.php
  type ClassAnnotatorTaskInterface (line 5) | interface ClassAnnotatorTaskInterface {
    method shouldRun (line 14) | public function shouldRun(string $path, string $content): bool;
    method annotate (line 20) | public function annotate(string $path): bool;

FILE: src/Annotator/ClassAnnotatorTask/FormClassAnnotatorTask.php
  class FormClassAnnotatorTask (line 12) | class FormClassAnnotatorTask extends AbstractClassAnnotatorTask implemen...
    method shouldRun (line 21) | public function shouldRun(string $path, string $content): bool {
    method annotate (line 43) | public function annotate(string $path): bool {
    method buildUsesAnnotations (line 78) | protected function buildUsesAnnotations(array $classes): array {

FILE: src/Annotator/ClassAnnotatorTask/MailerClassAnnotatorTask.php
  class MailerClassAnnotatorTask (line 12) | class MailerClassAnnotatorTask extends AbstractClassAnnotatorTask implem...
    method shouldRun (line 21) | public function shouldRun(string $path, string $content): bool {
    method annotate (line 64) | public function annotate(string $path): bool {
    method buildUsesAnnotations (line 149) | protected function buildUsesAnnotations(array $classes): array {

FILE: src/Annotator/ClassAnnotatorTask/ModelAwareClassAnnotatorTask.php
  class ModelAwareClassAnnotatorTask (line 11) | class ModelAwareClassAnnotatorTask extends AbstractClassAnnotatorTask im...
    method shouldRun (line 20) | public function shouldRun(string $path, string $content): bool {
    method annotate (line 45) | public function annotate(string $path): bool {
    method getUsedModels (line 58) | protected function getUsedModels(string $content): array {
    method getClassName (line 75) | protected function getClassName(string $path, string $content): ?string {

FILE: src/Annotator/ClassAnnotatorTask/PathAwareClassAnnotatorTaskInterface.php
  type PathAwareClassAnnotatorTaskInterface (line 29) | interface PathAwareClassAnnotatorTaskInterface extends ClassAnnotatorTas...
    method scanPaths (line 34) | public static function scanPaths(): array;

FILE: src/Annotator/ClassAnnotatorTask/TableFindAnnotatorTask.php
  class TableFindAnnotatorTask (line 19) | class TableFindAnnotatorTask extends AbstractClassAnnotatorTask implemen...
    method shouldRun (line 28) | public function shouldRun(string $path, string $content): bool {
    method annotate (line 44) | public function annotate(string $path): bool {
    method findTableFinderCalls (line 70) | protected function findTableFinderCalls(): array {
    method buildVarAnnotation (line 100) | protected function buildVarAnnotation(string $entityClass, string $var...

FILE: src/Annotator/ClassAnnotatorTask/TableFindNodeVisitor.php
  class TableFindNodeVisitor (line 13) | class TableFindNodeVisitor extends NodeVisitorAbstract {
    method __construct (line 25) | public function __construct(string $appNamespace) {
    method getFindings (line 32) | public function getFindings(): array {
    method enterNode (line 40) | public function enterNode(Node $node): ?int {
    method extractTableName (line 90) | private function extractTableName(Node\Expr $expr): ?string {

FILE: src/Annotator/ClassAnnotatorTask/TestClassAnnotatorTask.php
  class TestClassAnnotatorTask (line 18) | class TestClassAnnotatorTask extends AbstractClassAnnotatorTask implemen...
    method scanPaths (line 23) | public static function scanPaths(): array {
    method shouldRun (line 34) | public function shouldRun(string $path, string $content): bool {
    method matchesType (line 61) | protected function matchesType(string $content, array $types): bool {
    method annotate (line 75) | public function annotate(string $path): bool {
    method getTestedClass (line 99) | protected function getTestedClass(string $content): ?string {
    method buildLinkAnnotations (line 125) | protected function buildLinkAnnotations(array $classes): array {
    method hasUsesClassAttribute (line 145) | protected function hasUsesClassAttribute(string $content, string $clas...
    method hasLinkAnnotation (line 159) | protected function hasLinkAnnotation(string $content, string $class): ...

FILE: src/Annotator/ClassAnnotatorTaskCollection.php
  class ClassAnnotatorTaskCollection (line 12) | class ClassAnnotatorTaskCollection {
    method __construct (line 32) | public function __construct(array $tasks = []) {
    method defaultTasks (line 41) | public function defaultTasks(): array {
    method tasks (line 66) | public function tasks(Io $io, array $config, string $content): array {

FILE: src/Annotator/CommandAnnotator.php
  class CommandAnnotator (line 8) | class CommandAnnotator extends AbstractAnnotator {
    method annotate (line 16) | public function annotate(string $path): bool {
    method getPrimaryModelClass (line 43) | protected function getPrimaryModelClass(string $content): ?string {

FILE: src/Annotator/ComponentAnnotator.php
  class ComponentAnnotator (line 17) | class ComponentAnnotator extends AbstractAnnotator {
    method annotate (line 25) | public function annotate(string $path): bool {
    method buildAnnotations (line 58) | protected function buildAnnotations(string $className): array {
    method getComponentAnnotations (line 74) | protected function getComponentAnnotations(string $className) {
    method hasControllerAnnotation (line 114) | protected function hasControllerAnnotation(string $content): bool {

FILE: src/Annotator/ControllerAnnotator.php
  class ControllerAnnotator (line 22) | class ControllerAnnotator extends AbstractAnnotator {
    method annotate (line 31) | public function annotate(string $path): bool {
    method parseExistingAnnotations (line 74) | protected function parseExistingAnnotations(File $file, int $closeTagI...
    method getPrimaryModelClass (line 94) | protected function getPrimaryModelClass(string $content, string $class...
    method getComponentAnnotations (line 132) | protected function getComponentAnnotations(string $className, string $...
    method getUsedComponents (line 163) | protected function getUsedComponents(string $className, string $path):...
    method getPaginationAnnotations (line 202) | protected function getPaginationAnnotations(string $content, ?string $...
    method extractPaginateEntities (line 226) | protected function extractPaginateEntities(string $content, ?string $p...
    method getEntity (line 263) | protected function getEntity(string $modelName, ?string $primaryModelN...
    method findModelClass (line 294) | protected function findModelClass(string $className, string $path): ?s...
    method getPrefix (line 337) | protected function getPrefix(string $className, string $path): string {
    method filterByTypedProperties (line 354) | protected function filterByTypedProperties(array $annotations, string ...

FILE: src/Annotator/EntityAnnotator.php
  class EntityAnnotator (line 23) | class EntityAnnotator extends AbstractAnnotator {
    method annotate (line 48) | public function annotate(string $path): bool {
    method propertyHintMap (line 75) | protected function propertyHintMap(string $content, DocBlockHelper $he...
    method hydrateSchemaFromAssoc (line 104) | protected function hydrateSchemaFromAssoc(array $schema): array {
    method getThrough (line 180) | protected function getThrough(BelongsToMany $association): ?Table {
    method junctionTableName (line 203) | protected function junctionTableName(BelongsToMany $association): stri...
    method nullable (line 219) | protected function nullable(Association $association, array $schema): ...
    method entityName (line 246) | protected function entityName(string $name): string {
    method buildExtendedEntityPropertyHintTypeMap (line 256) | protected function buildExtendedEntityPropertyHintTypeMap(array $prope...
    method columnTypeToHintType (line 281) | protected function columnTypeToHintType(string $type): ?string {
    method buildVirtualPropertyHintTypeMap (line 297) | protected function buildVirtualPropertyHintTypeMap(string $content): a...
    method returnType (line 360) | protected function returnType(File $file, array $tokens, int $function...
    method typeHint (line 386) | protected function typeHint(File $file, array $tokens, int $functionIn...
    method extractReturnType (line 413) | protected function extractReturnType(array $tokens, int $docBlockOpenT...
    method buildAnnotations (line 467) | protected function buildAnnotations(array $propertyHintMap, DocBlockHe...
    method virtualFields (line 498) | protected function virtualFields(string $name): array {
    method fqcnIfNeeded (line 520) | protected function fqcnIfNeeded(string $type, array $useStatements): s...

FILE: src/Annotator/HelperAnnotator.php
  class HelperAnnotator (line 13) | class HelperAnnotator extends AbstractAnnotator {
    method annotate (line 21) | public function annotate(string $path): bool {
    method getHelperAnnotations (line 67) | protected function getHelperAnnotations(array $helperMap): array {

FILE: src/Annotator/ModelAnnotator.php
  class ModelAnnotator (line 23) | class ModelAnnotator extends AbstractAnnotator {
    method __construct (line 51) | public function __construct(Io $io, array $config) {
    method annotate (line 79) | public function annotate(string $path): bool {
    method table (line 158) | protected function table(string $path, string $entityName, array $asso...
    method buildAnnotations (line 178) | protected function buildAnnotations(array $associations, string $entit...
    method entity (line 276) | protected function entity(string $entityClass, string $entityName, Tab...
    method parseLoadedBehaviors (line 306) | protected function parseLoadedBehaviors(string $content): array {
    method getAssociations (line 327) | protected function getAssociations(AssociationCollection $tableAssocia...
    method getBehaviors (line 350) | protected function getBehaviors($table): array {
    method extractBehaviors (line 384) | protected function extractBehaviors(array $map): array {
    method resolveBehaviorName (line 405) | protected function resolveBehaviorName(string $className, string $name...
    method resolvePluginName (line 420) | protected function resolvePluginName(string $className, string $name):...
    method getEntityAnnotator (line 449) | protected function getEntityAnnotator(string $entityClass, TableSchema...
    method addBehaviorMixins (line 465) | protected function addBehaviorMixins(array $result, array $behaviors):...
    method addBehaviorExtends (line 492) | protected function addBehaviorExtends(array $result, array $behaviors,...
    method supportsEntityTemplate (line 533) | protected function supportsEntityTemplate(): bool {

FILE: src/Annotator/Template/VariableExtractor.php
  class VariableExtractor (line 11) | class VariableExtractor {
    method extract (line 17) | public function extract(File $file): array {
    method collect (line 50) | protected function collect(File $file): array {
    method getVar (line 107) | protected function getVar(File $file, array $token, int $index): array {
    method getVarType (line 132) | protected function getVarType(File $file, array $result): ?string {
    method getExcludeReason (line 159) | protected function getExcludeReason(File $file, array $result): ?string {
    method isLoopVar (line 182) | protected function isLoopVar(File $file, array $result): bool {
    method isInLoop (line 204) | protected function isInLoop(File $file, array $result, int $assignment...
    method isTryCatchVar (line 224) | protected function isTryCatchVar(File $file, array $result): bool {
    method isAnonymousFunctionParameter (line 248) | protected function isAnonymousFunctionParameter(File $file, array $res...
    method isAssignment (line 275) | protected function isAssignment(File $file, array $result): bool {
    method getVarsFromString (line 324) | protected function getVarsFromString(File $file, array $token, int $i)...
    method getVarsFromCompact (line 357) | protected function getVarsFromCompact(File $file, int $index): array {

FILE: src/Annotator/TemplateAnnotator.php
  class TemplateAnnotator (line 21) | class TemplateAnnotator extends AbstractAnnotator {
    method annotate (line 27) | public function annotate(string $path): bool {
    method annotateContent (line 45) | protected function annotateContent(string $path, string $content, arra...
    method findExistingDocBlock (line 107) | protected function findExistingDocBlock(File $file, int $phpOpenTagInd...
    method addNewTemplateDocBlock (line 152) | protected function addNewTemplateDocBlock(File $file, array $annotatio...
    method needsPhpTag (line 212) | protected function needsPhpTag(File $file, int $phpOpenTagIndex): bool {
    method needsViewAnnotation (line 241) | protected function needsViewAnnotation(string $content): bool {
    method getViewAnnotation (line 260) | protected function getViewAnnotation() {
    method allowsReplacing (line 282) | protected function allowsReplacing(AbstractAnnotation $annotation, arr...
    method isFirstContent (line 318) | protected function isFirstContent(array $tokens, int $phpOpenTagIndex)...
    method getEntityAnnotations (line 337) | protected function getEntityAnnotations(string $content, array $variab...
    method parseFormEntities (line 358) | protected function parseFormEntities(string $content): array {
    method parseLoopEntities (line 389) | protected function parseLoopEntities(string $content): array {
    method parseEntities (line 435) | protected function parseEntities(string $content): array {
    method isInlineDocBlockRedundant (line 470) | protected function isInlineDocBlockRedundant(File $file, array $annota...
    method buildAnnotations (line 490) | protected function buildAnnotations(string $path, string $content): ar...
    method getTemplateVariables (line 533) | protected function getTemplateVariables(string $path, string $content)...
    method getVariableAnnotation (line 565) | protected function getVariableAnnotation(array $variable) {
    method isV4 (line 593) | protected function isV4(): bool {
    method checkForDeclareStatement (line 619) | protected function checkForDeclareStatement(File $file, ?int $phpOpenT...

FILE: src/Annotator/Traits/ComponentTrait.php
  type ComponentTrait (line 11) | trait ComponentTrait {
    method findClassName (line 19) | protected function findClassName(string $component, bool $includeApp):...

FILE: src/Annotator/Traits/DocBlockTrait.php
  type DocBlockTrait (line 29) | trait DocBlockTrait {
    method getValueNode (line 37) | protected static function getValueNode(string $tagName, string $tagCom...
    method valueNodeParts (line 59) | protected function valueNodeParts(PhpDocTagValueNode $valueNode): array {
    method stringifyValueNode (line 84) | protected function stringifyValueNode(array $parts, PhpDocTagValueNode...
    method hasInheritDoc (line 139) | protected function hasInheritDoc(File $phpCsFile, int $docBlockStartIn...
    method containsIterableSyntax (line 169) | protected function containsIterableSyntax(array $docBlockTypes): bool {
    method renderUnionTypes (line 184) | protected function renderUnionTypes(array $typeNodes): string {

FILE: src/Annotator/Traits/FileTrait.php
  type FileTrait (line 11) | trait FileTrait {
    method getFile (line 19) | protected function getFile(string $file, ?string $content = null): File {
    method getFixer (line 46) | protected function getFixer(File $file): Fixer {

FILE: src/Annotator/Traits/HelperTrait.php
  type HelperTrait (line 11) | trait HelperTrait {
    method findClassName (line 19) | protected function findClassName(string $helper, bool $includeApp): ?s...

FILE: src/Annotator/Traits/ModelTrait.php
  type ModelTrait (line 8) | trait ModelTrait {
    method getUsedModels (line 15) | protected function getUsedModels(string $content): array {

FILE: src/Annotator/Traits/UseStatementsTrait.php
  type UseStatementsTrait (line 13) | trait UseStatementsTrait {
    method getUseStatements (line 20) | protected function getUseStatements(File $phpcsFile): array {
    method isMultipleUseStatement (line 88) | protected function isMultipleUseStatement(string $statementContent): b...

FILE: src/Annotator/ViewAnnotator.php
  class ViewAnnotator (line 16) | class ViewAnnotator extends AbstractAnnotator {
    method annotate (line 29) | public function annotate(string $path): bool {
    method getHelpers (line 44) | protected function getHelpers(): array {
    method addExtractedHelpers (line 57) | protected function addExtractedHelpers(array $helperArray) {
    method checkTemplates (line 87) | protected function checkTemplates(string $folder) {
    method parseHelpersInContent (line 109) | protected function parseHelpersInContent(string $content) {
    method parseViewClass (line 123) | protected function parseViewClass(): array {
    method addAppHelpers (line 153) | protected function addAppHelpers(array $helperArray): array {
    method buildAnnotations (line 184) | protected function buildAnnotations(array $helpers): array {
    method getFolders (line 196) | protected function getFolders(): array {

FILE: src/CodeCompletion/CodeCompletionGenerator.php
  class CodeCompletionGenerator (line 8) | class CodeCompletionGenerator {
    method __construct (line 15) | public function __construct(TaskCollection $taskCollection) {
    method generate (line 22) | public function generate(): array {
    method buildContent (line 56) | protected function buildContent(array $array): string {
    method type (line 65) | protected function type(string $namespace): string {
    method path (line 73) | protected function path(): string {

FILE: src/CodeCompletion/Task/BehaviorTask.php
  class BehaviorTask (line 10) | class BehaviorTask implements TaskInterface {
    method type (line 20) | public function type(): string {
    method create (line 27) | public function create(): string {
    method collectBehaviors (line 50) | protected function collectBehaviors(): array {
    method addBehaviors (line 76) | protected function addBehaviors(array $behaviors, string $folder, ?str...
    method build (line 105) | protected function build(array $behaviors): string {

FILE: src/CodeCompletion/Task/ControllerEventsTask.php
  class ControllerEventsTask (line 7) | class ControllerEventsTask implements TaskInterface {
    method type (line 17) | public function type(): string {
    method create (line 24) | public function create(): string {
    method events (line 55) | protected function events(bool $returnType): string {

FILE: src/CodeCompletion/Task/ModelEventsTask.php
  class ModelEventsTask (line 5) | class ModelEventsTask implements TaskInterface {
    method type (line 15) | public function type(): string {
    method create (line 22) | public function create(): string {

FILE: src/CodeCompletion/Task/SelectQueryTask.php
  class SelectQueryTask (line 5) | class SelectQueryTask implements TaskInterface {
    method type (line 15) | public function type(): string {
    method create (line 22) | public function create(): string {

FILE: src/CodeCompletion/Task/TaskInterface.php
  type TaskInterface (line 5) | interface TaskInterface {
    method type (line 10) | public function type(): string;
    method create (line 15) | public function create(): string;

FILE: src/CodeCompletion/Task/ViewEventsTask.php
  class ViewEventsTask (line 5) | class ViewEventsTask implements TaskInterface {
    method type (line 15) | public function type(): string {
    method create (line 22) | public function create(): string {

FILE: src/CodeCompletion/TaskCollection.php
  class TaskCollection (line 14) | class TaskCollection {
    method __construct (line 35) | public function __construct(array $tasks = []) {
    method add (line 55) | protected function add($task) {
    method tasks (line 75) | public function tasks(): array {
    method getMap (line 82) | public function getMap(): array {

FILE: src/Command/Annotate/AllCommand.php
  class AllCommand (line 10) | class AllCommand extends AnnotateCommand {
    method getDescription (line 17) | public static function getDescription(): string {
    method execute (line 26) | public function execute(Arguments $args, ConsoleIo $io): int {

FILE: src/Command/Annotate/CallbacksCommand.php
  class CallbacksCommand (line 12) | class CallbacksCommand extends AnnotateCommand {
    method getDescription (line 17) | public static function getDescription(): string {
    method buildOptionParser (line 25) | protected function buildOptionParser(ConsoleOptionParser $parser): Con...
    method execute (line 37) | public function execute(Arguments $args, ConsoleIo $io): int {
    method _callbacks (line 62) | protected function _callbacks(string $folder) {

FILE: src/Command/Annotate/ClassesCommand.php
  class ClassesCommand (line 14) | class ClassesCommand extends AnnotateCommand {
    method getDescription (line 19) | public static function getDescription(): string {
    method buildOptionParser (line 27) | protected function buildOptionParser(ConsoleOptionParser $parser): Con...
    method execute (line 39) | public function execute(Arguments $args, ConsoleIo $io): int {
    method _walkPathAwareTasks (line 74) | protected function _walkPathAwareTasks(array $tasks): void {
    method _normalizeScanPath (line 112) | protected function _normalizeScanPath(string $relPath): string {
    method _classes (line 123) | protected function _classes(string $folder) {

FILE: src/Command/Annotate/CommandsCommand.php
  class CommandsCommand (line 10) | class CommandsCommand extends AnnotateCommand {
    method getDescription (line 15) | public static function getDescription(): string {
    method execute (line 24) | public function execute(Arguments $args, ConsoleIo $io): int {
    method _commands (line 47) | protected function _commands(string $folder) {

FILE: src/Command/Annotate/ComponentsCommand.php
  class ComponentsCommand (line 10) | class ComponentsCommand extends AnnotateCommand {
    method getDescription (line 15) | public static function getDescription(): string {
    method execute (line 24) | public function execute(Arguments $args, ConsoleIo $io): int {
    method _components (line 46) | protected function _components(string $folder) {

FILE: src/Command/Annotate/ControllersCommand.php
  class ControllersCommand (line 11) | class ControllersCommand extends AnnotateCommand {
    method getDescription (line 16) | public static function getDescription(): string {
    method execute (line 25) | public function execute(Arguments $args, ConsoleIo $io): int {
    method _controllers (line 48) | protected function _controllers(string $folder) {

FILE: src/Command/Annotate/HelpersCommand.php
  class HelpersCommand (line 10) | class HelpersCommand extends AnnotateCommand {
    method getDescription (line 15) | public static function getDescription(): string {
    method execute (line 24) | public function execute(Arguments $args, ConsoleIo $io): int {
    method _helpers (line 46) | protected function _helpers($folder) {

FILE: src/Command/Annotate/ModelsCommand.php
  class ModelsCommand (line 10) | class ModelsCommand extends AnnotateCommand {
    method getDescription (line 15) | public static function getDescription(): string {
    method execute (line 24) | public function execute(Arguments $args, ConsoleIo $io): int {
    method _models (line 46) | protected function _models(string $folder) {

FILE: src/Command/Annotate/TemplatesCommand.php
  class TemplatesCommand (line 10) | class TemplatesCommand extends AnnotateCommand {
    method getDescription (line 15) | public static function getDescription(): string {
    method execute (line 24) | public function execute(Arguments $args, ConsoleIo $io): int {
    method _templates (line 47) | protected function _templates($folder) {

FILE: src/Command/Annotate/ViewCommand.php
  class ViewCommand (line 11) | class ViewCommand extends AnnotateCommand {
    method getDescription (line 16) | public static function getDescription(): string {
    method execute (line 25) | public function execute(Arguments $args, ConsoleIo $io): int {

FILE: src/Command/AnnotateCommand.php
  class AnnotateCommand (line 14) | abstract class AnnotateCommand extends Command {
    method initialize (line 38) | public function initialize(): void {
    method execute (line 57) | public function execute(Arguments $args, ConsoleIo $io) {
    method buildOptionParser (line 76) | protected function buildOptionParser(ConsoleOptionParser $parser): Con...
    method _io (line 121) | protected function _io(): Io {
    method _shouldSkip (line 131) | protected function _shouldSkip(string $fileName, ?string $path = null)...
    method _files (line 148) | protected function _files(): array {
    method _shouldSkipExtension (line 176) | protected function _shouldSkipExtension(string $extension): bool {
    method getAnnotator (line 187) | protected function getAnnotator(string $class): AbstractAnnotator {
    method _annotatorMadeChanges (line 203) | protected function _annotatorMadeChanges(): bool {

FILE: src/Command/Command.php
  class Command (line 13) | abstract class Command extends CoreCommand {
    method execute (line 33) | public function execute(Arguments $args, ConsoleIo $io) {
    method getPaths (line 44) | protected function getPaths(?string $type = null): array {
    method getPlugins (line 83) | protected function getPlugins(string $plugin): array {
    method filterPlugins (line 112) | protected function filterPlugins(array $plugins, string $pattern): arr...
    method setPlugin (line 122) | protected function setPlugin(string $plugin): void {

FILE: src/Command/GenerateCodeCompletionCommand.php
  class GenerateCodeCompletionCommand (line 12) | class GenerateCodeCompletionCommand extends Command {
    method getDescription (line 17) | public static function getDescription(): string {
    method execute (line 28) | public function execute(Arguments $args, ConsoleIo $io): int {
    method buildOptionParser (line 47) | protected function buildOptionParser(ConsoleOptionParser $parser): Con...
    method getGenerator (line 65) | protected function getGenerator(): CodeCompletionGenerator {

FILE: src/Command/GeneratePhpStormMetaCommand.php
  class GeneratePhpStormMetaCommand (line 14) | class GeneratePhpStormMetaCommand extends Command {
    method getDescription (line 29) | public static function getDescription(): string {
    method execute (line 40) | public function execute(Arguments $args, ConsoleIo $io): int {
    method buildOptionParser (line 79) | protected function buildOptionParser(ConsoleOptionParser $parser): Con...
    method getGenerator (line 99) | protected function getGenerator(): PhpstormGenerator {
    method getMetaFilePath (line 109) | protected function getMetaFilePath(): string {
    method ensureDir (line 121) | protected function ensureDir(): void {
    method io (line 131) | protected function io(): Io {

FILE: src/Command/IlluminateCommand.php
  class IlluminateCommand (line 12) | class IlluminateCommand extends Command {
    method getDescription (line 22) | public static function getDescription(): string {
    method execute (line 36) | public function execute(Arguments $args, ConsoleIo $io): int {
    method buildOptionParser (line 80) | protected function buildOptionParser(ConsoleOptionParser $parser): Con...
    method getIlluminator (line 124) | protected function getIlluminator(Arguments $args): Illuminator {
    method getTaskList (line 140) | protected function getTaskList(): array {
    method io (line 149) | protected function io(): Io {

FILE: src/Console/Io.php
  class Io (line 12) | class Io {
    method verbose (line 23) | public function verbose(array|string $message, int $newlines = 1): ?int {
    method __construct (line 30) | public function __construct(ConsoleIo $io) {
    method quiet (line 41) | public function quiet(array|string $message, int $newlines = 1): ?int {
    method out (line 61) | public function out(array|string $message = '', int $newlines = 1, int...
    method error (line 73) | public function error(array|string $message = '', int $newlines = 1): ...
    method err (line 86) | public function err(array|string $message = '', int $newlines = 1): ?i...
    method info (line 99) | public function info(array|string $message = '', int $newlines = 1, in...
    method comment (line 112) | public function comment(array|string $message = '', int $newlines = 1,...
    method warn (line 124) | public function warn(array|string $message = '', int $newlines = 1): ?...
    method success (line 137) | public function success(array|string $message = '', int $newlines = 1,...
    method nl (line 147) | public function nl(int $multiplier = 1): string {
    method hr (line 158) | public function hr(int $newlines = 0, int $width = 63): void {
    method abort (line 171) | public function abort(string $message, int $exitCode = Command::CODE_E...

FILE: src/Filesystem/Folder.php
  class Folder (line 32) | class Folder {
    method __construct (line 119) | public function __construct(?string $path = null, bool $create = false...
    method pwd (line 143) | public function pwd(): ?string {
    method cd (line 153) | public function cd(string $path) {
    method read (line 172) | public function read($sort = self::SORT_NAME, $exceptions = false, boo...
    method find (line 237) | public function find(string $regexpPattern = '.*', $sort = false): arr...
    method findRecursive (line 250) | public function findRecursive(string $pattern = '.*', bool $sort = fal...
    method _findRecursive (line 268) | protected function _findRecursive(string $pattern, bool $sort = false)...
    method isWindowsPath (line 293) | public static function isWindowsPath(string $path): bool {
    method isAbsolute (line 303) | public static function isAbsolute(string $path): bool {
    method isRegisteredStreamWrapper (line 320) | public static function isRegisteredStreamWrapper(string $path): bool {
    method normalizeFullPath (line 331) | public static function normalizeFullPath(string $path): string {
    method correctSlashFor (line 344) | public static function correctSlashFor(string $path): string {
    method slashTerm (line 354) | public static function slashTerm(string $path): string {
    method addPathElement (line 369) | public static function addPathElement(string $path, $element): string {
    method inPath (line 384) | public function inPath(string $path, bool $reverse = false): bool {
    method chmod (line 410) | public function chmod(string $path, ?int $mode = null, bool $recursive...
    method subdirectories (line 466) | public function subdirectories(?string $path = null, bool $fullPath = ...
    method tree (line 498) | public function tree(?string $path = null, $exceptions = false, ?strin...
    method create (line 589) | public function create(string $pathname, ?int $mode = null): bool {
    method dirsize (line 634) | public function dirsize(): int {
    method delete (line 671) | public function delete(?string $path = null): bool {
    method copy (line 752) | public function copy(string $to, array $options = []): bool {
    method move (line 852) | public function move(string $to, array $options = []): bool {
    method messages (line 868) | public function messages(bool $reset = true): array {
    method errors (line 883) | public function errors(bool $reset = true): array {
    method realpath (line 898) | public function realpath($path) {
    method isSlashTerm (line 940) | public static function isSlashTerm(string $path): bool {

FILE: src/Generator/Directive/BaseDirective.php
  class BaseDirective (line 12) | abstract class BaseDirective {
    method key (line 19) | abstract public function key();
    method build (line 26) | abstract public function build();
    method buildList (line 34) | protected function buildList(array $array, int $indentation = 2): stri...
    method buildKeyValueMap (line 59) | protected function buildKeyValueMap(array $array, int $indentation = 3...

FILE: src/Generator/Directive/ExitPoint.php
  class ExitPoint (line 16) | class ExitPoint extends BaseDirective {
    method __construct (line 28) | public function __construct($method) {
    method key (line 37) | public function key() {
    method toArray (line 44) | public function toArray() {
    method build (line 53) | public function build() {

FILE: src/Generator/Directive/ExpectedArguments.php
  class ExpectedArguments (line 29) | class ExpectedArguments extends BaseDirective {
    method __construct (line 50) | public function __construct($method, $position, array $list) {
    method key (line 61) | public function key() {
    method toArray (line 68) | public function toArray() {
    method build (line 79) | public function build() {

FILE: src/Generator/Directive/ExpectedReturnValues.php
  class ExpectedReturnValues (line 25) | class ExpectedReturnValues extends BaseDirective {
    method __construct (line 43) | public function __construct($method, array $list) {
    method key (line 53) | public function key() {
    method toArray (line 60) | public function toArray() {
    method build (line 70) | public function build() {

FILE: src/Generator/Directive/Override.php
  class Override (line 20) | class Override extends BaseDirective {
    method __construct (line 38) | public function __construct($method, array $map) {
    method toArray (line 46) | public function toArray() {
    method key (line 58) | public function key() {
    method build (line 65) | public function build() {

FILE: src/Generator/Directive/RegisterArgumentsSet.php
  class RegisterArgumentsSet (line 20) | class RegisterArgumentsSet extends BaseDirective {
    method __construct (line 38) | public function __construct($set, array $list) {
    method key (line 48) | public function key() {
    method toArray (line 55) | public function toArray() {
    method build (line 65) | public function build() {
    method __toString (line 82) | public function __toString() {

FILE: src/Generator/GeneratorInterface.php
  type GeneratorInterface (line 5) | interface GeneratorInterface {
    method generate (line 10) | public function generate(): string;

FILE: src/Generator/PhpstormGenerator.php
  class PhpstormGenerator (line 8) | class PhpstormGenerator implements GeneratorInterface {
    method __construct (line 18) | public function __construct(TaskCollection $taskCollection, ?Io $io = ...
    method generate (line 26) | public function generate(): string {
    method build (line 39) | protected function build(array $map): string {
    method outputSetInfo (line 65) | protected function outputSetInfo(array $map): void {

FILE: src/Generator/Task/BehaviorTask.php
  class BehaviorTask (line 15) | class BehaviorTask implements TaskInterface {
    method collect (line 50) | public function collect(): array {
    method collectBehaviors (line 98) | protected function collectBehaviors(): array {
    method addBehaviors (line 126) | protected function addBehaviors(array $behaviors, string $folder, ?str...

FILE: src/Generator/Task/CacheTask.php
  class CacheTask (line 10) | class CacheTask implements TaskInterface {
    method collect (line 39) | public function collect(): array {
    method collectCacheEngines (line 57) | protected function collectCacheEngines(): array {

FILE: src/Generator/Task/CellTask.php
  class CellTask (line 14) | class CellTask implements TaskInterface {
    method collect (line 23) | public function collect(): array {
    method collectCells (line 45) | protected function collectCells(): array {
    method addCells (line 71) | protected function addCells(array $components, $folder, $plugin = null) {

FILE: src/Generator/Task/ComponentTask.php
  class ComponentTask (line 16) | class ComponentTask implements TaskInterface {
    method collect (line 38) | public function collect(): array {
    method collectComponents (line 70) | protected function collectComponents(): array {
    method addComponents (line 96) | protected function addComponents(array $components, $folder, $plugin =...

FILE: src/Generator/Task/ConfigureTask.php
  class ConfigureTask (line 10) | class ConfigureTask implements TaskInterface {
    method collect (line 35) | public function collect(): array {
    method collectKeys (line 53) | protected function collectKeys(): array {
    method addKeys (line 71) | protected function addKeys(array $keys, array $data, array $path = [])...

FILE: src/Generator/Task/ConnectionTask.php
  class ConnectionTask (line 9) | class ConnectionTask implements TaskInterface {
    method collect (line 19) | public function collect(): array {
    method connectionKeys (line 35) | protected function connectionKeys(): array {

FILE: src/Generator/Task/ConsoleHelperTask.php
  class ConsoleHelperTask (line 13) | class ConsoleHelperTask implements TaskInterface {
    method collect (line 27) | public function collect(): array {
    method collectHelpers (line 52) | protected function collectHelpers(): array {
    method addHelpers (line 78) | protected function addHelpers(array $helpers, $folder, $plugin = null) {

FILE: src/Generator/Task/ConsoleTask.php
  class ConsoleTask (line 8) | class ConsoleTask implements TaskInterface {
    method collect (line 18) | public function collect(): array {

FILE: src/Generator/Task/DatabaseTableColumnNameTask.php
  class DatabaseTableColumnNameTask (line 13) | class DatabaseTableColumnNameTask extends DatabaseTableTask {
    method collect (line 34) | public function collect(): array {
    method collectTableColumnNames (line 62) | protected function collectTableColumnNames(): array {
    method getConnection (line 81) | protected function getConnection(string $name = 'default') {

FILE: src/Generator/Task/DatabaseTableColumnTypeTask.php
  class DatabaseTableColumnTypeTask (line 18) | class DatabaseTableColumnTypeTask implements TaskInterface {
    method collect (line 65) | public function collect(): array {
    method collectTableColumnTypes (line 90) | protected function collectTableColumnTypes(): array {
    method getAdapter (line 105) | protected function getAdapter(string $name = 'default') {
    method getAdapterV5 (line 120) | protected function getAdapterV5(string $name): AdapterInterface {
    method getAdapterV4 (line 146) | protected function getAdapterV4(string $name): PhinxAdapterInterface {

FILE: src/Generator/Task/DatabaseTableTask.php
  class DatabaseTableTask (line 16) | class DatabaseTableTask implements TaskInterface {
    method collect (line 41) | public function collect(): array {
    method collectTables (line 66) | protected function collectTables(): array {
    method getConnection (line 98) | protected function getConnection(string $name = 'default') {

FILE: src/Generator/Task/DatabaseTypeTask.php
  class DatabaseTypeTask (line 15) | class DatabaseTypeTask implements TaskInterface {
    method collect (line 22) | public function collect(): array {
    method getTypes (line 53) | protected function getTypes(): array {

FILE: src/Generator/Task/ElementTask.php
  class ElementTask (line 15) | class ElementTask implements TaskInterface {
    method collect (line 22) | public function collect(): array {
    method collectElements (line 43) | protected function collectElements(): array {
    method addElements (line 67) | protected function addElements(array $result, array $paths, ?string $p...

FILE: src/Generator/Task/EntityTask.php
  class EntityTask (line 11) | class EntityTask extends ModelTask {
    method collect (line 37) | public function collect(): array {
    method getEntityFields (line 58) | protected function getEntityFields(): array {
    method addFromRelations (line 118) | protected function addFromRelations(Table $table): array {

FILE: src/Generator/Task/EnvTask.php
  class EnvTask (line 8) | class EnvTask implements TaskInterface {
    method collect (line 55) | public function collect(): array {
    method envKeys (line 72) | protected function envKeys(): array {

FILE: src/Generator/Task/FixtureTask.php
  class FixtureTask (line 11) | class FixtureTask implements TaskInterface {
    method collect (line 21) | public function collect(): array {
    method getFixtures (line 36) | protected function getFixtures(): array {
    method parseFixtures (line 69) | protected function parseFixtures(string $fixtureFolder, string $domain...

FILE: src/Generator/Task/FormHelperTask.php
  class FormHelperTask (line 13) | class FormHelperTask extends ModelTask {
    method collect (line 20) | public function collect(): array {
    method collectFieldNames (line 37) | protected function collectFieldNames(): array {

FILE: src/Generator/Task/HelperTask.php
  class HelperTask (line 15) | class HelperTask implements TaskInterface {
    method collect (line 36) | public function collect(): array {
    method collectHelpers (line 67) | protected function collectHelpers(): array {
    method addHelpers (line 93) | protected function addHelpers(array $helpers, $folder, $plugin = null) {

FILE: src/Generator/Task/LayoutTask.php
  class LayoutTask (line 14) | class LayoutTask implements TaskInterface {
    method collect (line 21) | public function collect(): array {
    method collectLayouts (line 42) | protected function collectLayouts(): array {
    method addLayouts (line 66) | protected function addLayouts(array $result, array $paths, ?string $pl...

FILE: src/Generator/Task/MailerTask.php
  class MailerTask (line 13) | class MailerTask implements TaskInterface {
    method collect (line 22) | public function collect(): array {
    method collectMailers (line 44) | protected function collectMailers(): array {
    method addMailers (line 70) | protected function addMailers(array $components, $folder, $plugin = nu...

FILE: src/Generator/Task/ModelTask.php
  class ModelTask (line 14) | class ModelTask implements TaskInterface {
    method __construct (line 32) | public function __construct() {
    method clearBuffer (line 39) | public static function clearBuffer(): void {
    method collect (line 46) | public function collect(): array {
    method collectModels (line 68) | protected function collectModels(): array {
    method addModels (line 100) | protected function addModels(array $models, $folder, $plugin = null) {

FILE: src/Generator/Task/PluginTask.php
  class PluginTask (line 11) | class PluginTask implements TaskInterface {
    method collect (line 30) | public function collect(): array {
    method collectPlugins (line 54) | protected function collectPlugins(): array {

FILE: src/Generator/Task/RequestTask.php
  class RequestTask (line 15) | class RequestTask implements TaskInterface {
    method collect (line 50) | public function collect(): array {
    method collectParamKeys (line 71) | protected function collectParamKeys(): array {
    method collectAttributes (line 85) | protected function collectAttributes(): array {
    method addAttributesFromPlugins (line 116) | protected function addAttributesFromPlugins(array $attributes): array {

FILE: src/Generator/Task/RoutePathTask.php
  class RoutePathTask (line 17) | class RoutePathTask implements TaskInterface {
    method __construct (line 30) | public function __construct() {
    method collect (line 37) | public function collect(): array {
    method collectPaths (line 66) | protected function collectPaths(): array {
    method _paths (line 92) | protected function _paths(string $folder, ?string $plugin = null, ?str...

FILE: src/Generator/Task/TableAssociationTask.php
  class TableAssociationTask (line 13) | class TableAssociationTask extends ModelTask {
    method collect (line 30) | public function collect(): array {

FILE: src/Generator/Task/TableFinderTask.php
  class TableFinderTask (line 18) | class TableFinderTask extends ModelTask {
    method collect (line 48) | public function collect(): array {
    method collectFinders (line 71) | protected function collectFinders(): array {
    method getCustomFinders (line 91) | protected function getCustomFinders(): array {
    method invokeProperty (line 152) | protected function invokeProperty(&$object, string $name) {
    method getFinderMethods (line 167) | protected function getFinderMethods($className) {
    method addMethod (line 187) | protected function addMethod(array $result, $method, $className) {

FILE: src/Generator/Task/TaskInterface.php
  type TaskInterface (line 5) | interface TaskInterface {
    method collect (line 10) | public function collect(): array;

FILE: src/Generator/Task/TranslationKeyTask.php
  class TranslationKeyTask (line 19) | class TranslationKeyTask implements TaskInterface {
    method __construct (line 23) | public function __construct() {
    method collect (line 44) | public function collect(): array {
    method translationKeys (line 86) | protected function translationKeys(): array {
    method parseTranslations (line 109) | protected function parseTranslations(): array {
    method completeDomains (line 176) | protected function completeDomains(array $domains): array {

FILE: src/Generator/Task/ValidationTask.php
  class ValidationTask (line 10) | class ValidationTask implements TaskInterface {
    method collect (line 40) | public function collect(): array {
    method getValidatorRequirePresence (line 59) | protected function getValidatorRequirePresence(): array {

FILE: src/Generator/TaskCollection.php
  class TaskCollection (line 37) | class TaskCollection {
    method __construct (line 81) | public function __construct(array $tasks = []) {
    method defaultTasks (line 97) | protected function defaultTasks(): array {
    method add (line 117) | protected function add($task) {
    method tasks (line 137) | public function tasks(): array {
    method getMap (line 144) | public function getMap(): array {

FILE: src/IdeHelperPlugin.php
  class IdeHelperPlugin (line 24) | class IdeHelperPlugin extends BasePlugin {
    method console (line 32) | public function console(CommandCollection $commands): CommandCollection {

FILE: src/Illuminator/Illuminator.php
  class Illuminator (line 7) | class Illuminator {
    method __construct (line 14) | public function __construct(TaskCollection $taskCollection) {
    method illuminate (line 23) | public function illuminate($path, $filter) {
    method shouldSkip (line 49) | protected function shouldSkip($fileName, $filter) {
    method getFiles (line 61) | protected function getFiles($path) {

FILE: src/Illuminator/Task/AbstractTask.php
  class AbstractTask (line 24) | abstract class AbstractTask {
    method __construct (line 62) | public function __construct(array $config) {
    method shouldRun (line 74) | abstract public function shouldRun(string $path): bool;
    method run (line 81) | abstract public function run(string $content, string $path): string;

FILE: src/Illuminator/Task/ControllerDefaultTableTask.php
  class ControllerDefaultTableTask (line 13) | class ControllerDefaultTableTask extends AbstractTask {
    method shouldRun (line 29) | public function shouldRun(string $path): bool {
    method run (line 51) | public function run(string $content, string $path): string {
    method hasDefaultTableProperty (line 86) | protected function hasDefaultTableProperty(File $file, int $classIndex...
    method addDefaultTableProperty (line 124) | protected function addDefaultTableProperty(File $file, int $classIndex...
    method detectIndentation (line 149) | protected function detectIndentation(File $file, int $index): string {
    method extractNamespace (line 172) | protected function extractNamespace(string $content): ?string {
    method detectPluginFromNamespace (line 184) | protected function detectPluginFromNamespace(string $namespace): string {

FILE: src/Illuminator/Task/EntityFieldTask.php
  class EntityFieldTask (line 17) | class EntityFieldTask extends AbstractTask {
    method shouldRun (line 43) | public function shouldRun(string $path): bool {
    method run (line 52) | public function run(string $content, string $path): string {
    method getFields (line 86) | protected function getFields(File $file, int $classIndex): array {
    method findDocBlockCloseTagIndex (line 138) | protected function findDocBlockCloseTagIndex(File $file, int $index): ...
    method addClassConstants (line 155) | protected function addClassConstants(File $file, array $fields, $index...
    method getFieldConstants (line 211) | protected function getFieldConstants(array $tokens, int $startIndex, i...
    method visibility (line 253) | protected function visibility(): bool {

FILE: src/Illuminator/Task/TableValidationLinkTask.php
  class TableValidationLinkTask (line 18) | class TableValidationLinkTask extends AbstractTask {
    method shouldRun (line 24) | public function shouldRun(string $path): bool {
    method run (line 35) | public function run(string $content, string $path): string {
    method findTableProviderMethods (line 50) | protected function findTableProviderMethods(string $content): array {
    method insertLinkAnnotations (line 193) | protected function insertLinkAnnotations(string $content, array $links...
    method hasLinkAnnotation (line 230) | protected function hasLinkAnnotation(string $line, string $methodName)...

FILE: src/Illuminator/TaskCollection.php
  class TaskCollection (line 17) | class TaskCollection {
    method __construct (line 51) | public function __construct(Io $io, array $config, array $tasks = []) {
    method defaultTasks (line 79) | protected function defaultTasks(): array {
    method add (line 99) | protected function add($task) {
    method tasks (line 119) | public function tasks(): array {
    method taskNames (line 128) | public function taskNames($tasks = []): array {
    method run (line 150) | public function run(string $path): bool {
    method displayDiff (line 187) | protected function displayDiff(string $oldContent, string $newContent)...
    method storeFile (line 237) | protected function storeFile(string $path, string $contents, bool $dry...

FILE: src/Utility/App.php
  class App (line 10) | class App extends CoreApp {
    method className (line 20) | public static function className(string $class, string $type = '', str...
    method classNameOrFail (line 62) | public static function classNameOrFail(string $class, string $type = '...

FILE: src/Utility/AppPath.php
  class AppPath (line 8) | class AppPath {
    method get (line 16) | public static function get(string $type, ?string $plugin = null): array {

FILE: src/Utility/CollectionClass.php
  class CollectionClass (line 8) | class CollectionClass {
    method name (line 15) | public static function name(string $name): string {

FILE: src/Utility/ControllerActionParser.php
  class ControllerActionParser (line 11) | class ControllerActionParser {
    method parse (line 23) | public function parse(string $path): array {
    method parseFile (line 50) | protected function parseFile($path): array {

FILE: src/Utility/GenericString.php
  class GenericString (line 8) | class GenericString {
    method generate (line 16) | public static function generate(string $value, ?string $type = null): ...

FILE: src/Utility/Plugin.php
  class Plugin (line 8) | class Plugin extends CorePlugin {
    method all (line 13) | public static function all(): array {

FILE: src/Utility/PluginPath.php
  class PluginPath (line 7) | class PluginPath {
    method get (line 14) | public static function get(string $plugin): string {
    method classPath (line 28) | public static function classPath(string $plugin): string {

FILE: src/Utility/TranslationParser.php
  class TranslationParser (line 7) | class TranslationParser {
    method __construct (line 11) | public function __construct() {
    method parse (line 20) | public function parse(string $path): array {
    method escapeSlashes (line 39) | protected function escapeSlashes(string $key): string {

FILE: src/ValueObject/ArgumentsSet.php
  class ArgumentsSet (line 12) | class ArgumentsSet implements ValueObjectInterface {
    method __construct (line 19) | private function __construct(string $value) {
    method create (line 30) | public static function create(string $value) {
    method raw (line 37) | public function raw(): string {
    method __toString (line 44) | public function __toString(): string {

FILE: src/ValueObject/ClassName.php
  class ClassName (line 8) | class ClassName implements ValueObjectInterface {
    method __construct (line 15) | private function __construct(string $className) {
    method create (line 25) | public static function create(string $className) {
    method raw (line 36) | public function raw(): string {
    method __toString (line 43) | public function __toString(): string {

FILE: src/ValueObject/KeyValue.php
  class KeyValue (line 8) | class KeyValue {
    method __construct (line 18) | private function __construct(ValueObjectInterface $key, ValueObjectInt...
    method create (line 31) | public static function create(ValueObjectInterface $key, ValueObjectIn...
    method key (line 38) | public function key(): ValueObjectInterface {
    method value (line 45) | public function value(): ValueObjectInterface {

FILE: src/ValueObject/LiteralName.php
  class LiteralName (line 8) | class LiteralName implements ValueObjectInterface {
    method __construct (line 15) | private function __construct(string $value) {
    method create (line 26) | public static function create(string $value) {
    method raw (line 33) | public function raw(): string {
    method __toString (line 40) | public function __toString(): string {

FILE: src/ValueObject/StringName.php
  class StringName (line 8) | class StringName implements ValueObjectInterface {
    method __construct (line 15) | private function __construct(string $value) {
    method create (line 26) | public static function create(string $value) {
    method raw (line 33) | public function raw(): string {
    method __toString (line 40) | public function __toString(): string {

FILE: src/ValueObject/ValueObjectInterface.php
  type ValueObjectInterface (line 5) | interface ValueObjectInterface {
    method create (line 14) | public static function create(string $value);
    method raw (line 21) | public function raw(): string;
    method __toString (line 28) | public function __toString(): string;

FILE: src/View/Helper/DocBlockHelper.php
  class DocBlockHelper (line 12) | class DocBlockHelper extends BakeDocBlockHelper {
    method buildEntityPropertyHintTypeMap (line 30) | public function buildEntityPropertyHintTypeMap(array $propertySchema):...
    method columnTypeNullable (line 50) | public function columnTypeNullable(array $info, ?string $type, ?string...
    method buildEntityAssociationHintTypeMap (line 80) | public function buildEntityAssociationHintTypeMap(array $propertySchem...
    method associatedEntityTypeToHintType (line 113) | public function associatedEntityTypeToHintType(string $type, Associati...
    method buildTableAnnotations (line 137) | public function buildTableAnnotations(
    method setVirtualFields (line 214) | public function setVirtualFields(array $virtualFields): void {
    method getVirtualFields (line 221) | public function getVirtualFields(): array {

FILE: tests/Fixture/BarBarsFixture.php
  class BarBarsFixture (line 7) | class BarBarsFixture extends TestFixture {

FILE: tests/Fixture/CarsFixture.php
  class CarsFixture (line 8) | class CarsFixture extends TestFixture {

FILE: tests/Fixture/FoosFixture.php
  class FoosFixture (line 7) | class FoosFixture extends TestFixture {

FILE: tests/Fixture/HousesFixture.php
  class HousesFixture (line 7) | class HousesFixture extends TestFixture {

FILE: tests/Fixture/WheelsFixture.php
  class WheelsFixture (line 7) | class WheelsFixture extends TestFixture {

FILE: tests/Fixture/WindowsFixture.php
  class WindowsFixture (line 7) | class WindowsFixture extends TestFixture {

FILE: tests/TestCase/Annotation/AnnotationFactoryTest.php
  class AnnotationFactoryTest (line 14) | class AnnotationFactoryTest extends TestCase {
    method testCreate (line 19) | public function testCreate() {
    method testCreateFromString (line 53) | public function testCreateFromString() {
    method testCreateFromStringWithSpaceInsideGenericType (line 126) | public function testCreateFromStringWithSpaceInsideGenericType() {

FILE: tests/TestCase/Annotation/ExtendsAnnotationTest.php
  class ExtendsAnnotationTest (line 10) | class ExtendsAnnotationTest extends TestCase {
    method testBuild (line 15) | public function testBuild() {
    method testReplaceWith (line 25) | public function testReplaceWith() {
    method testMatches (line 38) | public function testMatches() {
    method testMatchesWithDescription (line 58) | public function testMatchesWithDescription() {
    method testIndex (line 71) | public function testIndex() {
    method testIndexInvalidCall (line 81) | public function testIndexInvalidCall() {

FILE: tests/TestCase/Annotation/MethodAnnotationTest.php
  class MethodAnnotationTest (line 10) | class MethodAnnotationTest extends TestCase {
    method testBuild (line 15) | public function testBuild() {
    method testReplaceWith (line 25) | public function testReplaceWith() {
    method testMatches (line 38) | public function testMatches() {
    method testMatchesWithDescription (line 58) | public function testMatchesWithDescription() {
    method testIndex (line 71) | public function testIndex() {
    method testIndexInvalidCall (line 81) | public function testIndexInvalidCall() {

FILE: tests/TestCase/Annotation/MixinAnnotationTest.php
  class MixinAnnotationTest (line 10) | class MixinAnnotationTest extends TestCase {
    method testBuild (line 15) | public function testBuild() {
    method testReplaceWith (line 25) | public function testReplaceWith() {
    method testMatches (line 38) | public function testMatches() {
    method testMatchesWithDescription (line 58) | public function testMatchesWithDescription() {
    method testIndex (line 77) | public function testIndex() {
    method testIndexInvalidCall (line 87) | public function testIndexInvalidCall() {

FILE: tests/TestCase/Annotation/ParamAnnotationTest.php
  class ParamAnnotationTest (line 9) | class ParamAnnotationTest extends TestCase {
    method testBuild (line 14) | public function testBuild() {
    method testReplaceWith (line 24) | public function testReplaceWith() {
    method testMatches (line 37) | public function testMatches() {
    method testMatchesWithDescription (line 57) | public function testMatchesWithDescription() {

FILE: tests/TestCase/Annotation/PropertyAnnotationTest.php
  class PropertyAnnotationTest (line 9) | class PropertyAnnotationTest extends TestCase {
    method testBuild (line 14) | public function testBuild() {
    method testReplaceWith (line 24) | public function testReplaceWith() {
    method testMatches (line 37) | public function testMatches() {
    method testMatchesWithDescription (line 57) | public function testMatchesWithDescription() {
    method testMatchesWithoutVariableChar (line 70) | public function testMatchesWithoutVariableChar() {

FILE: tests/TestCase/Annotation/UsesAnnotationTest.php
  class UsesAnnotationTest (line 10) | class UsesAnnotationTest extends TestCase {
    method testBuild (line 15) | public function testBuild() {
    method testReplaceWith (line 25) | public function testReplaceWith() {
    method testMatches (line 38) | public function testMatches() {
    method testMatchesWithDescription (line 58) | public function testMatchesWithDescription() {
    method testIndex (line 77) | public function testIndex() {
    method testIndexInvalidCall (line 87) | public function testIndexInvalidCall() {

FILE: tests/TestCase/Annotation/VariableAnnotationTest.php
  class VariableAnnotationTest (line 9) | class VariableAnnotationTest extends TestCase {
    method testBuild (line 14) | public function testBuild() {
    method testReplaceWith (line 24) | public function testReplaceWith() {
    method testMatches (line 37) | public function testMatches() {
    method testMatchesWithDescription (line 57) | public function testMatchesWithDescription() {
    method testReplaceWithPreservesNullableForGuessed (line 70) | public function testReplaceWithPreservesNullableForGuessed() {
    method testReplaceWithPreservesNullForNonGuessed (line 88) | public function testReplaceWithPreservesNullForNonGuessed() {
    method testReplaceWithDoesNotDuplicateNull (line 105) | public function testReplaceWithDoesNotDuplicateNull() {

FILE: tests/TestCase/Annotator/CallbackAnnotatorTask/VirtualFieldCallbackAnnotatorTaskTest.php
  class VirtualFieldCallbackAnnotatorTaskTest (line 12) | class VirtualFieldCallbackAnnotatorTaskTest extends TestCase {
    method setUp (line 23) | protected function setUp(): void {
    method testShouldRun (line 35) | public function testShouldRun() {
    method testAnnotate (line 48) | public function testAnnotate() {
    method testAnnotateExisting (line 63) | public function testAnnotateExisting() {
    method getTask (line 87) | protected function getTask(string $path, string $content, array $param...

FILE: tests/TestCase/Annotator/CallbackAnnotatorTest.php
  class CallbackAnnotatorTest (line 13) | class CallbackAnnotatorTest extends TestCase {
    method setUp (line 24) | protected function setUp(): void {
    method tearDown (line 41) | protected function tearDown(): void {
    method testAnnotate (line 53) | public function testAnnotate() {
    method _getAnnotatorMock (line 83) | protected function _getAnnotatorMock(array $params) {

FILE: tests/TestCase/Annotator/ClassAnnotatorTask/FormClassAnnotatorTaskTest.php
  class FormClassAnnotatorTaskTest (line 12) | class FormClassAnnotatorTaskTest extends TestCase {
    method setUp (line 23) | protected function setUp(): void {
    method testShouldRun (line 35) | public function testShouldRun() {
    method testShouldRunNestedNamespace (line 54) | public function testShouldRunNestedNamespace() {
    method testAnnotate (line 72) | public function testAnnotate() {
    method testAnnotateExisting (line 90) | public function testAnnotateExisting() {
    method getTask (line 112) | protected function getTask(string $content, array $params = []) {

FILE: tests/TestCase/Annotator/ClassAnnotatorTask/MailerClassAnnotatorTaskTest.php
  class MailerClassAnnotatorTaskTest (line 12) | class MailerClassAnnotatorTaskTest extends TestCase {
    method setUp (line 23) | protected function setUp(): void {
    method testShouldRun (line 35) | public function testShouldRun() {
    method testShouldRunViaCall (line 49) | public function testShouldRunViaCall() {
    method testShouldRunViaCallPlugin (line 63) | public function testShouldRunViaCallPlugin() {
    method testAnnotate (line 77) | public function testAnnotate() {
    method testAnnotateSingleLine (line 95) | public function testAnnotateSingleLine() {
    method testAnnotateMultiLine (line 113) | public function testAnnotateMultiLine() {
    method testAnnotateExisting (line 131) | public function testAnnotateExisting() {
    method testShouldRunInvalid (line 150) | public function testShouldRunInvalid() {
    method getTask (line 165) | protected function getTask(string $content, array $params = []) {

FILE: tests/TestCase/Annotator/ClassAnnotatorTask/TableFindAnnotatorTaskTest.php
  class TableFindAnnotatorTaskTest (line 12) | class TableFindAnnotatorTaskTest extends TestCase {
    method setUp (line 23) | protected function setUp(): void {
    method testShouldRun (line 35) | public function testShouldRun(): void {
    method testAnnotate (line 54) | public function testAnnotate(): void {
    method testAnnotateNoMatches (line 77) | public function testAnnotateNoMatches(): void {
    method testAnnotateAlreadyAnnotated (line 98) | public function testAnnotateAlreadyAnnotated(): void {
    method getTask (line 121) | protected function getTask(string $content, array $params = []): Table...

FILE: tests/TestCase/Annotator/ClassAnnotatorTask/TestClassAnnotatorTaskTest.php
  class TestClassAnnotatorTaskTest (line 13) | class TestClassAnnotatorTaskTest extends TestCase {
    method setUp (line 24) | protected function setUp(): void {
    method testShouldRun (line 36) | public function testShouldRun() {
    method testAnnotate (line 57) | public function testAnnotate() {
    method testAnnotateExisting (line 75) | public function testAnnotateExisting() {
    method testAnnotatePreferUses (line 94) | public function testAnnotatePreferUses() {
    method testAnnotateSkipsWhenUsesClassAttribute (line 114) | public function testAnnotateSkipsWhenUsesClassAttribute() {
    method testAnnotateExistingLink (line 129) | public function testAnnotateExistingLink() {
    method getTask (line 151) | protected function getTask(string $content, array $params = []) {

FILE: tests/TestCase/Annotator/ClassAnnotatorTest.php
  class ClassAnnotatorTest (line 12) | class ClassAnnotatorTest extends TestCase {
    method setUp (line 23) | protected function setUp(): void {
    method tearDown (line 41) | protected function tearDown(): void {
    method testAnnotate (line 54) | public function testAnnotate() {
    method _getAnnotatorMock (line 81) | protected function _getAnnotatorMock(array $params) {

FILE: tests/TestCase/Annotator/CommandAnnotatorTest.php
  class CommandAnnotatorTest (line 12) | class CommandAnnotatorTest extends TestCase {
    method setUp (line 30) | protected function setUp(): void {
    method testAnnotate (line 42) | public function testAnnotate() {
    method _getAnnotatorMock (line 68) | protected function _getAnnotatorMock(array $params): CommandAnnotator {

FILE: tests/TestCase/Annotator/ComponentAnnotatorTest.php
  class ComponentAnnotatorTest (line 13) | class ComponentAnnotatorTest extends TestCase {
    method setUp (line 27) | protected function setUp(): void {
    method testAnnotate (line 39) | public function testAnnotate() {
    method testAnnotateWithExistingDocBlock (line 66) | public function testAnnotateWithExistingDocBlock() {
    method _getAnnotatorMock (line 92) | protected function _getAnnotatorMock(array $params): ComponentAnnotator {
    method testAnnotateWithControllerUsage (line 104) | public function testAnnotateWithControllerUsage() {

FILE: tests/TestCase/Annotator/ControllerAnnotatorTest.php
  class ControllerAnnotatorTest (line 12) | class ControllerAnnotatorTest extends TestCase {
    method setUp (line 30) | protected function setUp(): void {
    method testAnnotate (line 42) | public function testAnnotate() {
    method testAnnotateWithCustomModelAndLoadModel (line 67) | public function testAnnotateWithCustomModelAndLoadModel() {
    method testAnnotateWithAppController (line 92) | public function testAnnotateWithAppController() {
    method _getAnnotatorMock (line 118) | protected function _getAnnotatorMock(array $params): ControllerAnnotat...
    method testAnnotateWithPluginController (line 130) | public function testAnnotateWithPluginController() {
    method testAnnotateWithPluginControllerExplicit (line 158) | public function testAnnotateWithPluginControllerExplicit() {
    method testAnnotateWithPluginControllerNoModel (line 186) | public function testAnnotateWithPluginControllerNoModel() {
    method testAnnotateWithDynamicProperties (line 203) | public function testAnnotateWithDynamicProperties() {
    method testAnnotateWithDynamicPropertiesExistingDocblock (line 228) | public function testAnnotateWithDynamicPropertiesExistingDocblock() {

FILE: tests/TestCase/Annotator/DiffHelperTrait.php
  type DiffHelperTrait (line 8) | trait DiffHelperTrait {
    method _displayDiff (line 18) | protected function _displayDiff($expected, $actual) {

FILE: tests/TestCase/Annotator/EntityAnnotatorTest.php
  class EntityAnnotatorTest (line 19) | class EntityAnnotatorTest extends TestCase {
    method setUp (line 33) | protected function setUp(): void {
    method testBuildExtendedEntityPropertyHintTypeMap (line 120) | public function testBuildExtendedEntityPropertyHintTypeMap() {
    method testAnnotate (line 166) | public function testAnnotate() {
    method testAnnotateWithExistingDocBlock (line 196) | public function testAnnotateWithExistingDocBlock() {
    method testAnnotateWithExistingDocBlockComplex (line 226) | public function testAnnotateWithExistingDocBlockComplex() {
    method testAnnotateWithVirtualProperties (line 256) | public function testAnnotateWithVirtualProperties() {
    method testAnnotateWithVirtualPropertiesReadOnly (line 287) | public function testAnnotateWithVirtualPropertiesReadOnly() {
    method testAnnotateWithVirtualPropertiesAndReturnTypes (line 318) | public function testAnnotateWithVirtualPropertiesAndReturnTypes() {
    method testAnnotateHasOne (line 349) | public function testAnnotateHasOne() {
    method testAnnotateBelongsToRequired (line 379) | public function testAnnotateBelongsToRequired() {
    method testAnnotateBelongsToNullable (line 409) | public function testAnnotateBelongsToNullable() {
    method testAnnotateWithGenericUsage (line 439) | public function testAnnotateWithGenericUsage() {
    method testAnnotateWithDuplicates (line 475) | public function testAnnotateWithDuplicates() {
    method testAnnotateWithComplexType (line 506) | public function testAnnotateWithComplexType() {
    method _getAnnotatorMock (line 538) | protected function _getAnnotatorMock(array $params): EntityAnnotator {

FILE: tests/TestCase/Annotator/HelperAnnotatorTest.php
  class HelperAnnotatorTest (line 12) | class HelperAnnotatorTest extends TestCase {
    method setUp (line 25) | protected function setUp(): void {
    method testAnnotate (line 37) | public function testAnnotate() {
    method testAnnotatePreservesCustomMethodAnnotations (line 62) | public function testAnnotatePreservesCustomMethodAnnotations() {
    method _getAnnotatorMock (line 88) | protected function _getAnnotatorMock(array $params): HelperAnnotator {

FILE: tests/TestCase/Annotator/ModelAnnotatorSpecificTest.php
  class ModelAnnotatorSpecificTest (line 19) | class ModelAnnotatorSpecificTest extends TestCase {
    method setUp (line 41) | protected function setUp(): void {
    method tearDown (line 105) | public function tearDown(): void {
    method _getAnnotatorMock (line 118) | protected function _getAnnotatorMock(array $params): ModelAnnotator {
    method testAnnotateSpecific (line 131) | public function testAnnotateSpecific() {
    method testAnnotateSpecificDetailed (line 156) | public function testAnnotateSpecificDetailed() {
    method testAnnotateSpecificExistingMerge (line 183) | public function testAnnotateSpecificExistingMerge() {
    method testAnnotateSpecificExistingReplace (line 208) | public function testAnnotateSpecificExistingReplace() {
    method testAnnotateSpecificSkip (line 233) | public function testAnnotateSpecificSkip() {
    method testAnnotateSpecificSkipAll (line 257) | public function testAnnotateSpecificSkipAll() {
    method testAnnotateSpecificCatchExceptions (line 281) | public function testAnnotateSpecificCatchExceptions() {
    method testAnnotateSpecificProtectedParent (line 294) | public function testAnnotateSpecificProtectedParent() {

FILE: tests/TestCase/Annotator/ModelAnnotatorTest.php
  class ModelAnnotatorTest (line 16) | class ModelAnnotatorTest extends TestCase {
    method setUp (line 38) | protected function setUp(): void {
    method tearDown (line 108) | public function tearDown(): void {
    method _getAnnotatorMock (line 120) | protected function _getAnnotatorMock(array $params): ModelAnnotator {
    method _getEntityTemplateAnnotatorMock (line 134) | protected function _getEntityTemplateAnnotatorMock(array $params): Mod...
    method testAnnotate (line 153) | public function testAnnotate() {
    method testAnnotateExistingMerge (line 178) | public function testAnnotateExistingMerge() {
    method testAnnotateExistingReplace (line 203) | public function testAnnotateExistingReplace() {
    method testAnnotateSkip (line 228) | public function testAnnotateSkip() {
    method testAnnotateSkipAll (line 252) | public function testAnnotateSkipAll() {
    method testAnnotateCatchExceptions (line 276) | public function testAnnotateCatchExceptions() {
    method testAnnotateProtectedParent (line 289) | public function testAnnotateProtectedParent() {
    method testAnnotateDetailed (line 314) | public function testAnnotateDetailed() {
    method testAnnotateWithEntityFindQuery (line 346) | public function testAnnotateWithEntityFindQuery() {
    method testAnnotateWithEntityTemplate (line 378) | public function testAnnotateWithEntityTemplate() {

FILE: tests/TestCase/Annotator/Template/VariableExtractorTest.php
  class VariableExtractorTest (line 17) | class VariableExtractorTest extends TestCase {
    method setUp (line 26) | protected function setUp(): void {
    method testExtract (line 35) | public function testExtract() {
    method testExtractExceptions (line 82) | public function testExtractExceptions() {
    method testExtractAssignment (line 115) | public function testExtractAssignment() {
    method testExtractTypeStringAndArray (line 161) | public function testExtractTypeStringAndArray() {
    method testExtractFromStrings (line 199) | public function testExtractFromStrings(): void {
    method testExtractExcludesAnonymousFunctionParameters (line 216) | public function testExtractExcludesAnonymousFunctionParameters() {
    method testExtractFromCompact (line 263) | public function testExtractFromCompact(): void {

FILE: tests/TestCase/Annotator/TemplateAnnotatorTest.php
  class TemplateAnnotatorTest (line 17) | class TemplateAnnotatorTest extends TestCase {
    method setUp (line 31) | protected function setUp(): void {
    method tearDown (line 64) | protected function tearDown(): void {
    method testGetVariableAnnotations (line 73) | public function testGetVariableAnnotations() {
    method testNeedsViewAnnotation (line 96) | public function testNeedsViewAnnotation() {
    method testAnnotate (line 127) | public function testAnnotate() {
    method testAnnotateLoop (line 154) | public function testAnnotateLoop() {
    method testAnnotatePhpLine (line 181) | public function testAnnotatePhpLine() {
    method testAnnotateExistingBasic (line 208) | public function testAnnotateExistingBasic() {
    method testAnnotateExistingOutdated (line 235) | public function testAnnotateExistingOutdated() {
    method testAnnotateExistingStrict (line 262) | public function testAnnotateExistingStrict() {
    method testAnnotateEmptyPreemptive (line 289) | public function testAnnotateEmptyPreemptive() {
    method testAnnotateVars (line 316) | public function testAnnotateVars() {
    method testAnnotateEmpty (line 343) | public function testAnnotateEmpty() {
    method testAnnotateInline (line 365) | public function testAnnotateInline() {
    method testAnnotateWithFollowingInline (line 392) | public function testAnnotateWithFollowingInline() {
    method testAnnotateWithShapedArray (line 419) | public function testAnnotateWithShapedArray() {
    method testAnnotateWithMultilineArray (line 446) | public function testAnnotateWithMultilineArray() {
    method testAnnotateWithAnonymousFunctions (line 473) | public function testAnnotateWithAnonymousFunctions() {
    method testAnnotateWithStringInterpolation (line 522) | public function testAnnotateWithStringInterpolation() {
    method testAnnotatePreservesCustomViewAnnotation (line 551) | public function testAnnotatePreservesCustomViewAnnotation() {
    method _getAnnotatorMock (line 578) | protected function _getAnnotatorMock(array $params): TemplateAnnotator {

FILE: tests/TestCase/Annotator/ViewAnnotatorTest.php
  class ViewAnnotatorTest (line 13) | class ViewAnnotatorTest extends TestCase {
    method setUp (line 27) | protected function setUp(): void {
    method testAnnotate (line 40) | public function testAnnotate() {
    method _getAnnotatorMock (line 66) | protected function _getAnnotatorMock(array $params): ViewAnnotator {
    method testGetHelpers (line 78) | public function testGetHelpers(): void {

FILE: tests/TestCase/CodeCompletion/CodeCompletionGeneratorTest.php
  class CodeCompletionGeneratorTest (line 9) | class CodeCompletionGeneratorTest extends TestCase {
    method setUp (line 16) | protected function setUp(): void {
    method testCollect (line 36) | public function testCollect() {

FILE: tests/TestCase/CodeCompletion/Task/BehaviorTaskTest.php
  class BehaviorTaskTest (line 8) | class BehaviorTaskTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testCollect (line 25) | public function testCollect() {

FILE: tests/TestCase/CodeCompletion/Task/ControllerEventsTaskTest.php
  class ControllerEventsTaskTest (line 9) | class ControllerEventsTaskTest extends TestCase {
    method setUp (line 16) | protected function setUp(): void {
    method testCollectLegacy (line 25) | public function testCollectLegacy(): void {
    method testCollect (line 149) | public function testCollect() {

FILE: tests/TestCase/CodeCompletion/Task/ModelEventsTaskTest.php
  class ModelEventsTaskTest (line 8) | class ModelEventsTaskTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testCollect (line 24) | public function testCollect() {

FILE: tests/TestCase/CodeCompletion/Task/ViewEventsTaskTest.php
  class ViewEventsTaskTest (line 8) | class ViewEventsTaskTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testCollect (line 24) | public function testCollect() {

FILE: tests/TestCase/CodeCompletion/TaskCollectionTest.php
  class TaskCollectionTest (line 8) | class TaskCollectionTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testTasks (line 24) | public function testTasks() {

FILE: tests/TestCase/Command/Annotate/ClassesCommandPathAwareTest.php
  class ClassesCommandPathAwareTest (line 11) | class ClassesCommandPathAwareTest extends TestCase {
    method setUp (line 23) | protected function setUp(): void {
    method tearDown (line 32) | protected function tearDown(): void {
    method placePluginFixtureFile (line 48) | protected function placePluginFixtureFile(): string {
    method placeFixtureFile (line 66) | protected function placeFixtureFile(): string {
    method testPathAwareTaskDirectoryIsWalked (line 81) | public function testPathAwareTaskDirectoryIsWalked(): void {
    method testPathAwareTaskWalksPluginPathInPluginMode (line 101) | public function testPathAwareTaskWalksPluginPathInPluginMode(): void {
    method testDuplicateScanPathsAcrossTasksAreWalkedOnlyOnce (line 121) | public function testDuplicateScanPathsAcrossTasksAreWalkedOnlyOnce(): ...
    method testNonExistentPathAwareDirectoryIsSkippedSilently (line 148) | public function testNonExistentPathAwareDirectoryIsSkippedSilently(): ...

FILE: tests/TestCase/Command/Annotate/ClassesCommandTestCaseWalkTest.php
  class ClassesCommandTestCaseWalkTest (line 14) | class ClassesCommandTestCaseWalkTest extends TestCase {
    method setUp (line 31) | protected function setUp(): void {
    method tearDown (line 40) | protected function tearDown(): void {
    method placeControllerTestFile (line 58) | protected function placeControllerTestFile(): void {
    method testTestCaseDirectoryIsWalkedByDefault (line 82) | public function testTestCaseDirectoryIsWalkedByDefault(): void {

FILE: tests/TestCase/Command/Annotate/Fixture/SecondTestPathAwareAnnotatorTask.php
  class SecondTestPathAwareAnnotatorTask (line 15) | class SecondTestPathAwareAnnotatorTask extends AbstractClassAnnotatorTas...
    method scanPaths (line 20) | public static function scanPaths(): array {
    method shouldRun (line 29) | public function shouldRun(string $path, string $content): bool {
    method annotate (line 37) | public function annotate(string $path): bool {

FILE: tests/TestCase/Command/Annotate/Fixture/TestPathAwareAnnotatorTask.php
  class TestPathAwareAnnotatorTask (line 13) | class TestPathAwareAnnotatorTask extends AbstractClassAnnotatorTask impl...
    method scanPaths (line 18) | public static function scanPaths(): array {
    method shouldRun (line 27) | public function shouldRun(string $path, string $content): bool {
    method annotate (line 35) | public function annotate(string $path): bool {

FILE: tests/TestCase/Command/AnnotateCommandTest.php
  class AnnotateCommandTest (line 11) | class AnnotateCommandTest extends TestCase {
    method setUp (line 25) | protected function setUp(): void {
    method tearDown (line 39) | public function tearDown(): void {
    method testModels (line 48) | public function testModels(): void {
    method testViews (line 57) | public function testViews(): void {
    method testHelpers (line 66) | public function testHelpers(): void {
    method testTemplates (line 75) | public function testTemplates(): void {
    method testTemplatesSkipFiles (line 84) | public function testTemplatesSkipFiles(): void {
    method testControllers (line 94) | public function testControllers() {
    method testCommands (line 105) | public function testCommands(): void {
    method testClasses (line 114) | public function testClasses(): void {
    method testCallbacks (line 123) | public function testCallbacks(): void {
    method testAll (line 131) | public function testAll() {
    method testAllCiModeNoChanges (line 147) | public function testAllCiModeNoChanges() {
    method testAllCiModeChanges (line 155) | public function testAllCiModeChanges() {
    method provideSubcommandsForCiModeTest (line 164) | public static function provideSubcommandsForCiModeTest() {
    method testIndividualSubcommandCiModeNoChanges (line 180) | #[DataProvider('provideSubcommandsForCiModeTest')]
    method testIndividualSubcommandCiModeChanges (line 193) | #[DataProvider('provideSubcommandsForCiModeTest')]

FILE: tests/TestCase/Command/GenerateCodeCompletionCommandTest.php
  class GenerateCodeCompletionCommandTest (line 8) | class GenerateCodeCompletionCommandTest extends TestCase {
    method setUp (line 22) | protected function setUp(): void {
    method tearDown (line 36) | protected function tearDown(): void {
    method testGenerate (line 49) | public function testGenerate() {

FILE: tests/TestCase/Command/GeneratePhpstormMetaCommandTest.php
  class GeneratePhpstormMetaCommandTest (line 12) | class GeneratePhpstormMetaCommandTest extends TestCase {
    method setUp (line 44) | protected function setUp(): void {
    method tearDown (line 62) | protected function tearDown(): void {
    method testDirExists (line 70) | public function testDirExists() {
    method testDirExistsDryRun (line 80) | public function testDirExistsDryRun() {
    method testGenerateDryRun (line 91) | public function testGenerateDryRun() {
    method testGenerate (line 101) | public function testGenerate() {

FILE: tests/TestCase/Command/IlluminateCommandTest.php
  class IlluminateCommandTest (line 8) | class IlluminateCommandTest extends TestCase {
    method testIlluminateDryRun (line 15) | public function testIlluminateDryRun() {

FILE: tests/TestCase/Console/IoTest.php
  class IoTest (line 11) | class IoTest extends TestCase {
    method setUp (line 24) | protected function setUp(): void {
    method tearDown (line 40) | protected function tearDown(): void {
    method testSuccess (line 48) | public function testSuccess() {
    method testInfo (line 58) | public function testInfo() {
    method testComment (line 68) | public function testComment() {
    method testWarn (line 78) | public function testWarn() {
    method testError (line 88) | public function testError() {
    method testErr (line 98) | public function testErr() {
    method testVerbose (line 108) | public function testVerbose() {
    method testQuiet (line 119) | public function testQuiet() {
    method testNl (line 130) | public function testNl() {
    method testHr (line 139) | public function testHr() {
    method testAbort (line 149) | public function testAbort() {

FILE: tests/TestCase/Generator/Directive/ExitPointTest.php
  class ExitPointTest (line 9) | class ExitPointTest extends TestCase {
    method testBuild (line 14) | public function testBuild() {

FILE: tests/TestCase/Generator/Directive/ExpectedArgumentsTest.php
  class ExpectedArgumentsTest (line 9) | class ExpectedArgumentsTest extends TestCase {
    method testBuild (line 14) | public function testBuild() {

FILE: tests/TestCase/Generator/Directive/ExpectedReturnValuesTest.php
  class ExpectedReturnValuesTest (line 9) | class ExpectedReturnValuesTest extends TestCase {
    method testBuild (line 14) | public function testBuild() {

FILE: tests/TestCase/Generator/Directive/OverrideTest.php
  class OverrideTest (line 11) | class OverrideTest extends TestCase {
    method testBuild (line 16) | public function testBuild() {
    method testBuildLiteralKey (line 40) | public function testBuildLiteralKey() {

FILE: tests/TestCase/Generator/Directive/RegisterArgumentsSetTest.php
  class RegisterArgumentsSetTest (line 10) | class RegisterArgumentsSetTest extends TestCase {
    method testBuild (line 15) | public function testBuild() {
    method testToString (line 37) | public function testToString() {
    method testSetInsideArguments (line 50) | public function testSetInsideArguments() {
    method testArgumentsSetInsideReturnValues (line 76) | public function testArgumentsSetInsideReturnValues() {

FILE: tests/TestCase/Generator/PhpstormGeneratorTest.php
  class PhpstormGeneratorTest (line 15) | class PhpstormGeneratorTest extends TestCase {
    method setUp (line 30) | protected function setUp(): void {
    method tearDown (line 49) | protected function tearDown(): void {
    method testCollect (line 61) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/BehaviorTaskTest.php
  class BehaviorTaskTest (line 8) | class BehaviorTaskTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testCollect (line 25) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/CacheTaskTest.php
  class CacheTaskTest (line 9) | class CacheTaskTest extends TestCase {
    method setUp (line 16) | protected function setUp(): void {
    method testCollect (line 25) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/CellTaskTest.php
  class CellTaskTest (line 8) | class CellTaskTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testCollect (line 24) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/ComponentTaskTest.php
  class ComponentTaskTest (line 8) | class ComponentTaskTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testCollect (line 24) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/ConfigureTaskTest.php
  class ConfigureTaskTest (line 10) | class ConfigureTaskTest extends TestCase {
    method setUp (line 19) | protected function setUp(): void {
    method testCollect (line 28) | public function testCollect() {
    method testCollectKeys (line 56) | public function testCollectKeys(): void {

FILE: tests/TestCase/Generator/Task/ConnectionTaskTest.php
  class ConnectionTaskTest (line 8) | class ConnectionTaskTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testCollect (line 24) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/ConsoleHelperTaskTest.php
  class ConsoleHelperTaskTest (line 8) | class ConsoleHelperTaskTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testCollect (line 24) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/ConsoleTaskTest.php
  class ConsoleTaskTest (line 8) | class ConsoleTaskTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testCollect (line 24) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/DatabaseTableColumnNameTaskTest.php
  class DatabaseTableColumnNameTaskTest (line 8) | class DatabaseTableColumnNameTaskTest extends TestCase {
    method setUp (line 23) | protected function setUp(): void {
    method tearDown (line 35) | protected function tearDown(): void {
    method testCollect (line 44) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/DatabaseTableColumnTypeTaskTest.php
  class DatabaseTableColumnTypeTaskTest (line 14) | class DatabaseTableColumnTypeTaskTest extends TestCase {
    method setUp (line 29) | protected function setUp(): void {
    method testCollect (line 41) | public function testCollect() {
    method testCollectPluginLoaded (line 100) | public function testCollectPluginLoaded() {

FILE: tests/TestCase/Generator/Task/DatabaseTableTaskTest.php
  class DatabaseTableTaskTest (line 9) | class DatabaseTableTaskTest extends TestCase {
    method setUp (line 24) | protected function setUp(): void {
    method tearDown (line 36) | protected function tearDown(): void {
    method testCollect (line 45) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/DatabaseTypeTaskTest.php
  class DatabaseTypeTaskTest (line 10) | class DatabaseTypeTaskTest extends TestCase {
    method setUp (line 17) | protected function setUp(): void {
    method testCollect (line 26) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/ElementTaskTest.php
  class ElementTaskTest (line 9) | class ElementTaskTest extends TestCase {
    method setUp (line 18) | protected function setUp(): void {
    method testCollect (line 28) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/EntityTaskTest.php
  class EntityTaskTest (line 10) | class EntityTaskTest extends TestCase {
    method setUp (line 25) | protected function setUp(): void {
    method testCollect (line 35) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/EnvTaskTest.php
  class EnvTaskTest (line 8) | class EnvTaskTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testCollect (line 24) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/FixtureTaskTest.php
  class FixtureTaskTest (line 8) | class FixtureTaskTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testCollect (line 25) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/FormHelperTaskTest.php
  class FormHelperTaskTest (line 9) | class FormHelperTaskTest extends TestCase {
    method setUp (line 24) | protected function setUp(): void {
    method tearDown (line 37) | protected function tearDown(): void {
    method testCollect (line 46) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/HelperTaskTest.php
  class HelperTaskTest (line 8) | class HelperTaskTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testCollect (line 25) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/LayoutTaskTest.php
  class LayoutTaskTest (line 9) | class LayoutTaskTest extends TestCase {
    method setUp (line 18) | protected function setUp(): void {
    method testCollect (line 27) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/MailerTaskTest.php
  class MailerTaskTest (line 8) | class MailerTaskTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testCollect (line 24) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/ModelTaskTest.php
  class ModelTaskTest (line 8) | class ModelTaskTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testCollect (line 25) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/PluginTaskTest.php
  class PluginTaskTest (line 9) | class PluginTaskTest extends TestCase {
    method setUp (line 16) | protected function setUp(): void {
    method testCollect (line 26) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/RequestTaskTest.php
  class RequestTaskTest (line 8) | class RequestTaskTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testCollect (line 24) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/RoutePathTaskTest.php
  class RoutePathTaskTest (line 8) | class RoutePathTaskTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testCollect (line 25) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/TableAssociationTaskTest.php
  class TableAssociationTaskTest (line 8) | class TableAssociationTaskTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testCollect (line 25) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/TableFinderTaskTest.php
  class TableFinderTaskTest (line 11) | class TableFinderTaskTest extends TestCase {
    method setUp (line 20) | protected function setUp(): void {
    method tearDown (line 31) | protected function tearDown(): void {
    method testCollect (line 40) | public function testCollect() {
    method testAddMethod (line 69) | public function testAddMethod() {
    method testAddMethodInvalid (line 83) | public function testAddMethodInvalid() {

FILE: tests/TestCase/Generator/Task/TranslationKeyTaskTest.php
  class TranslationKeyTaskTest (line 9) | class TranslationKeyTaskTest extends TestCase {
    method setUp (line 16) | protected function setUp(): void {
    method testCollect (line 30) | public function testCollect() {

FILE: tests/TestCase/Generator/Task/ValidationTaskTest.php
  class ValidationTaskTest (line 9) | class ValidationTaskTest extends TestCase {
    method setUp (line 16) | protected function setUp(): void {
    method testCollect (line 25) | public function testCollect() {

FILE: tests/TestCase/Generator/TaskCollectionTest.php
  class TaskCollectionTest (line 8) | class TaskCollectionTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testTasks (line 24) | public function testTasks() {

FILE: tests/TestCase/Illuminator/IlluminatorTest.php
  class IlluminatorTest (line 12) | class IlluminatorTest extends TestCase {
    method setUp (line 25) | protected function setUp(): void {
    method testIlluminate (line 42) | public function testIlluminate() {

FILE: tests/TestCase/Illuminator/Task/ControllerDefaultTableTaskTest.php
  class ControllerDefaultTableTaskTest (line 12) | class ControllerDefaultTableTaskTest extends TestCase {
    method setUp (line 23) | protected function setUp(): void {
    method testShouldRun (line 35) | public function testShouldRun() {
    method testShouldRunWithDifferentPathSeparators (line 51) | public function testShouldRunWithDifferentPathSeparators() {
    method testRunWithExistingTable (line 70) | public function testRunWithExistingTable() {
    method testRunWithoutTable (line 86) | public function testRunWithoutTable() {
    method testRunWithExistingProperty (line 123) | public function testRunWithExistingProperty() {
    method testRunWithOtherProperties (line 150) | public function testRunWithOtherProperties() {
    method testRunWithPluginController (line 197) | public function testRunWithPluginController() {
    method testRunWithNestedPluginNamespace (line 233) | public function testRunWithNestedPluginNamespace() {
    method testRunWithFastPropertyCheck (line 269) | public function testRunWithFastPropertyCheck() {
    method testRunWithPropertyInCommentDefaultCheck (line 297) | public function testRunWithPropertyInCommentDefaultCheck() {
    method testRunWithPropertyInCommentFastCheck (line 342) | public function testRunWithPropertyInCommentFastCheck() {
    method _getTask (line 374) | protected function _getTask(array $params = []) {

FILE: tests/TestCase/Illuminator/Task/EntityFieldTaskTest.php
  class EntityFieldTaskTest (line 12) | class EntityFieldTaskTest extends TestCase {
    method setUp (line 23) | protected function setUp(): void {
    method testShouldRun (line 35) | public function testShouldRun() {
    method testIlluminate (line 48) | public function testIlluminate() {
    method testIlluminateComplex (line 66) | public function testIlluminateComplex() {
    method testIlluminateComplex2 (line 83) | public function testIlluminateComplex2() {
    method testIlluminateExisting (line 100) | public function testIlluminateExisting() {
    method testIlluminateExistingPartial (line 116) | public function testIlluminateExistingPartial() {
    method testIlluminateVisibility (line 132) | public function testIlluminateVisibility() {
    method _getTask (line 147) | protected function _getTask(array $params = []) {

FILE: tests/TestCase/Illuminator/Task/TableValidationLinkTaskTest.php
  class TableValidationLinkTaskTest (line 9) | class TableValidationLinkTaskTest extends TestCase {
    method testShouldRun (line 14) | public function testShouldRun(): void {
    method testRun (line 30) | public function testRun(): void {
    method testRunNoChanges (line 51) | public function testRunNoChanges(): void {
    method testRunNoTableProvider (line 68) | public function testRunNoTableProvider(): void {
    method getTask (line 94) | protected function getTask(array $params = []): TableValidationLinkTask {

FILE: tests/TestCase/Utility/AppPathTest.php
  class AppPathTest (line 8) | class AppPathTest extends TestCase {
    method testGet (line 13) | public function testGet() {

FILE: tests/TestCase/Utility/AppTest.php
  class AppTest (line 9) | class AppTest extends TestCase {
    method testClassName (line 14) | public function testClassName() {

FILE: tests/TestCase/Utility/GenericStringTest.php
  class GenericStringTest (line 9) | class GenericStringTest extends TestCase {
    method testClassName (line 14) | public function testClassName() {
    method testClassNameObject (line 30) | public function testClassNameObject() {

FILE: tests/TestCase/Utility/PluginPathTest.php
  class PluginPathTest (line 9) | class PluginPathTest extends TestCase {
    method testGet (line 14) | public function testGet() {

FILE: tests/TestCase/Utility/PluginTest.php
  class PluginTest (line 9) | class PluginTest extends TestCase {
    method setUp (line 14) | protected function setUp(): void {
    method tearDown (line 24) | protected function tearDown(): void {
    method testAll (line 33) | public function testAll() {

FILE: tests/TestCase/Utility/TranslationParserTest.php
  class TranslationParserTest (line 8) | class TranslationParserTest extends TestCase {
    method setUp (line 15) | protected function setUp(): void {
    method testParse (line 24) | public function testParse() {

FILE: tests/TestCase/ValueObject/ClassNameTest.php
  class ClassNameTest (line 8) | class ClassNameTest extends TestCase {
    method testCreate (line 13) | public function testCreate(): void {
    method testToString (line 25) | public function testToString(): void {

FILE: tests/TestCase/ValueObject/DoubleQuoteStringNameTest.php
  class DoubleQuoteStringNameTest (line 8) | class DoubleQuoteStringNameTest extends TestCase {
    method testCreate (line 13) | public function testCreate(): void {
    method testToString (line 22) | public function testToString(): void {

FILE: tests/TestCase/ValueObject/KeyValueTest.php
  class KeyValueTest (line 9) | class KeyValueTest extends TestCase {
    method testCreate (line 14) | public function testCreate(): void {

FILE: tests/TestCase/ValueObject/LiteralNameTest.php
  class LiteralNameTest (line 8) | class LiteralNameTest extends TestCase {
    method testCreate (line 13) | public function testCreate(): void {
    method testToString (line 22) | public function testToString(): void {

FILE: tests/TestCase/ValueObject/StringNameTest.php
  class StringNameTest (line 8) | class StringNameTest extends TestCase {
    method testCreate (line 13) | public function testCreate(): void {
    method testToString (line 22) | public function testToString(): void {

FILE: tests/TestCase/View/Helper/DocBlockHelperTest.php
  class DocBlockHelperTest (line 16) | #[CoversClass(DocBlockHelper::class)]
    method setUp (line 29) | public function setUp(): void {
    method tearDown (line 43) | public function tearDown(): void {
    method testBuildTableAnnotations (line 53) | public function testBuildTableAnnotations(): void {

FILE: tests/test_app/plugins/Awesome/src/AwesomePlugin.php
  class AwesomePlugin (line 6) | class AwesomePlugin extends BasePlugin {

FILE: tests/test_app/plugins/Awesome/src/Controller/Admin/AwesomeHousesController.php
  class AwesomeHousesController (line 6) | class AwesomeHousesController extends Controller {
    method openDoor (line 11) | public function openDoor() {

FILE: tests/test_app/plugins/Awesome/src/Model/Table/HousesTable.php
  class HousesTable (line 9) | class HousesTable extends Table {
    method initialize (line 15) | public function initialize(array $config): void {

FILE: tests/test_app/plugins/Awesome/src/Model/Table/WindowsTable.php
  class WindowsTable (line 9) | class WindowsTable extends Table {
    method initialize (line 15) | public function initialize(array $config): void {

FILE: tests/test_app/plugins/Controllers/src/Controller/GenericController.php
  class GenericController (line 6) | #[\AllowDynamicProperties]

FILE: tests/test_app/plugins/Controllers/src/Controller/HousesController.php
  class HousesController (line 6) | class HousesController extends Controller {

FILE: tests/test_app/plugins/Controllers/src/Controller/WindowsController.php
  class WindowsController (line 6) | class WindowsController extends Controller {

FILE: tests/test_app/plugins/Controllers/src/ControllersPlugin.php
  class ControllersPlugin (line 6) | class ControllersPlugin extends BasePlugin {

FILE: tests/test_app/plugins/Controllers/src/Model/Table/HousesTable.php
  class HousesTable (line 6) | class HousesTable extends Table {

FILE: tests/test_app/plugins/MyNamespace/MyPlugin/src/Controller/Component/MyComponent.php
  class MyComponent (line 6) | class MyComponent extends Component {

FILE: tests/test_app/plugins/MyNamespace/MyPlugin/src/Model/Behavior/MyBehavior.php
  class MyBehavior (line 6) | class MyBehavior extends Behavior {

FILE: tests/test_app/plugins/MyNamespace/MyPlugin/src/Model/Table/MyTable.php
  class MyTable (line 7) | class MyTable extends Table {

FILE: tests/test_app/plugins/MyNamespace/MyPlugin/src/MyPluginPlugin.php
  class MyPluginPlugin (line 6) | class MyPluginPlugin extends BasePlugin {

FILE: tests/test_app/plugins/MyNamespace/MyPlugin/tests/Fixture/Sub/MyFixture.php
  class MyFixture (line 7) | class MyFixture extends TestFixture {

FILE: tests/test_app/plugins/Relations/src/Model/Entity/Bar.php
  class Bar (line 6) | class Bar extends Entity {

FILE: tests/test_app/plugins/Relations/src/Model/Entity/Foo.php
  class Foo (line 6) | class Foo extends Entity {

FILE: tests/test_app/plugins/Relations/src/Model/Entity/User.php
  class User (line 6) | class User extends Entity {

FILE: tests/test_app/plugins/Relations/src/Model/Table/BarsTable.php
  class BarsTable (line 8) | class BarsTable extends Table {
    method initialize (line 20) | public function initialize(array $config): void {
    method getSchema (line 30) | public function getSchema(): TableSchemaInterface {

FILE: tests/test_app/plugins/Relations/src/Model/Table/FoosTable.php
  class FoosTable (line 8) | class FoosTable extends Table {
    method initialize (line 21) | public function initialize(array $config): void {
    method getSchema (line 31) | public function getSchema(): TableSchemaInterface {

FILE: tests/test_app/plugins/Relations/src/Model/Table/UsersTable.php
  class UsersTable (line 8) | class UsersTable extends Table {
    method initialize (line 19) | public function initialize(array $config): void {
    method getSchema (line 29) | public function getSchema(): TableSchemaInterface {

FILE: tests/test_app/plugins/Relations/src/RelationsPlugin.php
  class RelationsPlugin (line 6) | class RelationsPlugin extends BasePlugin {

FILE: tests/test_app/src/Application.php
  class Application (line 9) | class Application extends BaseApplication
    method bootstrap (line 11) | public function bootstrap(): void
    method routes (line 15) | public function routes(RouteBuilder $routes): void
    method middleware (line 19) | public function middleware(MiddlewareQueue $middlewareQueue): Middlewa...

FILE: tests/test_app/src/Command/MyCommand.php
  class MyCommand (line 6) | class MyCommand extends Command {
    method main (line 16) | public function main() {

FILE: tests/test_app/src/Controller/AppController.php
  class AppController (line 9) | class AppController extends Controller {
    method initialize (line 14) | public function initialize(): void {

FILE: tests/test_app/src/Controller/BarController.php
  class BarController (line 6) | class BarController extends AppController {
    method initialize (line 15) | public function initialize(): void {
    method index (line 27) | public function index() {

FILE: tests/test_app/src/Controller/Component/CheckHttpCacheComponent.php
  class CheckHttpCacheComponent (line 6) | class CheckHttpCacheComponent extends CoreCheckHttpCacheComponent {

FILE: tests/test_app/src/Controller/Component/MyComponent.php
  class MyComponent (line 6) | class MyComponent extends Component {

FILE: tests/test_app/src/Controller/Component/MyControllerComponent.php
  class MyControllerComponent (line 7) | class MyControllerComponent extends Component {
    method beforeFilter (line 14) | public function beforeFilter(EventInterface $event): void {

FILE: tests/test_app/src/Controller/Component/MyOtherComponent.php
  class MyOtherComponent (line 10) | class MyOtherComponent extends Component {

FILE: tests/test_app/src/Controller/DynamicPropertiesController.php
  class DynamicPropertiesController (line 4) | #[\AllowDynamicProperties]

FILE: tests/test_app/src/Controller/DynamicPropertiesExistingDocblockController.php
  class DynamicPropertiesExistingDocblockController (line 7) | #[\AllowDynamicProperties]

FILE: tests/test_app/src/Controller/FoosController.php
  class FoosController (line 4) | class FoosController extends AppController {

FILE: tests/test_app/src/Custom/CustomClass.php
  class CustomClass (line 9) | class CustomClass {
    method initialize (line 16) | public function initialize() {

FILE: tests/test_app/src/Custom/Nested/NestedClass.php
  class NestedClass (line 6) | class NestedClass {
    method initialize (line 13) | public function initialize() {

FILE: tests/test_app/src/Database/Type/UuidType.php
  class UuidType (line 22) | class UuidType extends CoreUuidType {

FILE: tests/test_app/src/Generator/Task/TestDatabaseTableColumnTypeTask.php
  class TestDatabaseTableColumnTypeTask (line 7) | class TestDatabaseTableColumnTypeTask extends DatabaseTableColumnTypeTask
    method getAdapter (line 14) | protected function getAdapter(string $name = 'test') {

FILE: tests/test_app/src/Generator/Task/TestEnvTask.php
  class TestEnvTask (line 9) | class TestEnvTask extends EnvTask {
    method envKeys (line 14) | protected function envKeys(): array {

FILE: tests/test_app/src/Generator/Task/TestFixtureTask.php
  class TestFixtureTask (line 7) | class TestFixtureTask extends FixtureTask {
    method getFixtures (line 12) | protected function getFixtures(): array {

FILE: tests/test_app/src/Mailer/UserMailer.php
  class UserMailer (line 7) | class UserMailer extends Mailer {

FILE: tests/test_app/src/Model/Entity/BarBar.php
  class BarBar (line 6) | class BarBar extends Entity {

FILE: tests/test_app/src/Model/Entity/BarBarsAbstract.php
  class BarBarsAbstract (line 6) | class BarBarsAbstract extends Entity {

FILE: tests/test_app/src/Model/Entity/Callback.php
  class Callback (line 6) | class Callback extends Entity {

FILE: tests/test_app/src/Model/Entity/Car.php
  class Car (line 9) | class Car extends Entity {

FILE: tests/test_app/src/Model/Entity/Complex/Wheel.php
  class Wheel (line 18) | class Wheel extends Entity {
    method _getVirtualOne (line 27) | protected function _getVirtualOne() {

FILE: tests/test_app/src/Model/Entity/Complex2/Wheel.php
  class Wheel (line 18) | class Wheel extends Entity {
    method _getVirtualOne (line 27) | protected function _getVirtualOne() {

FILE: tests/test_app/src/Model/Entity/Foo.php
  class Foo (line 11) | class Foo extends Entity {

FILE: tests/test_app/src/Model/Entity/PHP/ComplexType.php
  class ComplexType (line 15) | class ComplexType extends Entity {

FILE: tests/test_app/src/Model/Entity/PHP/Duplicates.php
  class Duplicates (line 16) | class Duplicates extends Entity {

FILE: tests/test_app/src/Model/Entity/PHP/Generics.php
  class Generics (line 15) | class Generics extends Entity {

FILE: tests/test_app/src/Model/Entity/PHP7/Virtual.php
  class Virtual (line 6) | class Virtual extends Entity {
    method _getVirtualOne (line 8) | protected function _getVirtualOne(): ?string {
    method _getVirtualTwo (line 12) | protected function _getVirtualTwo(): string {
    method _getNestedGeneric (line 19) | protected function _getNestedGeneric(): array {

FILE: tests/test_app/src/Model/Entity/Virtual.php
  class Virtual (line 9) | class Virtual extends Entity {
    method _getVirtualOne (line 18) | protected function _getVirtualOne() {
    method _getVirtualTwo (line 22) | protected function _getVirtualTwo() {
    method _getVirtualReadOnly (line 30) | protected function _getVirtualReadOnly() {
    method _getWheels (line 39) | protected function _getWheels($wheels = []) {

FILE: tests/test_app/src/Model/Entity/Wheel.php
  class Wheel (line 6) | class Wheel extends Entity {
    method _getVirtualOne (line 15) | protected function _getVirtualOne() {

FILE: tests/test_app/src/Model/Enum/CarStatus.php
  method label (line 11) | public function label(): string {

FILE: tests/test_app/src/Model/Table/AbstractTable.php
  class AbstractTable (line 10) | abstract class AbstractTable extends Table
    method initialize (line 16) | public function initialize(array $config): void

FILE: tests/test_app/src/Model/Table/BarBarsAbstractTable.php
  class BarBarsAbstractTable (line 4) | class BarBarsAbstractTable extends AbstractTable {
    method initialize (line 10) | public function initialize(array $config): void {

FILE: tests/test_app/src/Model/Table/BarBarsTable.php
  class BarBarsTable (line 6) | class BarBarsTable extends Table {
    method initialize (line 12) | public function initialize(array $config): void {

FILE: tests/test_app/src/Model/Table/CallbacksTable.php
  class CallbacksTable (line 10) | class CallbacksTable extends Table {
    method beforeSave (line 18) | public function beforeSave(Event $event, EntityInterface $entity, Arra...
    method afterSave (line 27) | public function afterSave(EventInterface $event, EntityInterface $enti...
    method beforeDelete (line 30) | public function beforeDelete(Event $event, EntityInterface $entity, Ar...

FILE: tests/test_app/src/Model/Table/CarsTable.php
  class CarsTable (line 8) | class CarsTable extends Table {
    method initialize (line 14) | public function initialize(array $config): void {

FILE: tests/test_app/src/Model/Table/CustomFinderTable.php
  class CustomFinderTable (line 7) | class CustomFinderTable extends Table {
    method findSomethingCustom (line 14) | public function findSomethingCustom(SelectQuery $query): SelectQuery {

FILE: tests/test_app/src/Model/Table/ExceptionsTable.php
  class ExceptionsTable (line 6) | class ExceptionsTable extends Table {
    method initialize (line 12) | public function initialize(array $config): void {

FILE: tests/test_app/src/Model/Table/FoosTable.php
  class FoosTable (line 6) | class FoosTable extends Table {
    method initialize (line 12) | public function initialize(array $config): void {

FILE: tests/test_app/src/Model/Table/SkipMeTable.php
  class SkipMeTable (line 9) | class SkipMeTable extends Table {
    method initialize (line 15) | public function initialize(array $config): void {

FILE: tests/test_app/src/Model/Table/SkipSomeTable.php
  class SkipSomeTable (line 10) | class SkipSomeTable extends Table {
    method initialize (line 16) | public function initialize(array $config): void {
    method foo (line 25) | public function foo() {

FILE: tests/test_app/src/Model/Table/Specific/AbstractTable.php
  class AbstractTable (line 10) | abstract class AbstractTable extends Table
    method initialize (line 16) | public function initialize(array $config): void

FILE: tests/test_app/src/Model/Table/Specific/BarBarsAbstractTable.php
  class BarBarsAbstractTable (line 4) | class BarBarsAbstractTable extends AbstractTable {
    method initialize (line 10) | public function initialize(array $config): void {

FILE: tests/test_app/src/Model/Table/Specific/BarBarsTable.php
  class BarBarsTable (line 6) | class BarBarsTable extends Table {
    method initialize (line 12) | public function initialize(array $config): void {

FILE: tests/test_app/src/Model/Table/Specific/CallbacksTable.php
  class CallbacksTable (line 10) | class CallbacksTable extends Table {
    method beforeSave (line 18) | public function beforeSave(Event $event, EntityInterface $entity, Arra...
    method afterSave (line 27) | public function afterSave(EventInterface $event, EntityInterface $enti...
    method beforeDelete (line 30) | public function beforeDelete(Event $event, EntityInterface $entity, Ar...

FILE: tests/test_app/src/Model/Table/Specific/CarsTable.php
  class CarsTable (line 8) | class CarsTable extends Table {
    method initialize (line 14) | public function initialize(array $config): void {

FILE: tests/test_app/src/Model/Table/Specific/CustomFinderTable.php
  class CustomFinderTable (line 7) | class CustomFinderTable extends Table {
    method findSomethingCustom (line 14) | public function findSomethingCustom(SelectQuery $query): SelectQuery {

FILE: tests/test_app/src/Model/Table/Specific/ExceptionsTable.php
  class ExceptionsTable (line 6) | class ExceptionsTable extends Table {
    method initialize (line 12) | public function initialize(array $config): void {

FILE: tests/test_app/src/Model/Table/Specific/FoosTable.php
  class FoosTable (line 6) | class FoosTable extends Table {
    method initialize (line 12) | public function initialize(array $config): void {

FILE: tests/test_app/src/Model/Table/Specific/SkipMeTable.php
  class SkipMeTable (line 9) | class SkipMeTable extends Table {
    method initialize (line 15) | public function initialize(array $config): void {

FILE: tests/test_app/src/Model/Table/Specific/SkipSomeTable.php
  class SkipSomeTable (line 10) | class SkipSomeTable extends Table {
    method initialize (line 16) | public function initialize(array $config): void {
    method foo (line 25) | public function foo() {

FILE: tests/test_app/src/Model/Table/Specific/WheelsExtraTable.php
  class WheelsExtraTable (line 9) | class WheelsExtraTable extends Table {
    method initialize (line 15) | public function initialize(array $config): void {

FILE: tests/test_app/src/Model/Table/Specific/WheelsTable.php
  class WheelsTable (line 9) | class WheelsTable extends Table {
    method initialize (line 15) | public function initialize(array $config): void {

FILE: tests/test_app/src/Model/Table/WheelsExtraTable.php
  class WheelsExtraTable (line 9) | class WheelsExtraTable extends Table {
    method initialize (line 15) | public function initialize(array $config): void {

FILE: tests/test_app/src/Model/Table/WheelsTable.php
  class WheelsTable (line 9) | class WheelsTable extends Table {
    method initialize (line 15) | public function initialize(array $config): void {

FILE: tests/test_app/src/ValueObject/DoubleQuoteStringName.php
  class DoubleQuoteStringName (line 10) | class DoubleQuoteStringName extends StringName {
    method __toString (line 15) | public function __toString(): string {

FILE: tests/test_app/src/View/AppView.php
  class AppView (line 9) | class AppView extends View {

FILE: tests/test_app/src/View/Cell/TestCell.php
  class TestCell (line 8) | class TestCell extends Cell {
    method display (line 15) | public function display() {
    method custom (line 24) | public function custom() {

FILE: tests/test_app/src/View/CustomView.php
  class CustomView (line 8) | class CustomView extends AppView {

FILE: tests/test_app/src/View/Helper/HtmlHelper.php
  class HtmlHelper (line 6) | class HtmlHelper extends CoreHtmlHelper {

FILE: tests/test_app/src/View/Helper/MyHelper.php
  class MyHelper (line 6) | class MyHelper extends Helper {

FILE: tests/test_app/src/View/Helper/MyMethodHelper.php
  class MyMethodHelper (line 11) | class MyMethodHelper extends Helper {

FILE: tests/test_app/tests/Fixture/SmallWindowsFixture.php
  class SmallWindowsFixture (line 7) | class SmallWindowsFixture extends TestFixture {

FILE: tests/test_app/vendor/cakephp/cakephp/tests/Fixture/PostsFixture.php
  class PostsFixture (line 7) | class PostsFixture extends TestFixture

FILE: tests/test_files/ClassAnnotation/TableFind/before.php
  class TestMeController (line 8) | class TestMeController
    method test (line 12) | public function test(): void {

FILE: tests/test_files/Command/MyCommand.php
  class MyCommand (line 10) | class MyCommand extends Command {
    method main (line 20) | public function main() {

FILE: tests/test_files/Controller/AppController.php
  class AppController (line 10) | class AppController extends Controller {
    method initialize (line 15) | public function initialize(): void {

FILE: tests/test_files/Controller/BarController.php
  class BarController (line 12) | class BarController extends AppController {
    method initialize (line 21) | public function initialize(): void {
    method index (line 33) | public function index() {

FILE: tests/test_files/Controller/Component/MyComponent.php
  class MyComponent (line 11) | class MyComponent extends Component {

FILE: tests/test_files/Controller/Component/MyControllerComponent.php
  class MyControllerComponent (line 10) | class MyControllerComponent extends Component {
    method beforeFilter (line 17) | public function beforeFilter(EventInterface $event): void {

FILE: tests/test_files/Controller/Component/MyOtherComponent.php
  class MyOtherComponent (line 11) | class MyOtherComponent extends Component {

FILE: tests/test_files/Controller/DynamicPropertiesController.php
  class DynamicPropertiesController (line 7) | #[\AllowDynamicProperties]

FILE: tests/test_files/Controller/DynamicPropertiesExistingDocblockController.php
  class DynamicPropertiesExistingDocblockController (line 9) | #[\AllowDynamicProperties]

FILE: tests/test_files/Controller/FoosController.php
  class FoosController (line 7) | class FoosController extends AppController {

FILE: tests/test_files/Controller/HousesController.php
  class HousesController (line 9) | class HousesController extends Controller {

FILE: tests/test_files/Controller/WindowsController.php
  class WindowsController (line 9) | class WindowsController extends Controller {

FILE: tests/test_files/Custom/CustomClass.php
  class CustomClass (line 9) | class CustomClass {
    method initialize (line 16) | public function initialize() {

FILE: tests/test_files/Custom/Nested/NestedClass.php
  class NestedClass (line 9) | class NestedClass {
    method initialize (line 16) | public function initialize() {

FILE: tests/test_files/FormAnnotation/FormAnnotation.existing.php
  class FormAnnotation (line 6) | class FormAnnotation {
    method test (line 8) | public function test() {

FILE: tests/test_files/FormAnnotation/FormAnnotation.missing.php
  class FormAnnotation (line 6) | class FormAnnotation {
    method test (line 8) | public function test() {

FILE: tests/test_files/MailerAnnotation/MailerAnnotation.existing.php
  class MailerAnnotation (line 6) | class MailerAnnotation {
    method test (line 8) | public function test($notificationMailer) {

FILE: tests/test_files/MailerAnnotation/MailerAnnotation.invalid.php
  class DebugMailer (line 4) | class DebugMailer {
    method test (line 11) | public function test(): self {

FILE: tests/test_files/MailerAnnotation/MailerAnnotation.missing.php
  class MailerAnnotation (line 4) | class MailerAnnotation {
    method test (line 6) | public function test() {

FILE: tests/test_files/MailerAnnotation/MailerAnnotation.missing2.php
  class MailerAnnotation (line 4) | class MailerAnnotation {
    method test (line 6) | public function test() {

FILE: tests/test_files/MailerAnnotation/MailerAnnotation.missing3.php
  class MailerAnnotation (line 4) | class MailerAnnotation {
    method test (line 6) | public function test() {

FILE: tests/test_files/Model/Entity/Car.php
  class Car (line 17) | class Car extends Entity {

FILE: tests/test_files/Model/Entity/Complex/Wheel.php
  class Wheel (line 16) | class Wheel extends Entity {
    method _getVirtualOne (line 25) | protected function _getVirtualOne() {

FILE: tests/test_files/Model/Entity/Constants/Wheel.php
  class Wheel (line 18) | class Wheel extends Entity {
    method _getVirtualOne (line 27) | protected function _getVirtualOne() {

FILE: tests/test_files/Model/Entity/Constants/WheelComplex.php
  class Wheel (line 18) | class Wheel extends Entity {
    method _getVirtualOne (line 27) | protected function _getVirtualOne() {

FILE: tests/test_files/Model/Entity/ConstantsPartial/Wheel.php
  class Wheel (line 16) | class Wheel extends Entity {
    method _getVirtualOne (line 34) | protected function _getVirtualOne() {
    method _getVirtualTwo (line 38) | protected function _getVirtualTwo() {
    method _getWheels (line 48) | protected function _getWheels($wheels = []) {

FILE: tests/test_files/Model/Entity/ConstantsPartialResult/Wheel.php
  class Wheel (line 16) | class Wheel extends Entity {
    method _getVirtualOne (line 36) | protected function _getVirtualOne() {
    method _getVirtualTwo (line 40) | protected function _getVirtualTwo() {
    method _getWheels (line 50) | protected function _getWheels($wheels = []) {

FILE: tests/test_files/Model/Entity/Foo.php
  class Foo (line 15) | class Foo extends Entity {

FILE: tests/test_files/Model/Entity/PHP/ComplexType.php
  class ComplexType (line 15) | class ComplexType extends Entity {

FILE: tests/test_files/Model/Entity/PHP/Duplicates.php
  class Duplicates (line 15) | class Duplicates extends Entity {

FILE: tests/test_files/Model/Entity/PHP/Generics.php
  class Generics (line 16) | class Generics extends Entity {

FILE: tests/test_files/Model/Entity/PHP7/Virtual.php
  class Virtual (line 20) | class Virtual extends Entity {
    method _getVirtualOne (line 22) | protected function _getVirtualOne(): ?string {
    method _getVirtualTwo (line 26) | protected function _getVirtualTwo(): string {
    method _getNestedGeneric (line 33) | protected function _getNestedGeneric(): array {

FILE: tests/test_files/Model/Entity/Relations/Bar.php
  class Bar (line 12) | class Bar extends Entity {

FILE: tests/test_files/Model/Entity/Relations/Foo.php
  class Foo (line 13) | class Foo extends Entity {

FILE: tests/test_files/Model/Entity/Relations/User.php
  class User (line 12) | class User extends Entity {

FILE: tests/test_files/Model/Entity/Virtual.php
  class Virtual (line 19) | class Virtual extends Entity {
    method _getVirtualOne (line 28) | protected function _getVirtualOne() {
    method _getVirtualTwo (line 32) | protected function _getVirtualTwo() {
    method _getVirtualReadOnly (line 40) | protected function _getVirtualReadOnly() {
    method _getWheels (line 49) | protected function _getWheels($wheels = []) {

FILE: tests/test_files/Model/Entity/Wheel.php
  class Wheel (line 18) | class Wheel extends Entity {
    method _getVirtualOne (line 27) | protected function _getVirtualOne() {

FILE: tests/test_files/Model/Table/BarBarsAbstractTable.php
  class BarBarsAbstractTable (line 27) | class BarBarsAbstractTable extends AbstractTable {
    method initialize (line 33) | public function initialize(array $config): void {

FILE: tests/test_files/Model/Table/BarBarsDetailedTable.php
  class BarBarsTable (line 29) | class BarBarsTable extends Table {
    method initialize (line 35) | public function initialize(array $config): void {

FILE: tests/test_files/Model/Table/BarBarsEntityTemplateTable.php
  class BarBarsTable (line 29) | class BarBarsTable extends Table {
    method initialize (line 35) | public function initialize(array $config): void {

FILE: tests/test_files/Model/Table/BarBarsTable.php
  class BarBarsTable (line 29) | class BarBarsTable extends Table {
    method initialize (line 35) | public function initialize(array $config): void {

FILE: tests/test_files/Model/Table/CallbacksTable.php
  class CallbacksTable (line 10) | class CallbacksTable extends Table {
    method beforeSave (line 18) | public function beforeSave(Event $event, EntityInterface $entity, Arra...
    method afterSave (line 27) | public function afterSave(EventInterface $event, EntityInterface $enti...
    method beforeDelete (line 30) | public function beforeDelete(Event $event, EntityInterface $entity, Ar...

FILE: tests/test_files/Model/Table/FooTable.php
  class FooTable (line 19) | class FooTable extends Table {
    method initialize (line 25) | public function initialize(array $config): void {

FILE: tests/test_files/Model/Table/SkipMeTable.php
  class SkipMeTable (line 9) | class SkipMeTable extends Table {
    method initialize (line 15) | public function initialize(array $config): void {

FILE: tests/test_files/Model/Table/SkipSomeTable.php
  class SkipSomeTable (line 10) | class SkipSomeTable extends Table {
    method initialize (line 16) | public function initialize(array $config): void {
    method foo (line 25) | public function foo() {

FILE: tests/test_files/Model/Table/Specific/BarBarsAbstractTable.php
  class BarBarsAbstractTable (line 25) | class BarBarsAbstractTable extends AbstractTable {
    method initialize (line 31) | public function initialize(array $config): void {

FILE: tests/test_files/Model/Table/Specific/BarBarsDetailedTable.php
  class BarBarsTable (line 27) | class BarBarsTable extends Table {
    method initialize (line 33) | public function initialize(array $config): void {

FILE: tests/test_files/Model/Table/Specific/BarBarsTable.php
  class BarBarsTable (line 27) | class BarBarsTable extends Table {
    method initialize (line 33) | public function initialize(array $config): void {

FILE: tests/test_files/Model/Table/Specific/CallbacksTable.php
  class CallbacksTable (line 10) | class CallbacksTable extends Table {
    method beforeSave (line 18) | public function beforeSave(Event $event, EntityInterface $entity, Arra...
    method afterSave (line 27) | public function afterSave(EventInterface $event, EntityInterface $enti...
    method beforeDelete (line 30) | public function beforeDelete(Event $event, EntityInterface $entity, Ar...

FILE: tests/test_files/Model/Table/Specific/FooTable.php
  class FooTable (line 19) | class FooTable extends Table {
    method initialize (line 25) | public function initialize(array $config): void {

FILE: tests/test_files/Model/Table/Specific/SkipMeTable.php
  class SkipMeTable (line 9) | class SkipMeTable extends Table {
    method initialize (line 15) | public function initialize(array $config): void {

FILE: tests/test_files/Model/Table/Specific/SkipSomeTable.php
  class SkipSomeTable (line 10) | class SkipSomeTable extends Table {
    method initialize (line 16) | public function initialize(array $config): void {
    method foo (line 25) | public function foo() {

FILE: tests/test_files/Model/Table/Specific/WheelsExtraTable.php
  class WheelsExtraTable (line 9) | class WheelsExtraTable extends Table {
    method initialize (line 15) | public function initialize(array $config): void {

FILE: tests/test_files/Model/Table/Specific/WheelsTable.php
  class WheelsTable (line 23) | class WheelsTable extends Table {
    method initialize (line 29) | public function initialize(array $config): void {

FILE: tests/test_files/Model/Table/Validation/IpRulesTable.php
  class IpRulesTable (line 8) | class IpRulesTable extends Table {
    method validationDefault (line 14) | public function validationDefault(Validator $validator): Validator {
    method verifyIpRanges (line 40) | public function verifyIpRanges(string $value, array $context): bool {
    method verifyDenyRanges (line 49) | public function verifyDenyRanges(string $value, array $context): bool {

FILE: tests/test_files/Model/Table/ValidationResult/IpRulesTable.php
  class IpRulesTable (line 8) | class IpRulesTable extends Table {
    method validationDefault (line 14) | public function validationDefault(Validator $validator): Validator {
    method verifyIpRanges (line 42) | public function verifyIpRanges(string $value, array $context): bool {
    method verifyDenyRanges (line 51) | public function verifyDenyRanges(string $value, array $context): bool {

FILE: tests/test_files/Model/Table/WheelsExtraTable.php
  class WheelsExtraTable (line 9) | class WheelsExtraTable extends Table {
    method initialize (line 15) | public function initialize(array $config): void {

FILE: tests/test_files/Model/Table/WheelsTable.php
  class WheelsTable (line 24) | class WheelsTable extends Table {
    method initialize (line 30) | public function initialize(array $config): void {

FILE: tests/test_files/View/AppView.php
  class AppView (line 13) | class AppView extends View {

FILE: tests/test_files/View/Helper/MyHelper.php
  class MyHelper (line 11) | class MyHelper extends Helper {

FILE: tests/test_files/View/Helper/MyMethodHelper.php
  class MyMethodHelper (line 13) | class MyMethodHelper extends Helper {

FILE: tests/test_files/VirtualFieldAnnotation/VirtualFieldAnnotation.existing.php
  class Foo (line 4) | class Foo {
    method _getExpectedReleaseType (line 11) | protected function _getExpectedReleaseType(): ?int {
    method _getSomething (line 22) | protected function _getSomething(): ?string {

FILE: tests/test_files/VirtualFieldAnnotation/VirtualFieldAnnotation.missing.php
  class Foo (line 4) | class Foo {
    method _getExpectedReleaseType (line 9) | protected function _getExpectedReleaseType(): ?int

FILE: tests/test_files/tests/BarControllerTest.attribute.php
  class BarControllerTest (line 8) | #[UsesClass(BarController::class)]

FILE: tests/test_files/tests/BarControllerTest.existing.php
  class BarControllerTest (line 9) | class BarControllerTest extends IntegrationTestCase {

FILE: tests/test_files/tests/BarControllerTest.link.php
  class BarControllerTest (line 9) | class BarControllerTest extends IntegrationTestCase {

FILE: tests/test_files/tests/BarControllerTest.missing.php
  class BarControllerTest (line 6) | class BarControllerTest extends IntegrationTestCase {
Condensed preview — 478 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,095K chars).
[
  {
    "path": ".editorconfig",
    "chars": 356,
    "preview": "; This file is for unifying the coding style for different editors and IDEs.\n; More information at https://editorconfig."
  },
  {
    "path": ".gitattributes",
    "chars": 473,
    "preview": "# Define the line ending behavior of the different file extensions\n# Set default behavior, in case users don't have core"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 67,
    "preview": "# These are supported funding model platforms\n\ngithub: dereuromark\n"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 382,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"npm\"\n    directory: \"/docs\"\n    schedule:\n      interval: \"weekly\"\n    ignor"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 3610,
    "preview": "name: CI\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  testsuite:\n    runs-on: ubuntu-24.04\n    strategy:\n "
  },
  {
    "path": ".github/workflows/deploy-docs.yml",
    "chars": 1257,
    "preview": "name: Deploy Documentation\n\non:\n  push:\n    branches:\n      - master\n    paths:\n      - 'docs/**'\n      - '.github/workf"
  },
  {
    "path": ".gitignore",
    "chars": 351,
    "preview": "/phpunit.phar\n/.phpunit.result.cache\n/.phpunit.cache/\n/vendor/\n/tmp/\n/composer.lock\n/composer.phar\n# IDE and editor spec"
  },
  {
    "path": "LICENSE",
    "chars": 1066,
    "preview": "MIT License\n\nCopyright (c) 2017 Mark Sch.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\n"
  },
  {
    "path": "README.md",
    "chars": 4907,
    "preview": "#  CakePHP IdeHelper Plugin\n\n[![CI](https://github.com/dereuromark/cakephp-ide-helper/actions/workflows/ci.yml/badge.svg"
  },
  {
    "path": "annotate-watcher.cjs",
    "chars": 1051,
    "preview": "// watcher.js\nconst chokidar = require('chokidar');\nconst { exec } = require('child_process');\n\n// Helper to parse CLI a"
  },
  {
    "path": "composer.json",
    "chars": 3224,
    "preview": "{\n\t\"name\": \"dereuromark/cakephp-ide-helper\",\n\t\"description\": \"CakePHP IdeHelper Plugin to improve auto-completion\",\n\t\"li"
  },
  {
    "path": "config/app.example.php",
    "chars": 2827,
    "preview": "<?php\n\nreturn [\n\t// Copy the following over to your project one in ROOT/config/\n\t'IdeHelper' => [\n\t\t// Additional plugin"
  },
  {
    "path": "docs/.vitepress/config.ts",
    "chars": 4190,
    "preview": "import { defineConfig } from 'vitepress'\n\nexport default defineConfig({\n  title: 'cakephp-ide-helper',\n  description: 'A"
  },
  {
    "path": "docs/.vitepress/theme/custom.css",
    "chars": 642,
    "preview": ":root {\n  --vp-c-brand-1: #6d28d9;\n  --vp-c-brand-2: #7c3aed;\n  --vp-c-brand-3: #a78bfa;\n  --vp-c-brand-soft: rgba(167, "
  },
  {
    "path": "docs/.vitepress/theme/index.ts",
    "chars": 94,
    "preview": "import DefaultTheme from 'vitepress/theme'\nimport './custom.css'\n\nexport default DefaultTheme\n"
  },
  {
    "path": "docs/annotations/callbacks.md",
    "chars": 2735,
    "preview": "# Callbacks and CallbackAnnotationTasks\n\nThis is a separate annotations tool that focuses on **methods** and their doc\nb"
  },
  {
    "path": "docs/annotations/classes.md",
    "chars": 4694,
    "preview": "# Classes and ClassAnnotationTasks\n\nIn order to run certain \"fixers\" over all classes, class annotations and their\ntasks"
  },
  {
    "path": "docs/annotations/commands.md",
    "chars": 841,
    "preview": "# Commands and Routes\n\n## Commands\n\nCommands should annotate their primary model as well as all manually loaded\nmodels.\n"
  },
  {
    "path": "docs/annotations/controllers.md",
    "chars": 1421,
    "preview": "# Controllers\n\nAll controllers should at least annotate their primary model. They should also\nannotate the other loaded "
  },
  {
    "path": "docs/annotations/custom-class-annotator.md",
    "chars": 4565,
    "preview": "# Custom Class Annotators\n\nWorked examples of writing custom class annotators in your CakePHP application.\n\n## `getByUui"
  },
  {
    "path": "docs/annotations/index.md",
    "chars": 1975,
    "preview": "# Annotations Updater\n\nThe Annotator keeps doc blocks and annotations in sync with your code without\nmodifying functiona"
  },
  {
    "path": "docs/annotations/models.md",
    "chars": 5524,
    "preview": "# Models\n\nAnnotate Tables and their Entities:\n\n```bash\nbin/cake annotate models\n```\n\n## Tables\n\nTables annotate their en"
  },
  {
    "path": "docs/annotations/operations.md",
    "chars": 4920,
    "preview": "# Operations\n\nCross-cutting flags and workflows that apply to every annotator subcommand.\n\n## Running All Commands\n\n```b"
  },
  {
    "path": "docs/annotations/templates.md",
    "chars": 4634,
    "preview": "# Templates\n\nAnnotate view templates and elements:\n\n```bash\nbin/cake annotate templates\n```\n\nTemplates should have a `/*"
  },
  {
    "path": "docs/annotations/view.md",
    "chars": 1885,
    "preview": "# View, Components, Helpers\n\n## View\n\nThe `AppView` class should annotate the helpers of the plugins and the app.\n\n```ba"
  },
  {
    "path": "docs/code-completion/index.md",
    "chars": 5587,
    "preview": "# Code Completion File Generator\n\nIn contrast to the [PhpStorm meta file generator](/generator/), this tool is\nintention"
  },
  {
    "path": "docs/generator/custom-tasks.md",
    "chars": 6447,
    "preview": "# Custom Tasks and Directives\n\n## Adding Your Own Tasks\n\nCreate your own task class:\n\n```php\nnamespace App\\Generator\\Tas"
  },
  {
    "path": "docs/generator/index.md",
    "chars": 1093,
    "preview": "# Meta File Generator\n\n![Model Typehinting](/img/model_typehinting.png)\n\n![Model Autocomplete](/img/model_autocomplete.p"
  },
  {
    "path": "docs/generator/operations.md",
    "chars": 1082,
    "preview": "# Operations\n\nCross-cutting flags and workflows for the meta file generator.\n\n## Include/Exclude Plugins\n\nMany plugins d"
  },
  {
    "path": "docs/generator/tasks.md",
    "chars": 6052,
    "preview": "# Available Tasks\n\nThe list of built-in tasks shipped with the meta file generator.\n\n## Plugins\n\nIn your `Application.ph"
  },
  {
    "path": "docs/guide/ide-support.md",
    "chars": 1759,
    "preview": "# IDE Support\n\nThis plugin is intended to work with **any IDE** that supports annotations and\ncode completion. Below is "
  },
  {
    "path": "docs/guide/index.md",
    "chars": 1892,
    "preview": "# Introduction\n\n`cakephp-ide-helper` improves IDE compatibility and uses annotations to make\nyour IDE understand the \"ma"
  },
  {
    "path": "docs/guide/installation.md",
    "chars": 739,
    "preview": "# Installation\n\n## Composer\n\nInstall as a `require-dev` dependency:\n\n```bash\ncomposer require --dev dereuromark/cakephp-"
  },
  {
    "path": "docs/guide/migration.md",
    "chars": 393,
    "preview": "# Migrating from 4.x\n\nA few commands were renamed when moving to 5.x:\n\n| 4.x | 5.x |\n|-----|-----|\n| `bin/cake code_comp"
  },
  {
    "path": "docs/guide/usage.md",
    "chars": 2098,
    "preview": "# Usage\n\nA short tour of the high-level commands. Each section has a dedicated guide\nwith the full set of options.\n\n## A"
  },
  {
    "path": "docs/illuminator/index.md",
    "chars": 4173,
    "preview": "# PHP File Illuminator\n\nThe Illuminator can modify your PHP files based on Illuminator rule sets. You\ncan use the pre-se"
  },
  {
    "path": "docs/index.md",
    "chars": 1552,
    "preview": "---\nlayout: home\n\nhero:\n  name: cakephp-ide-helper\n  text: IDE Helper for CakePHP\n  tagline: Annotations, meta files, co"
  },
  {
    "path": "docs/package.json",
    "chars": 285,
    "preview": "{\n  \"name\": \"cakephp-ide-helper-docs\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"docs:dev\": \"vitepress "
  },
  {
    "path": "docs/reference/configuration.md",
    "chars": 3880,
    "preview": "# Configuration Reference\n\nAll Configure options live under the `IdeHelper` key in `app.php`. The\ncanonical reference wi"
  },
  {
    "path": "docs/reference/contributing.md",
    "chars": 428,
    "preview": "# Contributing\n\n## Basics\n\nSee the composer scripts:\n\n```bash\ncomposer cs-check\ncomposer cs-fix\ncomposer test-setup\ncomp"
  },
  {
    "path": "phpcs.xml",
    "chars": 331,
    "preview": "<?xml version=\"1.0\"?>\n<ruleset name=\"plugin\">\n\t<arg name=\"cache\" value=\".phpcs.cache\"/>\n\t<rule ref=\"PSR2R\"/>\n\n    <arg v"
  },
  {
    "path": "phpstan.neon",
    "chars": 453,
    "preview": "parameters:\n\tlevel: 8\n\tpaths:\n\t\t- src/\n\n\ttreatPhpDocTypesAsCertain: false\n\tbootstrapFiles:\n\t\t- tests/bootstrap.php\n\t\t- t"
  },
  {
    "path": "phpunit.xml.dist",
    "chars": 798,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t colors=\"true\"\n\t"
  },
  {
    "path": "src/Annotation/AbstractAnnotation.php",
    "chars": 1360,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotation;\n\nuse RuntimeException;\n\nabstract class AbstractAnnotation implements AnnotationIn"
  },
  {
    "path": "src/Annotation/AnnotationFactory.php",
    "chars": 3662,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotation;\n\nuse RuntimeException;\n\nclass AnnotationFactory {\n\n\tprotected string $property;\n\n"
  },
  {
    "path": "src/Annotation/AnnotationInterface.php",
    "chars": 212,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotation;\n\ninterface AnnotationInterface {\n\n\t/**\n\t * @return string\n\t */\n\tpublic function g"
  },
  {
    "path": "src/Annotation/ExtendsAnnotation.php",
    "chars": 1500,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotation;\n\nclass ExtendsAnnotation extends AbstractAnnotation {\n\n\t/**\n\t * @var string\n\t */\n"
  },
  {
    "path": "src/Annotation/LinkAnnotation.php",
    "chars": 1443,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotation;\n\nclass LinkAnnotation extends AbstractAnnotation {\n\n\t/**\n\t * @var string\n\t */\n\tpu"
  },
  {
    "path": "src/Annotation/MethodAnnotation.php",
    "chars": 1851,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotation;\n\nclass MethodAnnotation extends AbstractAnnotation {\n\n\t/**\n\t * @var string\n\t */\n\t"
  },
  {
    "path": "src/Annotation/MixinAnnotation.php",
    "chars": 1367,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotation;\n\nclass MixinAnnotation extends AbstractAnnotation {\n\n\t/**\n\t * @var string\n\t */\n\tp"
  },
  {
    "path": "src/Annotation/ParamAnnotation.php",
    "chars": 1618,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotation;\n\nclass ParamAnnotation extends AbstractAnnotation {\n\n\t/**\n\t * @var string\n\t */\n\tp"
  },
  {
    "path": "src/Annotation/PropertyAnnotation.php",
    "chars": 1708,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotation;\n\nclass PropertyAnnotation extends AbstractAnnotation {\n\n\t/**\n\t * @var string\n\t */"
  },
  {
    "path": "src/Annotation/PropertyReadAnnotation.php",
    "chars": 166,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotation;\n\nclass PropertyReadAnnotation extends PropertyAnnotation {\n\n\t/**\n\t * @var string\n"
  },
  {
    "path": "src/Annotation/ReplacableAnnotationInterface.php",
    "chars": 409,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotation;\n\ninterface ReplacableAnnotationInterface {\n\n\t/**\n\t * @param \\IdeHelper\\Annotation"
  },
  {
    "path": "src/Annotation/SeeAnnotation.php",
    "chars": 1363,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotation;\n\nclass SeeAnnotation extends AbstractAnnotation {\n\n\t/**\n\t * @var string\n\t */\n\tpub"
  },
  {
    "path": "src/Annotation/UsesAnnotation.php",
    "chars": 1365,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotation;\n\nclass UsesAnnotation extends AbstractAnnotation {\n\n\t/**\n\t * @var string\n\t */\n\tpu"
  },
  {
    "path": "src/Annotation/VariableAnnotation.php",
    "chars": 2384,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotation;\n\nclass VariableAnnotation extends AbstractAnnotation {\n\n\t/**\n\t * @var string\n\t */"
  },
  {
    "path": "src/Annotator/AbstractAnnotator.php",
    "chars": 25455,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator;\n\nuse Bake\\View\\Helper\\DocBlockHelper;\nuse Cake\\Core\\Configure;\nuse Cake\\Core\\Insta"
  },
  {
    "path": "src/Annotator/CallbackAnnotator.php",
    "chars": 1096,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator;\n\nuse RuntimeException;\n\nclass CallbackAnnotator extends AbstractAnnotator {\n\n\t/**\n"
  },
  {
    "path": "src/Annotator/CallbackAnnotatorTask/AbstractCallbackAnnotatorTask.php",
    "chars": 3334,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\CallbackAnnotatorTask;\n\nuse IdeHelper\\Annotator\\AbstractAnnotator;\nuse IdeHelper\\Co"
  },
  {
    "path": "src/Annotator/CallbackAnnotatorTask/CallbackAnnotatorTaskInterface.php",
    "chars": 334,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\CallbackAnnotatorTask;\n\ninterface CallbackAnnotatorTaskInterface {\n\n\t/**\n\t * @param"
  },
  {
    "path": "src/Annotator/CallbackAnnotatorTask/TableCallbackAnnotatorTask.php",
    "chars": 3434,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\CallbackAnnotatorTask;\n\nuse Cake\\ORM\\TableRegistry;\nuse IdeHelper\\Annotation\\ParamA"
  },
  {
    "path": "src/Annotator/CallbackAnnotatorTask/VirtualFieldCallbackAnnotatorTask.php",
    "chars": 5700,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\CallbackAnnotatorTask;\n\nuse Cake\\Core\\Exception\\CakeException;\nuse Cake\\Utility\\Inf"
  },
  {
    "path": "src/Annotator/CallbackAnnotatorTaskCollection.php",
    "chars": 2166,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator;\n\nuse Cake\\Core\\Configure;\nuse IdeHelper\\Annotator\\CallbackAnnotatorTask\\TableCallb"
  },
  {
    "path": "src/Annotator/ClassAnnotator.php",
    "chars": 1042,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator;\n\nuse RuntimeException;\n\nclass ClassAnnotator extends AbstractAnnotator {\n\n\t/**\n\t *"
  },
  {
    "path": "src/Annotator/ClassAnnotatorTask/AbstractClassAnnotatorTask.php",
    "chars": 5352,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\ClassAnnotatorTask;\n\nuse IdeHelper\\Annotator\\AbstractAnnotator;\nuse IdeHelper\\Conso"
  },
  {
    "path": "src/Annotator/ClassAnnotatorTask/ClassAnnotatorTaskInterface.php",
    "chars": 416,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\ClassAnnotatorTask;\n\ninterface ClassAnnotatorTaskInterface {\n\n\t/**\n\t * Deprecated: "
  },
  {
    "path": "src/Annotator/ClassAnnotatorTask/FormClassAnnotatorTask.php",
    "chars": 2179,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\ClassAnnotatorTask;\n\nuse Cake\\Core\\Configure;\nuse IdeHelper\\Annotation\\AnnotationFa"
  },
  {
    "path": "src/Annotator/ClassAnnotatorTask/MailerClassAnnotatorTask.php",
    "chars": 4299,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\ClassAnnotatorTask;\n\nuse Cake\\Core\\Configure;\nuse IdeHelper\\Annotation\\AnnotationFa"
  },
  {
    "path": "src/Annotator/ClassAnnotatorTask/ModelAwareClassAnnotatorTask.php",
    "chars": 1925,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\ClassAnnotatorTask;\n\nuse ReflectionClass;\nuse Throwable;\n\n/**\n * Classes that use M"
  },
  {
    "path": "src/Annotator/ClassAnnotatorTask/PathAwareClassAnnotatorTaskInterface.php",
    "chars": 1486,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\ClassAnnotatorTask;\n\n/**\n * Optional interface that a class annotator task can impl"
  },
  {
    "path": "src/Annotator/ClassAnnotatorTask/TableFindAnnotatorTask.php",
    "chars": 2841,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\ClassAnnotatorTask;\n\nuse Cake\\Core\\Configure;\nuse IdeHelper\\Annotation\\AnnotationFa"
  },
  {
    "path": "src/Annotator/ClassAnnotatorTask/TableFindNodeVisitor.php",
    "chars": 2667,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\ClassAnnotatorTask;\n\nuse Cake\\Utility\\Inflector;\nuse PhpParser\\Node;\nuse PhpParser\\"
  },
  {
    "path": "src/Annotator/ClassAnnotatorTask/TestClassAnnotatorTask.php",
    "chars": 4144,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\ClassAnnotatorTask;\n\nuse Cake\\Core\\Configure;\nuse IdeHelper\\Annotation\\AnnotationFa"
  },
  {
    "path": "src/Annotator/ClassAnnotatorTaskCollection.php",
    "chars": 2297,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator;\n\nuse Cake\\Core\\Configure;\nuse IdeHelper\\Annotator\\ClassAnnotatorTask\\FormClassAnno"
  },
  {
    "path": "src/Annotator/CommandAnnotator.php",
    "chars": 1232,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator;\n\nuse IdeHelper\\Annotator\\Traits\\ModelTrait;\nuse RuntimeException;\n\nclass CommandAn"
  },
  {
    "path": "src/Annotator/ComponentAnnotator.php",
    "chars": 3099,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator;\n\nuse Cake\\Controller\\ComponentRegistry;\nuse Cake\\Controller\\Controller;\nuse Cake\\C"
  },
  {
    "path": "src/Annotator/ControllerAnnotator.php",
    "chars": 10868,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator;\n\nuse Cake\\Core\\Configure;\nuse Cake\\Datasource\\ResultSetInterface;\nuse Cake\\Http\\Se"
  },
  {
    "path": "src/Annotator/EntityAnnotator.php",
    "chars": 14280,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator;\n\nuse Cake\\Core\\Configure;\nuse Cake\\ORM\\Association;\nuse Cake\\ORM\\Association\\Belon"
  },
  {
    "path": "src/Annotator/HelperAnnotator.php",
    "chars": 2101,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator;\n\nuse Cake\\View\\View;\nuse IdeHelper\\Annotation\\AnnotationFactory;\nuse IdeHelper\\Ann"
  },
  {
    "path": "src/Annotator/ModelAnnotator.php",
    "chars": 17146,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator;\n\nuse Cake\\Core\\Configure;\nuse Cake\\Database\\Schema\\TableSchemaInterface;\nuse Cake\\"
  },
  {
    "path": "src/Annotator/Template/VariableExtractor.php",
    "chars": 10869,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\Template;\n\nuse PHP_CodeSniffer\\Files\\File;\nuse PHP_CodeSniffer\\Util\\Tokens;\n\n/**\n *"
  },
  {
    "path": "src/Annotator/TemplateAnnotator.php",
    "chars": 18124,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator;\n\nuse Bake\\View\\Helper\\DocBlockHelper;\nuse Cake\\Collection\\CollectionInterface;\nuse"
  },
  {
    "path": "src/Annotator/Traits/ComponentTrait.php",
    "chars": 784,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\Traits;\n\nuse IdeHelper\\Utility\\App;\nuse IdeHelper\\Utility\\Plugin;\n\n/**\n * Handles c"
  },
  {
    "path": "src/Annotator/Traits/DocBlockTrait.php",
    "chars": 5863,
    "preview": "<?php\n\n/**\n * MIT License\n * For full license information, please view the LICENSE file that was distributed with this s"
  },
  {
    "path": "src/Annotator/Traits/FileTrait.php",
    "chars": 1146,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\Traits;\n\nuse PHP_CodeSniffer\\Config;\nuse PHP_CodeSniffer\\Files\\File;\nuse PHP_CodeSn"
  },
  {
    "path": "src/Annotator/Traits/HelperTrait.php",
    "chars": 684,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\Traits;\n\nuse IdeHelper\\Utility\\App;\nuse IdeHelper\\Utility\\Plugin;\n\n/**\n * Handles c"
  },
  {
    "path": "src/Annotator/Traits/ModelTrait.php",
    "chars": 752,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator\\Traits;\n\n/**\n * Handles model related things\n */\ntrait ModelTrait {\n\n\t/**\n\t * @para"
  },
  {
    "path": "src/Annotator/Traits/UseStatementsTrait.php",
    "chars": 2380,
    "preview": "<?php\n\n/**\n * MIT License\n * For full license information, please view the LICENSE file that was distributed with this s"
  },
  {
    "path": "src/Annotator/ViewAnnotator.php",
    "chars": 4912,
    "preview": "<?php\n\nnamespace IdeHelper\\Annotator;\n\nuse Cake\\Core\\Configure;\nuse Cake\\Http\\ServerRequest;\nuse IdeHelper\\Annotation\\An"
  },
  {
    "path": "src/CodeCompletion/CodeCompletionGenerator.php",
    "chars": 1806,
    "preview": "<?php\n\nnamespace IdeHelper\\CodeCompletion;\n\nuse Cake\\Core\\Configure;\nuse RuntimeException;\n\nclass CodeCompletionGenerato"
  },
  {
    "path": "src/CodeCompletion/Task/BehaviorTask.php",
    "chars": 2380,
    "preview": "<?php\n\nnamespace IdeHelper\\CodeCompletion\\Task;\n\nuse IdeHelper\\Filesystem\\Folder;\nuse IdeHelper\\Utility\\App;\nuse IdeHelp"
  },
  {
    "path": "src/CodeCompletion/Task/ControllerEventsTask.php",
    "chars": 2071,
    "preview": "<?php\n\nnamespace IdeHelper\\CodeCompletion\\Task;\n\nuse Cake\\Core\\Configure;\n\nclass ControllerEventsTask implements TaskInt"
  },
  {
    "path": "src/CodeCompletion/Task/ModelEventsTask.php",
    "chars": 2087,
    "preview": "<?php\n\nnamespace IdeHelper\\CodeCompletion\\Task;\n\nclass ModelEventsTask implements TaskInterface {\n\n\t/**\n\t * @var string\n"
  },
  {
    "path": "src/CodeCompletion/Task/SelectQueryTask.php",
    "chars": 2654,
    "preview": "<?php\n\nnamespace IdeHelper\\CodeCompletion\\Task;\n\nclass SelectQueryTask implements TaskInterface {\n\n\t/**\n\t * @var string\n"
  },
  {
    "path": "src/CodeCompletion/Task/TaskInterface.php",
    "chars": 206,
    "preview": "<?php\n\nnamespace IdeHelper\\CodeCompletion\\Task;\n\ninterface TaskInterface {\n\n\t/**\n\t * @return string\n\t */\n\tpublic functio"
  },
  {
    "path": "src/CodeCompletion/Task/ViewEventsTask.php",
    "chars": 849,
    "preview": "<?php\n\nnamespace IdeHelper\\CodeCompletion\\Task;\n\nclass ViewEventsTask implements TaskInterface {\n\n\t/**\n\t * @var string\n\t"
  },
  {
    "path": "src/CodeCompletion/TaskCollection.php",
    "chars": 2408,
    "preview": "<?php\n\nnamespace IdeHelper\\CodeCompletion;\n\nuse Cake\\Core\\Configure;\nuse IdeHelper\\CodeCompletion\\Task\\BehaviorTask;\nuse"
  },
  {
    "path": "src/Command/Annotate/AllCommand.php",
    "chars": 1905,
    "preview": "<?php\n\nnamespace IdeHelper\\Command\\Annotate;\n\nuse Cake\\Console\\Arguments;\nuse Cake\\Console\\ConsoleIo;\nuse Cake\\Core\\App;"
  },
  {
    "path": "src/Command/Annotate/CallbacksCommand.php",
    "chars": 2395,
    "preview": "<?php\n\nnamespace IdeHelper\\Command\\Annotate;\n\nuse Cake\\Console\\Arguments;\nuse Cake\\Console\\ConsoleIo;\nuse Cake\\Console\\C"
  },
  {
    "path": "src/Command/Annotate/ClassesCommand.php",
    "chars": 4432,
    "preview": "<?php\n\nnamespace IdeHelper\\Command\\Annotate;\n\nuse Cake\\Console\\Arguments;\nuse Cake\\Console\\ConsoleIo;\nuse Cake\\Console\\C"
  },
  {
    "path": "src/Command/Annotate/CommandsCommand.php",
    "chars": 1500,
    "preview": "<?php\n\nnamespace IdeHelper\\Command\\Annotate;\n\nuse Cake\\Console\\Arguments;\nuse Cake\\Console\\ConsoleIo;\nuse IdeHelper\\Anno"
  },
  {
    "path": "src/Command/Annotate/ComponentsCommand.php",
    "chars": 1543,
    "preview": "<?php\n\nnamespace IdeHelper\\Command\\Annotate;\n\nuse Cake\\Console\\Arguments;\nuse Cake\\Console\\ConsoleIo;\nuse IdeHelper\\Anno"
  },
  {
    "path": "src/Command/Annotate/ControllersCommand.php",
    "chars": 1889,
    "preview": "<?php\n\nnamespace IdeHelper\\Command\\Annotate;\n\nuse Cake\\Console\\Arguments;\nuse Cake\\Console\\ConsoleIo;\nuse Cake\\Core\\Conf"
  },
  {
    "path": "src/Command/Annotate/HelpersCommand.php",
    "chars": 1503,
    "preview": "<?php\n\nnamespace IdeHelper\\Command\\Annotate;\n\nuse Cake\\Console\\Arguments;\nuse Cake\\Console\\ConsoleIo;\nuse IdeHelper\\Anno"
  },
  {
    "path": "src/Command/Annotate/ModelsCommand.php",
    "chars": 1495,
    "preview": "<?php\n\nnamespace IdeHelper\\Command\\Annotate;\n\nuse Cake\\Console\\Arguments;\nuse Cake\\Console\\ConsoleIo;\nuse IdeHelper\\Anno"
  },
  {
    "path": "src/Command/Annotate/TemplatesCommand.php",
    "chars": 2298,
    "preview": "<?php\n\nnamespace IdeHelper\\Command\\Annotate;\n\nuse Cake\\Console\\Arguments;\nuse Cake\\Console\\ConsoleIo;\nuse IdeHelper\\Anno"
  },
  {
    "path": "src/Command/Annotate/ViewCommand.php",
    "chars": 1405,
    "preview": "<?php\n\nnamespace IdeHelper\\Command\\Annotate;\n\nuse Cake\\Console\\Arguments;\nuse Cake\\Console\\ConsoleIo;\nuse Cake\\Core\\App;"
  },
  {
    "path": "src/Command/AnnotateCommand.php",
    "chars": 5038,
    "preview": "<?php\n\nnamespace IdeHelper\\Command;\n\nuse Cake\\Console\\Arguments;\nuse Cake\\Console\\ConsoleIo;\nuse Cake\\Console\\ConsoleOpt"
  },
  {
    "path": "src/Command/Command.php",
    "chars": 2885,
    "preview": "<?php\n\nnamespace IdeHelper\\Command;\n\nuse Cake\\Command\\Command as CoreCommand;\nuse Cake\\Console\\Arguments;\nuse Cake\\Conso"
  },
  {
    "path": "src/Command/GenerateCodeCompletionCommand.php",
    "chars": 1866,
    "preview": "<?php\n\nnamespace IdeHelper\\Command;\n\nuse Cake\\Command\\Command;\nuse Cake\\Console\\Arguments;\nuse Cake\\Console\\ConsoleIo;\nu"
  },
  {
    "path": "src/Command/GeneratePhpStormMetaCommand.php",
    "chars": 3579,
    "preview": "<?php\n\nnamespace IdeHelper\\Command;\n\nuse Cake\\Command\\Command;\nuse Cake\\Console\\Arguments;\nuse Cake\\Console\\ConsoleIo;\nu"
  },
  {
    "path": "src/Command/IlluminateCommand.php",
    "chars": 3942,
    "preview": "<?php\n\nnamespace IdeHelper\\Command;\n\nuse Cake\\Console\\Arguments;\nuse Cake\\Console\\ConsoleIo;\nuse Cake\\Console\\ConsoleOpt"
  },
  {
    "path": "src/Console/Io.php",
    "chars": 6525,
    "preview": "<?php\n\nnamespace IdeHelper\\Console;\n\nuse Cake\\Command\\Command;\nuse Cake\\Console\\ConsoleIo;\nuse Cake\\Console\\Exception\\St"
  },
  {
    "path": "src/Filesystem/Folder.php",
    "chars": 24862,
    "preview": "<?php\ndeclare(strict_types=1);\n\n/**\n * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)\n * Copyright (c) "
  },
  {
    "path": "src/Generator/Directive/BaseDirective.php",
    "chars": 1620,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Directive;\n\nuse IdeHelper\\ValueObject\\KeyValue;\nuse IdeHelper\\ValueObject\\ValueObje"
  },
  {
    "path": "src/Generator/Directive/ExitPoint.php",
    "chars": 979,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Directive;\n\n/**\n * Helps to annotate expected exit point methods.\n *\n * This is ava"
  },
  {
    "path": "src/Generator/Directive/ExpectedArguments.php",
    "chars": 1687,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Directive;\n\n/**\n * Helps to annotate expected method arguments.\n *\n * The position "
  },
  {
    "path": "src/Generator/Directive/ExpectedReturnValues.php",
    "chars": 1440,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Directive;\n\n/**\n * Helps to annotate expected method return values.\n *\n * ### Examp"
  },
  {
    "path": "src/Generator/Directive/Override.php",
    "chars": 1381,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Directive;\n\n/**\n * Helps to annotate expected method argument and return type combi"
  },
  {
    "path": "src/Generator/Directive/RegisterArgumentsSet.php",
    "chars": 1497,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Directive;\n\n/**\n * Helps to register an argument set to be used in other directives"
  },
  {
    "path": "src/Generator/GeneratorInterface.php",
    "chars": 140,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator;\n\ninterface GeneratorInterface {\n\n\t/**\n\t * @return string\n\t */\n\tpublic function gen"
  },
  {
    "path": "src/Generator/PhpstormGenerator.php",
    "chars": 1657,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator;\n\nuse IdeHelper\\Console\\Io;\nuse IdeHelper\\Generator\\Directive\\RegisterArgumentsSet;"
  },
  {
    "path": "src/Generator/Task/BehaviorTask.php",
    "chars": 3730,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\ORM\\Table;\nuse IdeHelper\\Filesystem\\Folder;\nuse IdeHelper\\Generator"
  },
  {
    "path": "src/Generator/Task/CacheTask.php",
    "chars": 1838,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Cache\\Cache;\nuse IdeHelper\\Generator\\Directive\\ExpectedArguments;\nu"
  },
  {
    "path": "src/Generator/Task/CellTask.php",
    "chars": 2317,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\View\\Cell;\nuse Cake\\View\\CellTrait;\nuse IdeHelper\\Filesystem\\Folder"
  },
  {
    "path": "src/Generator/Task/ComponentTask.php",
    "chars": 2937,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Controller\\ComponentRegistry;\nuse Cake\\Controller\\Controller;\nuse I"
  },
  {
    "path": "src/Generator/Task/ConfigureTask.php",
    "chars": 2163,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Core\\Configure;\nuse IdeHelper\\Generator\\Directive\\ExpectedArguments"
  },
  {
    "path": "src/Generator/Task/ConnectionTask.php",
    "chars": 925,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Datasource\\ConnectionManager;\nuse IdeHelper\\Generator\\Directive\\Exp"
  },
  {
    "path": "src/Generator/Task/ConsoleHelperTask.php",
    "chars": 2247,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Console\\ConsoleIo;\nuse IdeHelper\\Filesystem\\Folder;\nuse IdeHelper\\G"
  },
  {
    "path": "src/Generator/Task/ConsoleTask.php",
    "chars": 517,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Console\\ConsoleIo;\nuse IdeHelper\\Generator\\Directive\\ExitPoint;\n\ncl"
  },
  {
    "path": "src/Generator/Task/DatabaseTableColumnNameTask.php",
    "chars": 2160,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Datasource\\ConnectionManager;\nuse IdeHelper\\Generator\\Directive\\Exp"
  },
  {
    "path": "src/Generator/Task/DatabaseTableColumnTypeTask.php",
    "chars": 3654,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Core\\Plugin;\nuse Cake\\Datasource\\ConnectionManager;\nuse IdeHelper\\G"
  },
  {
    "path": "src/Generator/Task/DatabaseTableTask.php",
    "chars": 2423,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Bake\\Utility\\TableScanner;\nuse Cake\\Core\\Configure;\nuse Cake\\Datasource\\"
  },
  {
    "path": "src/Generator/Task/DatabaseTypeTask.php",
    "chars": 1538,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Database\\TypeFactory;\nuse IdeHelper\\Generator\\Directive\\ExpectedArg"
  },
  {
    "path": "src/Generator/Task/ElementTask.php",
    "chars": 2084,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Core\\App;\nuse Cake\\View\\View;\nuse IdeHelper\\Generator\\Directive\\Exp"
  },
  {
    "path": "src/Generator/Task/EntityTask.php",
    "chars": 3095,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\ORM\\Table;\nuse IdeHelper\\Generator\\Directive\\ExpectedArguments;\nuse"
  },
  {
    "path": "src/Generator/Task/EnvTask.php",
    "chars": 1638,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse IdeHelper\\Generator\\Directive\\ExpectedArguments;\nuse IdeHelper\\ValueObje"
  },
  {
    "path": "src/Generator/Task/FixtureTask.php",
    "chars": 2370,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\TestSuite\\TestCase;\nuse IdeHelper\\Filesystem\\Folder;\nuse IdeHelper\\"
  },
  {
    "path": "src/Generator/Task/FormHelperTask.php",
    "chars": 1614,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\ORM\\TableRegistry;\nuse Cake\\View\\Helper\\FormHelper;\nuse IdeHelper\\G"
  },
  {
    "path": "src/Generator/Task/HelperTask.php",
    "chars": 2658,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\View\\View;\nuse Cake\\View\\ViewBuilder;\nuse IdeHelper\\Filesystem\\Fold"
  },
  {
    "path": "src/Generator/Task/LayoutTask.php",
    "chars": 2014,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Core\\App;\nuse Cake\\View\\ViewBuilder;\nuse DirectoryIterator;\nuse Ide"
  },
  {
    "path": "src/Generator/Task/MailerTask.php",
    "chars": 2040,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Mailer\\MailerAwareTrait;\nuse IdeHelper\\Filesystem\\Folder;\nuse IdeHe"
  },
  {
    "path": "src/Generator/Task/ModelTask.php",
    "chars": 2760,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\ORM\\Table;\nuse IdeHelper\\Filesystem\\Folder;\nuse IdeHelper\\Generator"
  },
  {
    "path": "src/Generator/Task/PluginTask.php",
    "chars": 1332,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Core\\Configure;\nuse Cake\\Core\\PluginApplicationInterface;\nuse Cake\\"
  },
  {
    "path": "src/Generator/Task/RequestTask.php",
    "chars": 3140,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Http\\ServerRequest;\nuse Cake\\Http\\Session;\nuse Cake\\Routing\\Route\\R"
  },
  {
    "path": "src/Generator/Task/RoutePathTask.php",
    "chars": 3633,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Core\\Configure;\nuse Cake\\Routing\\Router;\nuse Cake\\View\\Helper\\HtmlH"
  },
  {
    "path": "src/Generator/Task/TableAssociationTask.php",
    "chars": 1201,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\ORM\\Association\\BelongsTo;\nuse Cake\\ORM\\Association\\BelongsToMany;\n"
  },
  {
    "path": "src/Generator/Task/TableFinderTask.php",
    "chars": 5276,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Datasource\\QueryInterface;\nuse Cake\\ORM\\Association;\nuse Cake\\ORM\\Q"
  },
  {
    "path": "src/Generator/Task/TaskInterface.php",
    "chars": 191,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\ninterface TaskInterface {\n\n\t/**\n\t * @return array<string, \\IdeHelper\\Generat"
  },
  {
    "path": "src/Generator/Task/TranslationKeyTask.php",
    "chars": 4919,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Utility\\Inflector;\nuse IdeHelper\\Generator\\Directive\\ExpectedArgume"
  },
  {
    "path": "src/Generator/Task/ValidationTask.php",
    "chars": 1614,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator\\Task;\n\nuse Cake\\Validation\\Validator;\nuse IdeHelper\\Generator\\Directive\\ExpectedArg"
  },
  {
    "path": "src/Generator/TaskCollection.php",
    "chars": 4600,
    "preview": "<?php\n\nnamespace IdeHelper\\Generator;\n\nuse Cake\\Core\\Configure;\nuse IdeHelper\\Generator\\Task\\BehaviorTask;\nuse IdeHelper"
  },
  {
    "path": "src/IdeHelperPlugin.php",
    "chars": 1961,
    "preview": "<?php\n\nnamespace IdeHelper;\n\nuse Cake\\Console\\CommandCollection;\nuse Cake\\Core\\BasePlugin;\nuse IdeHelper\\Command\\Annotat"
  },
  {
    "path": "src/Illuminator/Illuminator.php",
    "chars": 1213,
    "preview": "<?php\n\nnamespace IdeHelper\\Illuminator;\n\nuse IdeHelper\\Filesystem\\Folder;\n\nclass Illuminator {\n\n\tprotected TaskCollectio"
  },
  {
    "path": "src/Illuminator/Task/AbstractTask.php",
    "chars": 1842,
    "preview": "<?php\n\nnamespace IdeHelper\\Illuminator\\Task;\n\nuse Cake\\Core\\Configure;\nuse Cake\\Core\\InstanceConfigTrait;\nuse IdeHelper\\"
  },
  {
    "path": "src/Illuminator/Task/ControllerDefaultTableTask.php",
    "chars": 4850,
    "preview": "<?php\n\nnamespace IdeHelper\\Illuminator\\Task;\n\nuse Cake\\Core\\Configure;\nuse IdeHelper\\Annotator\\Traits\\DocBlockTrait;\nuse"
  },
  {
    "path": "src/Illuminator/Task/EntityFieldTask.php",
    "chars": 6676,
    "preview": "<?php\n\nnamespace IdeHelper\\Illuminator\\Task;\n\nuse Cake\\Core\\Configure;\nuse Cake\\Utility\\Inflector;\nuse IdeHelper\\Annotat"
  },
  {
    "path": "src/Illuminator/Task/TableValidationLinkTask.php",
    "chars": 5886,
    "preview": "<?php\n\nnamespace IdeHelper\\Illuminator\\Task;\n\nuse PhpParser\\Node;\nuse PhpParser\\NodeTraverser;\nuse PhpParser\\NodeVisitor"
  },
  {
    "path": "src/Illuminator/TaskCollection.php",
    "chars": 5887,
    "preview": "<?php\n\nnamespace IdeHelper\\Illuminator;\n\nuse Cake\\Console\\ConsoleIo;\nuse Cake\\Core\\Configure;\nuse IdeHelper\\Console\\Io;\n"
  },
  {
    "path": "src/Utility/App.php",
    "chars": 1638,
    "preview": "<?php\n\nnamespace IdeHelper\\Utility;\n\nuse Cake\\Core\\App as CoreApp;\nuse Cake\\Core\\Configure;\nuse Cake\\Http\\Exception\\NotF"
  },
  {
    "path": "src/Utility/AppPath.php",
    "chars": 494,
    "preview": "<?php\n\nnamespace IdeHelper\\Utility;\n\nuse Cake\\Core\\App;\nuse Cake\\Core\\Exception\\MissingPluginException;\n\nclass AppPath {"
  },
  {
    "path": "src/Utility/CollectionClass.php",
    "chars": 601,
    "preview": "<?php\n\nnamespace IdeHelper\\Utility;\n\nuse Cake\\Core\\Configure;\nuse Cake\\Core\\Exception\\CakeException;\n\nclass CollectionCl"
  },
  {
    "path": "src/Utility/ControllerActionParser.php",
    "chars": 1267,
    "preview": "<?php\n\nnamespace IdeHelper\\Utility;\n\nuse App\\Controller\\AppController;\nuse ReflectionClass;\nuse ReflectionMethod;\nuse Ru"
  },
  {
    "path": "src/Utility/GenericString.php",
    "chars": 1227,
    "preview": "<?php\n\nnamespace IdeHelper\\Utility;\n\nuse Cake\\Core\\Configure;\nuse Cake\\Datasource\\ResultSetInterface;\n\nclass GenericStri"
  },
  {
    "path": "src/Utility/Plugin.php",
    "chars": 572,
    "preview": "<?php\n\nnamespace IdeHelper\\Utility;\n\nuse Cake\\Core\\Configure;\nuse Cake\\Core\\Plugin as CorePlugin;\n\nclass Plugin extends "
  },
  {
    "path": "src/Utility/PluginPath.php",
    "chars": 662,
    "preview": "<?php\n\nnamespace IdeHelper\\Utility;\n\nuse Cake\\Core\\Exception\\MissingPluginException;\n\nclass PluginPath {\n\n\t/**\n\t * @para"
  },
  {
    "path": "src/Utility/TranslationParser.php",
    "chars": 762,
    "preview": "<?php\n\nnamespace IdeHelper\\Utility;\n\nuse Cake\\I18n\\Parser\\PoFileParser;\n\nclass TranslationParser {\n\n\tprotected PoFilePar"
  },
  {
    "path": "src/ValueObject/ArgumentsSet.php",
    "chars": 879,
    "preview": "<?php\n\nnamespace IdeHelper\\ValueObject;\n\n/**\n * Helps to use an existing argument set to be used in other directives for"
  },
  {
    "path": "src/ValueObject/ClassName.php",
    "chars": 823,
    "preview": "<?php\n\nnamespace IdeHelper\\ValueObject;\n\n/**\n * Holds a FQCN string, which can be auto-casted to string\n */\nclass ClassN"
  },
  {
    "path": "src/ValueObject/KeyValue.php",
    "chars": 1135,
    "preview": "<?php\n\nnamespace IdeHelper\\ValueObject;\n\n/**\n * Holds a ValueObject key/value combination that will be treated literally"
  },
  {
    "path": "src/ValueObject/LiteralName.php",
    "chars": 683,
    "preview": "<?php\n\nnamespace IdeHelper\\ValueObject;\n\n/**\n * Holds a string that will be treated literally on output (no extra quotin"
  },
  {
    "path": "src/ValueObject/StringName.php",
    "chars": 703,
    "preview": "<?php\n\nnamespace IdeHelper\\ValueObject;\n\n/**\n * Holds a string with auto added `'` chars - can be auto-casted to string "
  },
  {
    "path": "src/ValueObject/ValueObjectInterface.php",
    "chars": 413,
    "preview": "<?php\n\nnamespace IdeHelper\\ValueObject;\n\ninterface ValueObjectInterface {\n\n\t/**\n\t * Creates itself from a string.\n\t *\n\t "
  },
  {
    "path": "src/View/Helper/DocBlockHelper.php",
    "chars": 7695,
    "preview": "<?php\n\nnamespace IdeHelper\\View\\Helper;\n\nuse Bake\\View\\Helper\\DocBlockHelper as BakeDocBlockHelper;\nuse Cake\\Core\\App;\nu"
  },
  {
    "path": "tests/Fixture/BarBarsFixture.php",
    "chars": 1142,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\Fixture;\n\nuse Cake\\TestSuite\\Fixture\\TestFixture;\n\nclass BarBarsFixture extends TestFixt"
  },
  {
    "path": "tests/Fixture/CarsFixture.php",
    "chars": 1466,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\Fixture;\n\nuse Cake\\TestSuite\\Fixture\\TestFixture;\nuse TestApp\\Model\\Enum\\CarStatus;\n\ncla"
  },
  {
    "path": "tests/Fixture/FoosFixture.php",
    "chars": 1285,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\Fixture;\n\nuse Cake\\TestSuite\\Fixture\\TestFixture;\n\nclass FoosFixture extends TestFixture"
  },
  {
    "path": "tests/Fixture/HousesFixture.php",
    "chars": 799,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\Fixture;\n\nuse Cake\\TestSuite\\Fixture\\TestFixture;\n\nclass HousesFixture extends TestFixtu"
  },
  {
    "path": "tests/Fixture/WheelsFixture.php",
    "chars": 1141,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\Fixture;\n\nuse Cake\\TestSuite\\Fixture\\TestFixture;\n\nclass WheelsFixture extends TestFixtu"
  },
  {
    "path": "tests/Fixture/WindowsFixture.php",
    "chars": 800,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\Fixture;\n\nuse Cake\\TestSuite\\Fixture\\TestFixture;\n\nclass WindowsFixture extends TestFixt"
  },
  {
    "path": "tests/TestCase/Annotation/AnnotationFactoryTest.php",
    "chars": 7357,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotation;\n\nuse Cake\\TestSuite\\TestCase;\nuse IdeHelper\\Annotation\\AnnotationFa"
  },
  {
    "path": "tests/TestCase/Annotation/ExtendsAnnotationTest.php",
    "chars": 2930,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotation;\n\nuse IdeHelper\\Annotation\\ExtendsAnnotation;\nuse IdeHelper\\Annotati"
  },
  {
    "path": "tests/TestCase/Annotation/MethodAnnotationTest.php",
    "chars": 2667,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotation;\n\nuse Cake\\TestSuite\\TestCase;\nuse IdeHelper\\Annotation\\MethodAnnota"
  },
  {
    "path": "tests/TestCase/Annotation/MixinAnnotationTest.php",
    "chars": 2848,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotation;\n\nuse Cake\\TestSuite\\TestCase;\nuse IdeHelper\\Annotation\\MixinAnnotat"
  },
  {
    "path": "tests/TestCase/Annotation/ParamAnnotationTest.php",
    "chars": 2084,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotation;\n\nuse Cake\\TestSuite\\TestCase;\nuse IdeHelper\\Annotation\\MethodAnnota"
  },
  {
    "path": "tests/TestCase/Annotation/PropertyAnnotationTest.php",
    "chars": 2450,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotation;\n\nuse Cake\\TestSuite\\TestCase;\nuse IdeHelper\\Annotation\\MethodAnnota"
  },
  {
    "path": "tests/TestCase/Annotation/UsesAnnotationTest.php",
    "chars": 2830,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotation;\n\nuse IdeHelper\\Annotation\\PropertyAnnotation;\nuse IdeHelper\\Annotat"
  },
  {
    "path": "tests/TestCase/Annotation/VariableAnnotationTest.php",
    "chars": 3807,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotation;\n\nuse Cake\\TestSuite\\TestCase;\nuse IdeHelper\\Annotation\\MethodAnnota"
  },
  {
    "path": "tests/TestCase/Annotator/CallbackAnnotatorTask/VirtualFieldCallbackAnnotatorTaskTest.php",
    "chars": 2583,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotator\\CallbackAnnotatorTask;\n\nuse Cake\\Console\\ConsoleIo;\nuse IdeHelper\\Ann"
  },
  {
    "path": "tests/TestCase/Annotator/CallbackAnnotatorTest.php",
    "chars": 1950,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotator;\n\nuse Cake\\Console\\ConsoleIo;\nuse Cake\\TestSuite\\TestCase;\nuse IdeHel"
  },
  {
    "path": "tests/TestCase/Annotator/ClassAnnotatorTask/FormClassAnnotatorTaskTest.php",
    "chars": 3502,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotator\\ClassAnnotatorTask;\n\nuse Cake\\Console\\ConsoleIo;\nuse IdeHelper\\Annota"
  },
  {
    "path": "tests/TestCase/Annotator/ClassAnnotatorTask/MailerClassAnnotatorTaskTest.php",
    "chars": 4838,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotator\\ClassAnnotatorTask;\n\nuse Cake\\Console\\ConsoleIo;\nuse IdeHelper\\Annota"
  },
  {
    "path": "tests/TestCase/Annotator/ClassAnnotatorTask/TableFindAnnotatorTaskTest.php",
    "chars": 3420,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotator\\ClassAnnotatorTask;\n\nuse Cake\\Console\\ConsoleIo;\nuse IdeHelper\\Annota"
  },
  {
    "path": "tests/TestCase/Annotator/ClassAnnotatorTask/TestClassAnnotatorTaskTest.php",
    "chars": 4479,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotator\\ClassAnnotatorTask;\n\nuse Cake\\Console\\ConsoleIo;\nuse Cake\\Core\\Config"
  },
  {
    "path": "tests/TestCase/Annotator/ClassAnnotatorTest.php",
    "chars": 1963,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotator;\n\nuse Cake\\Console\\ConsoleIo;\nuse Cake\\TestSuite\\TestCase;\nuse IdeHel"
  },
  {
    "path": "tests/TestCase/Annotator/CommandAnnotatorTest.php",
    "chars": 1959,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotator;\n\nuse Cake\\Console\\ConsoleIo;\nuse Cake\\TestSuite\\TestCase;\nuse IdeHel"
  },
  {
    "path": "tests/TestCase/Annotator/ComponentAnnotatorTest.php",
    "chars": 3671,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotator;\n\nuse Cake\\Console\\ConsoleIo;\nuse Cake\\TestSuite\\TestCase;\nuse IdeHel"
  },
  {
    "path": "tests/TestCase/Annotator/ControllerAnnotatorTest.php",
    "chars": 7565,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotator;\n\nuse Cake\\Console\\ConsoleIo;\nuse Cake\\TestSuite\\TestCase;\nuse IdeHel"
  },
  {
    "path": "tests/TestCase/Annotator/DiffHelperTrait.php",
    "chars": 1248,
    "preview": "<?php\n\nnamespace IdeHelper\\Test\\TestCase\\Annotator;\n\nuse SebastianBergmann\\Diff\\Differ;\nuse SebastianBergmann\\Diff\\Outpu"
  }
]

// ... and 278 more files (download for full content)

About this extraction

This page contains the full source code of the dereuromark/cakephp-ide-helper GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 478 files (935.6 KB), approximately 281.9k tokens, and a symbol index with 1488 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!