├── LICENSE
├── README.md
├── annotate-watcher.cjs
├── composer.json
├── config
└── app.example.php
├── docs
├── Annotations.md
├── CodeCompletion.md
├── Contributing.md
├── Generator.md
├── Illuminator.md
├── README.md
├── img
│ ├── code_completion.png
│ ├── configure_autocomplete.png
│ ├── fields_autocomplete.png
│ ├── model_autocomplete.png
│ ├── model_autocomplete_finder.png
│ ├── model_autocomplete_loadmodel.png
│ ├── model_typehinting.png
│ └── validation_autocomplete_validator_require_presence.png
└── screenshot.jpg
├── phpcs.xml
├── phpstan.neon
├── 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
│ │ └── 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
│ │ ├── 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
│ │ └── EntityFieldTask.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
│ │ └── 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
│ ├── 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
│ │ └── EntityFieldTaskTest.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
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Mark Sch.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/annotate-watcher.cjs:
--------------------------------------------------------------------------------
1 | // watcher.js
2 | const chokidar = require('chokidar');
3 | const { exec } = require('child_process');
4 |
5 | // Helper to parse CLI arguments
6 | function getPathsFromArgs() {
7 | const arg = process.argv.find(arg => arg.startsWith('--path='));
8 | if (!arg) return ['src/', 'templates/']; // default path
9 | const value = arg.split('=')[1];
10 | return value.split(',').map(p => p.trim()).filter(Boolean);
11 | }
12 |
13 | const watchPaths = getPathsFromArgs();
14 | console.log(`🔍 Watching: ${watchPaths.join(', ')}`);
15 |
16 | // Initialize watcher
17 | chokidar.watch(watchPaths, {
18 | ignored: /(^|[\/\\])\../, // ignore dotfiles
19 | persistent: true,
20 | })
21 | .on('change', path => {
22 | if (!path.endsWith('.php')) return; // skip non-PHP files
23 |
24 | console.log(`📝 File changed: ${path}`);
25 |
26 | // Run CakePHP command
27 | exec('bin/cake annotate all --file ' + `${path}`, (error, stdout, stderr) => {
28 | if (error) {
29 | console.error(`❌ Error executing CakePHP command: ${error.message}`);
30 | return;
31 | }
32 | if (stderr) {
33 | console.error(`⚠️ STDERR: ${stderr}`);
34 | }
35 | console.log(`✅ Output:\n${stdout}`);
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/docs/Contributing.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Basics
4 | See composer scripts for
5 | ```
6 | composer cs-check
7 | composer cs-fix
8 | composer test-setup
9 | composer test
10 | ```
11 | etc
12 |
13 | ## Generator
14 |
15 | ### New meta file
16 |
17 | Run the test with the `--debug` option to generate a new `TMP/.meta.php`:
18 | ```
19 | php phpunit.phar tests/TestCase/Generator/PhpstormGeneratorTest.php --debug
20 | ```
21 | This way you can easily copy it over into the test_files/ directory and replace the existing one.
22 |
--------------------------------------------------------------------------------
/docs/img/code_completion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/8660d61ce4ef157aec648d9b21b53814b84efb33/docs/img/code_completion.png
--------------------------------------------------------------------------------
/docs/img/configure_autocomplete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/8660d61ce4ef157aec648d9b21b53814b84efb33/docs/img/configure_autocomplete.png
--------------------------------------------------------------------------------
/docs/img/fields_autocomplete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/8660d61ce4ef157aec648d9b21b53814b84efb33/docs/img/fields_autocomplete.png
--------------------------------------------------------------------------------
/docs/img/model_autocomplete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/8660d61ce4ef157aec648d9b21b53814b84efb33/docs/img/model_autocomplete.png
--------------------------------------------------------------------------------
/docs/img/model_autocomplete_finder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/8660d61ce4ef157aec648d9b21b53814b84efb33/docs/img/model_autocomplete_finder.png
--------------------------------------------------------------------------------
/docs/img/model_autocomplete_loadmodel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/8660d61ce4ef157aec648d9b21b53814b84efb33/docs/img/model_autocomplete_loadmodel.png
--------------------------------------------------------------------------------
/docs/img/model_typehinting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/8660d61ce4ef157aec648d9b21b53814b84efb33/docs/img/model_typehinting.png
--------------------------------------------------------------------------------
/docs/img/validation_autocomplete_validator_require_presence.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/8660d61ce4ef157aec648d9b21b53814b84efb33/docs/img/validation_autocomplete_validator_require_presence.png
--------------------------------------------------------------------------------
/docs/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/8660d61ce4ef157aec648d9b21b53814b84efb33/docs/screenshot.jpg
--------------------------------------------------------------------------------
/phpcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | config/
7 | src/
8 | tests/
9 | /tests/test_files/
10 | /tests/test_app/
11 |
12 |
13 |
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | level: 8
3 | paths:
4 | - src/
5 |
6 | treatPhpDocTypesAsCertain: false
7 | bootstrapFiles:
8 | - tests/bootstrap.php
9 | - tests/shim.php
10 | ignoreErrors:
11 | - '#Unsafe usage of new static\(\).+#'
12 | - '#Parameter \#1 \$object of function get_class expects object, object\|string given.#'
13 |
--------------------------------------------------------------------------------
/src/Annotation/AbstractAnnotation.php:
--------------------------------------------------------------------------------
1 | type = $type;
29 | $this->index = $index;
30 | }
31 |
32 | /**
33 | * @return string
34 | */
35 | public function __toString(): string {
36 | return static::TAG . ' ' . $this->build();
37 | }
38 |
39 | /**
40 | * @return string
41 | */
42 | public function getType(): string {
43 | return $this->type;
44 | }
45 |
46 | /**
47 | * @return bool
48 | */
49 | public function hasIndex(): bool {
50 | return $this->index !== null;
51 | }
52 |
53 | /**
54 | * @throws \RuntimeException
55 | * @return int
56 | */
57 | public function getIndex(): int {
58 | $index = $this->index;
59 | if ($index === null) {
60 | throw new RuntimeException('You cannot get an non-defined index. You can check with hasIndex() before calling this method.');
61 | }
62 |
63 | return $index;
64 | }
65 |
66 | /**
67 | * @param bool $inUse
68 | *
69 | * @return void
70 | */
71 | public function setInUse(bool $inUse = true): void {
72 | $this->isInUse = $inUse;
73 | }
74 |
75 | /**
76 | * @return bool
77 | */
78 | public function isInUse(): bool {
79 | return $this->isInUse;
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/src/Annotation/AnnotationInterface.php:
--------------------------------------------------------------------------------
1 | ');
22 | if ($closingBracket) {
23 | $description = substr($type, $closingBracket + 1);
24 | $description = trim($description);
25 | $type = substr($type, 0, $closingBracket + 1);
26 | }
27 |
28 | parent::__construct($type, $index);
29 |
30 | $this->description = $description;
31 | }
32 |
33 | /**
34 | * @return string
35 | */
36 | public function getDescription(): string {
37 | return $this->description;
38 | }
39 |
40 | /**
41 | * @return string
42 | */
43 | public function build(): string {
44 | $description = $this->description !== '' ? (' ' . $this->description) : '';
45 |
46 | return $this->type . $description;
47 | }
48 |
49 | /**
50 | * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\ExtendsAnnotation $annotation
51 | *
52 | * @return bool
53 | */
54 | public function matches(AbstractAnnotation $annotation): bool {
55 | if (!$annotation instanceof self) {
56 | return false;
57 | }
58 |
59 | // Always matches as there can only be one per docblock
60 | return true;
61 | }
62 |
63 | /**
64 | * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\ExtendsAnnotation $annotation
65 | * @return void
66 | */
67 | public function replaceWith(AbstractAnnotation $annotation): void {
68 | $this->type = $annotation->getType();
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/Annotation/LinkAnnotation.php:
--------------------------------------------------------------------------------
1 | description = $description;
27 | }
28 |
29 | /**
30 | * @return string
31 | */
32 | public function getDescription(): string {
33 | return $this->description;
34 | }
35 |
36 | /**
37 | * @return string
38 | */
39 | public function build(): string {
40 | $description = $this->description !== '' ? (' ' . $this->description) : '';
41 |
42 | return $this->type . $description;
43 | }
44 |
45 | /**
46 | * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MixinAnnotation $annotation
47 | *
48 | * @return bool
49 | */
50 | public function matches(AbstractAnnotation $annotation): bool {
51 | if (!$annotation instanceof self) {
52 | return false;
53 | }
54 | if ($annotation->getType() !== $this->type) {
55 | return false;
56 | }
57 |
58 | return true;
59 | }
60 |
61 | /**
62 | * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MixinAnnotation $annotation
63 | * @return void
64 | */
65 | public function replaceWith(AbstractAnnotation $annotation): void {
66 | $this->type = $annotation->getType();
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/Annotation/MethodAnnotation.php:
--------------------------------------------------------------------------------
1 | method = $method;
32 | $this->description = $description;
33 | }
34 |
35 | /**
36 | * @return string
37 | */
38 | public function getMethod(): string {
39 | return $this->method;
40 | }
41 |
42 | /**
43 | * @return string
44 | */
45 | public function getDescription(): string {
46 | return $this->description;
47 | }
48 |
49 | /**
50 | * @return string
51 | */
52 | public function build(): string {
53 | $description = $this->description !== '' ? (' ' . $this->description) : '';
54 |
55 | return $this->type . ' ' . $this->method . $description;
56 | }
57 |
58 | /**
59 | * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MethodAnnotation $annotation
60 | *
61 | * @return bool
62 | */
63 | public function matches(AbstractAnnotation $annotation): bool {
64 | if (!$annotation instanceof self) {
65 | return false;
66 | }
67 | $methodName = substr($annotation->getMethod(), 0, strpos($annotation->getMethod(), '(') ?: 0);
68 | if ($methodName !== substr($this->method, 0, strpos($this->method, '(') ?: 0)) {
69 | return false;
70 | }
71 |
72 | return true;
73 | }
74 |
75 | /**
76 | * @param \IdeHelper\Annotation\MethodAnnotation $annotation
77 | * @return void
78 | */
79 | public function replaceWith(AbstractAnnotation $annotation): void {
80 | $this->type = $annotation->getType();
81 | $this->method = $annotation->getMethod();
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/Annotation/MixinAnnotation.php:
--------------------------------------------------------------------------------
1 | description = $description;
27 | }
28 |
29 | /**
30 | * @return string
31 | */
32 | public function getDescription(): string {
33 | return $this->description;
34 | }
35 |
36 | /**
37 | * @return string
38 | */
39 | public function build(): string {
40 | $description = $this->description !== '' ? (' ' . $this->description) : '';
41 |
42 | return $this->type . $description;
43 | }
44 |
45 | /**
46 | * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MixinAnnotation $annotation
47 | *
48 | * @return bool
49 | */
50 | public function matches(AbstractAnnotation $annotation): bool {
51 | if (!$annotation instanceof self) {
52 | return false;
53 | }
54 | if ($annotation->getType() !== $this->type) {
55 | return false;
56 | }
57 |
58 | return true;
59 | }
60 |
61 | /**
62 | * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MixinAnnotation $annotation
63 | * @return void
64 | */
65 | public function replaceWith(AbstractAnnotation $annotation): void {
66 | $this->type = $annotation->getType();
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/Annotation/ParamAnnotation.php:
--------------------------------------------------------------------------------
1 | variable = $variable;
30 | $this->description = $description;
31 | }
32 |
33 | /**
34 | * @return string
35 | */
36 | public function getVariable(): string {
37 | return $this->variable;
38 | }
39 |
40 | /**
41 | * @return string
42 | */
43 | public function getDescription(): string {
44 | return $this->description;
45 | }
46 |
47 | /**
48 | * @return string
49 | */
50 | public function build(): string {
51 | $description = $this->description !== '' ? (' ' . $this->description) : '';
52 |
53 | return $this->type . ' ' . $this->variable . $description;
54 | }
55 |
56 | /**
57 | * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\ParamAnnotation $annotation
58 | *
59 | * @return bool
60 | */
61 | public function matches(AbstractAnnotation $annotation): bool {
62 | if (!$annotation instanceof self) {
63 | return false;
64 | }
65 | if ($annotation->getVariable() !== $this->variable) {
66 | return false;
67 | }
68 |
69 | return true;
70 | }
71 |
72 | /**
73 | * @param \IdeHelper\Annotation\ParamAnnotation $annotation
74 | * @return void
75 | */
76 | public function replaceWith(AbstractAnnotation $annotation): void {
77 | $this->type = $annotation->getType();
78 | $this->variable = $annotation->getVariable();
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/src/Annotation/PropertyAnnotation.php:
--------------------------------------------------------------------------------
1 | property = $property;
33 | $this->description = $description;
34 | }
35 |
36 | /**
37 | * @return string
38 | */
39 | public function getProperty(): string {
40 | return $this->property;
41 | }
42 |
43 | /**
44 | * @return string
45 | */
46 | public function getDescription(): string {
47 | return $this->description;
48 | }
49 |
50 | /**
51 | * @return string
52 | */
53 | public function build(): string {
54 | $description = $this->description !== '' ? (' ' . $this->description) : '';
55 |
56 | return $this->type . ' ' . $this->property . $description;
57 | }
58 |
59 | /**
60 | * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\PropertyAnnotation $annotation
61 | *
62 | * @return bool
63 | */
64 | public function matches(AbstractAnnotation $annotation): bool {
65 | if (!$annotation instanceof self) {
66 | return false;
67 | }
68 | if ($annotation->getProperty() !== $this->property) {
69 | return false;
70 | }
71 |
72 | return true;
73 | }
74 |
75 | /**
76 | * @param \IdeHelper\Annotation\PropertyAnnotation $annotation
77 | * @return void
78 | */
79 | public function replaceWith(AbstractAnnotation $annotation): void {
80 | $this->type = $annotation->getType();
81 | $this->property = $annotation->getProperty();
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/Annotation/PropertyReadAnnotation.php:
--------------------------------------------------------------------------------
1 | description = $description;
27 | }
28 |
29 | /**
30 | * @return string
31 | */
32 | public function getDescription(): string {
33 | return $this->description;
34 | }
35 |
36 | /**
37 | * @return string
38 | */
39 | public function build(): string {
40 | $description = $this->description !== '' ? (' ' . $this->description) : '';
41 |
42 | return $this->type . $description;
43 | }
44 |
45 | /**
46 | * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MixinAnnotation $annotation
47 | *
48 | * @return bool
49 | */
50 | public function matches(AbstractAnnotation $annotation): bool {
51 | if (!$annotation instanceof self) {
52 | return false;
53 | }
54 | if ($annotation->getType() !== $this->type) {
55 | return false;
56 | }
57 |
58 | return true;
59 | }
60 |
61 | /**
62 | * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MixinAnnotation $annotation
63 | * @return void
64 | */
65 | public function replaceWith(AbstractAnnotation $annotation): void {
66 | $this->type = $annotation->getType();
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/Annotation/UsesAnnotation.php:
--------------------------------------------------------------------------------
1 | description = $description;
27 | }
28 |
29 | /**
30 | * @return string
31 | */
32 | public function getDescription(): string {
33 | return $this->description;
34 | }
35 |
36 | /**
37 | * @return string
38 | */
39 | public function build(): string {
40 | $description = $this->description !== '' ? (' ' . $this->description) : '';
41 |
42 | return $this->type . $description;
43 | }
44 |
45 | /**
46 | * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MixinAnnotation $annotation
47 | *
48 | * @return bool
49 | */
50 | public function matches(AbstractAnnotation $annotation): bool {
51 | if (!$annotation instanceof self) {
52 | return false;
53 | }
54 | if ($annotation->getType() !== $this->type) {
55 | return false;
56 | }
57 |
58 | return true;
59 | }
60 |
61 | /**
62 | * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MixinAnnotation $annotation
63 | * @return void
64 | */
65 | public function replaceWith(AbstractAnnotation $annotation): void {
66 | $this->type = $annotation->getType();
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/Annotation/VariableAnnotation.php:
--------------------------------------------------------------------------------
1 | variable = $variable;
31 | $this->description = $description;
32 | }
33 |
34 | /**
35 | * @return string
36 | */
37 | public function getVariable(): string {
38 | return $this->variable;
39 | }
40 |
41 | /**
42 | * @return string
43 | */
44 | public function getDescription(): string {
45 | return $this->description;
46 | }
47 |
48 | /**
49 | * @param bool $value
50 | *
51 | * @return $this
52 | */
53 | public function setGuessed($value) {
54 | $this->guessed = $value;
55 |
56 | return $this;
57 | }
58 |
59 | /**
60 | * @return bool
61 | */
62 | public function getGuessed() {
63 | return $this->guessed;
64 | }
65 |
66 | /**
67 | * @return string
68 | */
69 | public function build(): string {
70 | $description = $this->description !== '' ? (' ' . $this->description) : '';
71 |
72 | return $this->type . ' ' . $this->variable . $description;
73 | }
74 |
75 | /**
76 | * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\VariableAnnotation $annotation
77 | *
78 | * @return bool
79 | */
80 | public function matches(AbstractAnnotation $annotation): bool {
81 | if (!$annotation instanceof self) {
82 | return false;
83 | }
84 | if ($annotation->getVariable() !== $this->variable) {
85 | return false;
86 | }
87 |
88 | return true;
89 | }
90 |
91 | /**
92 | * @param \IdeHelper\Annotation\VariableAnnotation $annotation
93 | * @return void
94 | */
95 | public function replaceWith(AbstractAnnotation $annotation): void {
96 | $this->type = $annotation->getType();
97 | $this->variable = $annotation->getVariable();
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/src/Annotator/CallbackAnnotator.php:
--------------------------------------------------------------------------------
1 | invokeTasks($path, $content);
20 |
21 | return true;
22 | }
23 |
24 | /**
25 | * @param string $path
26 | * @param string $content
27 | *
28 | * @return void
29 | */
30 | protected function invokeTasks(string $path, string $content): void {
31 | $tasks = $this->getTasks($path, $content);
32 |
33 | foreach ($tasks as $task) {
34 | if (!$task->shouldRun($path)) {
35 | continue;
36 | }
37 |
38 | $task->annotate($path);
39 | }
40 | }
41 |
42 | /**
43 | * @param string $path
44 | * @param string $content
45 | * @return array<\IdeHelper\Annotator\CallbackAnnotatorTask\CallbackAnnotatorTaskInterface>
46 | */
47 | protected function getTasks(string $path, string $content): array {
48 | $taskCollection = new CallbackAnnotatorTaskCollection();
49 |
50 | return $taskCollection->tasks($this->_io, $this->_config, $path, $content);
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/Annotator/CallbackAnnotatorTask/CallbackAnnotatorTaskInterface.php:
--------------------------------------------------------------------------------
1 | , class-string<\IdeHelper\Annotator\CallbackAnnotatorTask\CallbackAnnotatorTaskInterface>>
14 | */
15 | protected array $defaultTasks = [
16 | TableCallbackAnnotatorTask::class => TableCallbackAnnotatorTask::class,
17 | VirtualFieldCallbackAnnotatorTask::class => VirtualFieldCallbackAnnotatorTask::class,
18 | ];
19 |
20 | /**
21 | * @var array>
22 | */
23 | protected array $tasks;
24 |
25 | /**
26 | * @param array> $tasks
27 | */
28 | public function __construct(array $tasks = []) {
29 | $defaultTasks = $this->defaultTasks();
30 | $tasks += $defaultTasks;
31 |
32 | foreach ($tasks as $task) {
33 | if (!$task) {
34 | continue;
35 | }
36 |
37 | $this->tasks = $tasks;
38 | }
39 | }
40 |
41 | /**
42 | * @return array>
43 | */
44 | public function defaultTasks(): array {
45 | $tasks = (array)Configure::read('IdeHelper.callbackAnnotatorTasks') + $this->defaultTasks;
46 |
47 | foreach ($tasks as $k => $v) {
48 | if (is_numeric($k)) {
49 | $tasks[$v] = $v;
50 | unset($tasks[$k]);
51 | }
52 | }
53 |
54 | return $tasks;
55 | }
56 |
57 | /**
58 | * @param \IdeHelper\Console\Io $io
59 | * @param array $config
60 | * @param string $path
61 | * @param string $content
62 | * @return array<\IdeHelper\Annotator\CallbackAnnotatorTask\CallbackAnnotatorTaskInterface>
63 | */
64 | public function tasks(Io $io, array $config, string $path, string $content): array {
65 | $tasks = $this->tasks;
66 |
67 | $collection = [];
68 | foreach ($tasks as $task) {
69 | /** @var \IdeHelper\Annotator\CallbackAnnotatorTask\CallbackAnnotatorTaskInterface $object */
70 | $object = new $task($io, $config, $path, $content);
71 | $collection[] = $object;
72 | }
73 |
74 | return $collection;
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/src/Annotator/ClassAnnotator.php:
--------------------------------------------------------------------------------
1 | invokeTasks($path, $content);
20 |
21 | return true;
22 | }
23 |
24 | /**
25 | * @param string $path
26 | * @param string $content
27 | *
28 | * @return void
29 | */
30 | protected function invokeTasks(string $path, string $content): void {
31 | $tasks = $this->getTasks($content);
32 |
33 | foreach ($tasks as $task) {
34 | if (!$task->shouldRun($path, $content)) {
35 | continue;
36 | }
37 |
38 | $task->annotate($path);
39 | }
40 | }
41 |
42 | /**
43 | * @param string $content
44 | * @return array<\IdeHelper\Annotator\ClassAnnotatorTask\ClassAnnotatorTaskInterface>
45 | */
46 | protected function getTasks(string $content): array {
47 | $taskCollection = new ClassAnnotatorTaskCollection();
48 |
49 | return $taskCollection->tasks($this->_io, $this->_config, $content);
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/Annotator/ClassAnnotatorTask/ClassAnnotatorTaskInterface.php:
--------------------------------------------------------------------------------
1 | content instead.
9 | *
10 | * @param string $path
11 | * @param string $content
12 | * @return bool
13 | */
14 | public function shouldRun(string $path, string $content): bool;
15 |
16 | /**
17 | * @param string $path Path to file.
18 | * @return bool
19 | */
20 | public function annotate(string $path): bool;
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/Annotator/ClassAnnotatorTask/FormClassAnnotatorTask.php:
--------------------------------------------------------------------------------
1 | content instead.
16 | *
17 | * @param string $path
18 | * @param string $content
19 | * @return bool
20 | */
21 | public function shouldRun(string $path, string $content): bool {
22 | if (!str_contains($path, DS . 'src' . DS)) {
23 | return false;
24 | }
25 |
26 | $appNamespace = Configure::read('App.namespace') ?: 'App';
27 | if (!preg_match('#\buse (\w+)\\\\Form\\\\(.+)Form\b#', $content, $matches)) {
28 | return false;
29 | }
30 |
31 | $varName = lcfirst($matches[2]) . 'Form';
32 | if (!preg_match('#\$' . $varName . '->execute\(#', $content)) {
33 | return false;
34 | }
35 |
36 | return true;
37 | }
38 |
39 | /**
40 | * @param string $path
41 | * @return bool
42 | */
43 | public function annotate(string $path): bool {
44 | preg_match('#\buse (\w+)\\\\Form\\\\(.+)Form\b#', $this->content, $matches);
45 | if (empty($matches[1]) || empty($matches[2])) {
46 | return false;
47 | }
48 |
49 | $appNamespace = $matches[1];
50 | $name = $matches[2] . 'Form';
51 |
52 | $varName = lcfirst($name);
53 | $rows = explode(PHP_EOL, $this->content);
54 | $rowToAnnotate = null;
55 | foreach ($rows as $i => $row) {
56 | if (!preg_match('#\$' . $varName . '->execute\(#', $row)) {
57 | continue;
58 | }
59 | $rowToAnnotate = $i + 1;
60 |
61 | break;
62 | }
63 |
64 | if (!$rowToAnnotate) {
65 | return false;
66 | }
67 |
68 | $method = $appNamespace . '\\Form\\' . $name . '::_execute()';
69 | $annotations = $this->buildUsesAnnotations([$method]);
70 |
71 | return $this->annotateInlineContent($path, $this->content, $annotations, $rowToAnnotate);
72 | }
73 |
74 | /**
75 | * @param array $classes
76 | * @return array<\IdeHelper\Annotation\AbstractAnnotation>
77 | */
78 | protected function buildUsesAnnotations(array $classes): array {
79 | $annotations = [];
80 |
81 | foreach ($classes as $className) {
82 | $annotations[] = AnnotationFactory::createOrFail(UsesAnnotation::TAG, '\\' . $className);
83 | }
84 |
85 | return $annotations;
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/src/Annotator/ClassAnnotatorTask/ModelAwareClassAnnotatorTask.php:
--------------------------------------------------------------------------------
1 | content instead.
15 | *
16 | * @param string $path
17 | * @param string $content
18 | * @return bool
19 | */
20 | public function shouldRun(string $path, string $content): bool {
21 | if (!str_contains($path, DS . 'src' . DS)) {
22 | return false;
23 | }
24 | if (preg_match('#\buse ModelAwareTrait\b#', $content)) {
25 | return true;
26 | }
27 |
28 | /** @var class-string|null $className */
29 | $className = $this->getClassName($path, $content);
30 | if (!$className) {
31 | return false;
32 | }
33 |
34 | try {
35 | return (new ReflectionClass($className))->hasMethod('fetchModel');
36 | } catch (Throwable $exception) {
37 | return false;
38 | }
39 | }
40 |
41 | /**
42 | * @param string $path
43 | * @return bool
44 | */
45 | public function annotate(string $path): bool {
46 | $models = $this->getUsedModels($this->content);
47 |
48 | $annotations = $this->getModelAnnotations($models, $this->content);
49 |
50 | return $this->annotateContent($path, $this->content, $annotations);
51 | }
52 |
53 | /**
54 | * @param string $content
55 | *
56 | * @return array
57 | */
58 | protected function getUsedModels(string $content): array {
59 | preg_match_all('/\$this-\>fetchModel\(\'([a-z.]+)\'/i', $content, $matches);
60 | if (empty($matches[1])) {
61 | return [];
62 | }
63 |
64 | $models = $matches[1];
65 |
66 | return array_unique($models);
67 | }
68 |
69 | /**
70 | * @param string $path
71 | * @param string $content
72 | *
73 | * @return string|null
74 | */
75 | protected function getClassName(string $path, string $content): ?string {
76 | preg_match('#^namespace (.+)\b#m', $content, $matches);
77 | if (!$matches) {
78 | return null;
79 | }
80 |
81 | $className = pathinfo($path, PATHINFO_FILENAME);
82 |
83 | return $matches[1] . '\\' . $className;
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/src/Annotator/CommandAnnotator.php:
--------------------------------------------------------------------------------
1 | getPrimaryModelClass($content);
27 | $usedModels = $this->getUsedModels($content);
28 | if ($primaryModelClass) {
29 | $usedModels[] = $primaryModelClass;
30 | }
31 | $usedModels = array_unique($usedModels);
32 |
33 | $annotations = $this->getModelAnnotations($usedModels, $content);
34 |
35 | return $this->annotateContent($path, $content, $annotations);
36 | }
37 |
38 | /**
39 | * @param string $content
40 | *
41 | * @return string|null
42 | */
43 | protected function getPrimaryModelClass(string $content): ?string {
44 | if (!preg_match('/\bprotected \?string \$defaultTable = \'([a-z.\/]+)\'/i', $content, $matches)) {
45 | return null;
46 | }
47 |
48 | $modelName = $matches[1];
49 |
50 | return $modelName;
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/Annotator/HelperAnnotator.php:
--------------------------------------------------------------------------------
1 | getConfig(static::CONFIG_PLUGIN);
29 |
30 | /** @phpstan-var class-string