├── docs
├── screenshot.jpg
├── img
│ ├── code_completion.png
│ ├── model_autocomplete.png
│ ├── model_typehinting.png
│ ├── fields_autocomplete.png
│ ├── configure_autocomplete.png
│ ├── model_autocomplete_finder.png
│ ├── model_autocomplete_loadmodel.png
│ └── validation_autocomplete_validator_require_presence.png
└── Contributing.md
├── src
├── Generator
│ ├── GeneratorInterface.php
│ ├── Task
│ │ ├── TaskInterface.php
│ │ ├── ConsoleTask.php
│ │ ├── ConnectionTask.php
│ │ ├── TableAssociationTask.php
│ │ ├── PluginTask.php
│ │ ├── DatabaseTypeTask.php
│ │ ├── ValidationTask.php
│ │ ├── FormHelperTask.php
│ │ ├── EnvTask.php
│ │ ├── CacheTask.php
│ │ ├── LayoutTask.php
│ │ ├── MailerTask.php
│ │ ├── ElementTask.php
│ │ ├── DatabaseTableColumnNameTask.php
│ │ └── ConfigureTask.php
│ ├── Directive
│ │ ├── ExitPoint.php
│ │ ├── Override.php
│ │ ├── ExpectedReturnValues.php
│ │ ├── RegisterArgumentsSet.php
│ │ ├── BaseDirective.php
│ │ └── ExpectedArguments.php
│ └── PhpstormGenerator.php
├── Annotation
│ ├── PropertyReadAnnotation.php
│ ├── AnnotationInterface.php
│ ├── ReplacableAnnotationInterface.php
│ ├── SeeAnnotation.php
│ ├── MixinAnnotation.php
│ ├── UsesAnnotation.php
│ ├── AbstractAnnotation.php
│ ├── LinkAnnotation.php
│ ├── ExtendsAnnotation.php
│ ├── ParamAnnotation.php
│ ├── PropertyAnnotation.php
│ └── MethodAnnotation.php
├── CodeCompletion
│ ├── Task
│ │ ├── TaskInterface.php
│ │ ├── ViewEventsTask.php
│ │ └── ModelEventsTask.php
│ └── CodeCompletionGenerator.php
├── Annotator
│ ├── CallbackAnnotatorTask
│ │ └── CallbackAnnotatorTaskInterface.php
│ ├── ClassAnnotatorTask
│ │ ├── ClassAnnotatorTaskInterface.php
│ │ ├── ModelAwareClassAnnotatorTask.php
│ │ └── FormClassAnnotatorTask.php
│ ├── Traits
│ │ ├── HelperTrait.php
│ │ ├── ModelTrait.php
│ │ ├── ComponentTrait.php
│ │ └── FileTrait.php
│ ├── ClassAnnotator.php
│ ├── CallbackAnnotator.php
│ ├── CommandAnnotator.php
│ ├── HelperAnnotator.php
│ ├── CallbackAnnotatorTaskCollection.php
│ └── ClassAnnotatorTaskCollection.php
├── ValueObject
│ ├── ValueObjectInterface.php
│ ├── LiteralName.php
│ ├── StringName.php
│ ├── ClassName.php
│ ├── ArgumentsSet.php
│ └── KeyValue.php
├── Utility
│ ├── AppPath.php
│ ├── Plugin.php
│ ├── CollectionClass.php
│ ├── PluginPath.php
│ ├── TranslationParser.php
│ ├── GenericString.php
│ ├── ControllerActionParser.php
│ └── App.php
├── Illuminator
│ ├── Illuminator.php
│ └── Task
│ │ └── AbstractTask.php
├── Command
│ ├── Annotate
│ │ ├── ViewCommand.php
│ │ ├── ModelsCommand.php
│ │ ├── CommandsCommand.php
│ │ ├── HelpersCommand.php
│ │ ├── ComponentsCommand.php
│ │ ├── ControllersCommand.php
│ │ └── AllCommand.php
│ └── GenerateCodeCompletionCommand.php
└── IdeHelperPlugin.php
├── tests
├── phpstan.neon
├── TestCase
│ ├── Utility
│ │ ├── AppPathTest.php
│ │ ├── AppTest.php
│ │ ├── PluginPathTest.php
│ │ ├── TranslationParserTest.php
│ │ ├── GenericStringTest.php
│ │ └── PluginTest.php
│ ├── Command
│ │ ├── IlluminateCommandTest.php
│ │ └── GenerateCodeCompletionCommandTest.php
│ ├── ValueObject
│ │ ├── KeyValueTest.php
│ │ ├── LiteralNameTest.php
│ │ ├── StringNameTest.php
│ │ ├── DoubleQuoteStringNameTest.php
│ │ └── ClassNameTest.php
│ ├── Generator
│ │ ├── TaskCollectionTest.php
│ │ ├── Directive
│ │ │ ├── ExitPointTest.php
│ │ │ ├── ExpectedArgumentsTest.php
│ │ │ ├── ExpectedReturnValuesTest.php
│ │ │ ├── OverrideTest.php
│ │ │ └── RegisterArgumentsSetTest.php
│ │ ├── Task
│ │ │ ├── ConsoleTaskTest.php
│ │ │ ├── CellTaskTest.php
│ │ │ ├── MailerTaskTest.php
│ │ │ ├── ConnectionTaskTest.php
│ │ │ ├── LayoutTaskTest.php
│ │ │ ├── ConsoleHelperTaskTest.php
│ │ │ ├── EnvTaskTest.php
│ │ │ ├── ComponentTaskTest.php
│ │ │ ├── ElementTaskTest.php
│ │ │ ├── CacheTaskTest.php
│ │ │ ├── FixtureTaskTest.php
│ │ │ ├── PluginTaskTest.php
│ │ │ ├── BehaviorTaskTest.php
│ │ │ ├── DatabaseTypeTaskTest.php
│ │ │ ├── RequestTaskTest.php
│ │ │ ├── ValidationTaskTest.php
│ │ │ ├── EntityTaskTest.php
│ │ │ ├── HelperTaskTest.php
│ │ │ ├── FormHelperTaskTest.php
│ │ │ ├── ConfigureTaskTest.php
│ │ │ ├── RoutePathTaskTest.php
│ │ │ ├── ModelTaskTest.php
│ │ │ ├── TableAssociationTaskTest.php
│ │ │ └── DatabaseTableTaskTest.php
│ │ └── PhpstormGeneratorTest.php
│ ├── CodeCompletion
│ │ ├── TaskCollectionTest.php
│ │ └── Task
│ │ │ ├── BehaviorTaskTest.php
│ │ │ └── ViewEventsTaskTest.php
│ ├── Illuminator
│ │ └── IlluminatorTest.php
│ ├── Annotator
│ │ ├── DiffHelperTrait.php
│ │ ├── HelperAnnotatorTest.php
│ │ ├── CommandAnnotatorTest.php
│ │ ├── CallbackAnnotatorTest.php
│ │ └── ClassAnnotatorTest.php
│ └── Annotation
│ │ └── ParamAnnotationTest.php
├── shim.php
├── Fixture
│ ├── HousesFixture.php
│ ├── WindowsFixture.php
│ ├── WheelsFixture.php
│ ├── BarBarsFixture.php
│ ├── FoosFixture.php
│ └── CarsFixture.php
└── schema.php
├── phpcs.xml
├── phpstan.neon
├── LICENSE
└── annotate-watcher.cjs
/docs/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/HEAD/docs/screenshot.jpg
--------------------------------------------------------------------------------
/docs/img/code_completion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/HEAD/docs/img/code_completion.png
--------------------------------------------------------------------------------
/docs/img/model_autocomplete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/HEAD/docs/img/model_autocomplete.png
--------------------------------------------------------------------------------
/docs/img/model_typehinting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/HEAD/docs/img/model_typehinting.png
--------------------------------------------------------------------------------
/docs/img/fields_autocomplete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/HEAD/docs/img/fields_autocomplete.png
--------------------------------------------------------------------------------
/docs/img/configure_autocomplete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/HEAD/docs/img/configure_autocomplete.png
--------------------------------------------------------------------------------
/docs/img/model_autocomplete_finder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/HEAD/docs/img/model_autocomplete_finder.png
--------------------------------------------------------------------------------
/docs/img/model_autocomplete_loadmodel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/HEAD/docs/img/model_autocomplete_loadmodel.png
--------------------------------------------------------------------------------
/docs/img/validation_autocomplete_validator_require_presence.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dereuromark/cakephp-ide-helper/HEAD/docs/img/validation_autocomplete_validator_require_presence.png
--------------------------------------------------------------------------------
/src/Generator/GeneratorInterface.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | public function collect(): array;
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/tests/phpstan.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | level: 6
3 | paths:
4 | - TestCase/
5 | bootstrapFiles:
6 | - bootstrap.php
7 | - shim.php
8 | earlyTerminatingMethodCalls:
9 | Cake\Console\BaseCommand:
10 | - abort
11 | ignoreErrors:
12 | - identifier: missingType.iterableValue
13 |
--------------------------------------------------------------------------------
/src/CodeCompletion/Task/TaskInterface.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | config/
6 | src/
7 | tests/
8 | /tests/test_files/
9 | /tests/test_app/
10 |
11 |
12 |
--------------------------------------------------------------------------------
/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/Annotator/CallbackAnnotatorTask/CallbackAnnotatorTaskInterface.php:
--------------------------------------------------------------------------------
1 | assertCount(1, $result);
16 |
17 | $path = array_shift($result);
18 | $this->assertTextContains('/tests/test_app/src/View/Helper/', $path);
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/Annotation/ReplacableAnnotationInterface.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/ValueObject/ValueObjectInterface.php:
--------------------------------------------------------------------------------
1 | assertNull($result);
17 |
18 | $result = App::className('IdeHelper.DocBlock', 'View/Helper', 'Helper');
19 | $this->assertSame(DocBlockHelper::class, $result);
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/tests/TestCase/Command/IlluminateCommandTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['IdeHelper']);
17 | $this->exec('illuminate code -d -v');
18 |
19 | $this->assertExitCode(2);
20 | $this->assertOutputContains('# /src/Model/Entity/Foo.php');
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/Utility/AppPath.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | public static function get(string $type, ?string $plugin = null): array {
17 | try {
18 | return App::classPath($type, $plugin);
19 | } catch (MissingPluginException $exception) {
20 | }
21 |
22 | return App::classPath($type, $plugin);
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/tests/TestCase/ValueObject/KeyValueTest.php:
--------------------------------------------------------------------------------
1 | assertSame($key, $result->key());
21 | $this->assertSame($value, $result->value());
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/Generator/Task/ConsoleTask.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | public function collect(): array {
19 | $result = [];
20 |
21 | $directive = new ExitPoint(static::METHOD_ABORT);
22 | $result[$directive->key()] = $directive;
23 |
24 | return $result;
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/TaskCollectionTest.php:
--------------------------------------------------------------------------------
1 | taskCollection = new TaskCollection();
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | public function testTasks() {
25 | $result = $this->taskCollection->tasks();
26 |
27 | $this->assertNotEmpty($result);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/tests/TestCase/CodeCompletion/TaskCollectionTest.php:
--------------------------------------------------------------------------------
1 | taskCollection = new TaskCollection();
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | public function testTasks() {
25 | $result = $this->taskCollection->tasks();
26 |
27 | $this->assertNotEmpty($result);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/tests/shim.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | public static function all(): array {
14 | $plugins = static::loaded();
15 | $plugins = array_combine($plugins, $plugins);
16 |
17 | $pluginMap = (array)Configure::read('IdeHelper.plugins');
18 | foreach ($pluginMap as $plugin) {
19 | if (str_starts_with($plugin, '-')) {
20 | $plugin = substr($plugin, 1);
21 | unset($plugins[$plugin]);
22 |
23 | continue;
24 | }
25 |
26 | $plugins[$plugin] = $plugin;
27 | }
28 |
29 | return $plugins;
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Directive/ExitPointTest.php:
--------------------------------------------------------------------------------
1 | build();
18 | $expected = <<<'TXT'
19 | exitPoint(\Cake\Console\ConsoleIo::abort());
20 | TXT;
21 | $this->assertSame($expected, $result);
22 | $this->assertSame('\\' . ConsoleIo::class . '::abort()@exitPoint', $directive->key());
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/tests/TestCase/Utility/PluginPathTest.php:
--------------------------------------------------------------------------------
1 | assertTextContains('cakephp-ide-helper/vendor/dereuromark/cakephp-shim/', $result);
18 |
19 | Plugin::getCollection()->remove('Awesome');
20 |
21 | $plugin = 'Awesome';
22 | $result = PluginPath::get($plugin);
23 | $this->assertTextContains('cakephp-ide-helper/tests/test_app/plugins/Awesome/', $result);
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/tests/TestCase/ValueObject/LiteralNameTest.php:
--------------------------------------------------------------------------------
1 | assertSame($stringName, $result->raw());
17 | }
18 |
19 | /**
20 | * @return void
21 | */
22 | public function testToString(): void {
23 | $stringName = 'Foo.Baz';
24 | $object = LiteralName::create($stringName);
25 |
26 | $result = (string)$object;
27 | $this->assertSame($stringName, $result);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/Utility/CollectionClass.php:
--------------------------------------------------------------------------------
1 | assertSame($stringName, $result->raw());
17 | }
18 |
19 | /**
20 | * @return void
21 | */
22 | public function testToString(): void {
23 | $stringName = 'Foo.Baz';
24 | $object = StringName::create($stringName);
25 |
26 | $result = (string)$object;
27 | $this->assertSame('\'' . $stringName . '\'', $result);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/tests/TestCase/ValueObject/DoubleQuoteStringNameTest.php:
--------------------------------------------------------------------------------
1 | assertSame($stringName, $result->raw());
17 | }
18 |
19 | /**
20 | * @return void
21 | */
22 | public function testToString(): void {
23 | $stringName = 'Foo.Baz';
24 | $object = DoubleQuoteStringName::create($stringName);
25 |
26 | $result = (string)$object;
27 | $this->assertSame('"' . $stringName . '"', $result);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/Utility/PluginPath.php:
--------------------------------------------------------------------------------
1 | task = new ConsoleTask();
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | public function testCollect() {
25 | $result = $this->task->collect();
26 |
27 | $this->assertCount(1, $result);
28 |
29 | /** @var \IdeHelper\Generator\Directive\ExitPoint $directive */
30 | $directive = array_shift($result);
31 | $this->assertSame('\Cake\Console\ConsoleIo::abort()', $directive->toArray()['method']);
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/Annotator/Traits/HelperTrait.php:
--------------------------------------------------------------------------------
1 | assertSame($className, $result->raw());
17 |
18 | $result = ClassName::create('\\' . $className);
19 | $this->assertSame($className, $result->raw());
20 | }
21 |
22 | /**
23 | * @return void
24 | */
25 | public function testToString(): void {
26 | $className = 'Foo\\Bar\\Baz';
27 | $object = ClassName::create($className);
28 |
29 | $result = (string)$object;
30 | $this->assertSame('\\' . $className . '::class', $result);
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/ValueObject/LiteralName.php:
--------------------------------------------------------------------------------
1 | value = $value;
17 | }
18 |
19 | /**
20 | * Creates itself from a string.
21 | *
22 | * @param string $value
23 | *
24 | * @return static
25 | */
26 | public static function create(string $value) {
27 | return new static($value);
28 | }
29 |
30 | /**
31 | * @return string
32 | */
33 | public function raw(): string {
34 | return $this->value;
35 | }
36 |
37 | /**
38 | * @return string
39 | */
40 | public function __toString(): string {
41 | return $this->raw();
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Directive/ExpectedArgumentsTest.php:
--------------------------------------------------------------------------------
1 | build();
22 | $expected = <<<'TXT'
23 | expectedArguments(
24 | \Cake\ORM\Table::addBehavior(),
25 | 0,
26 | \Foo\Bar::class,
27 | "string",
28 | );
29 | TXT;
30 | $this->assertSame($expected, $result);
31 | $this->assertSame('\\' . Table::class . '::addBehavior()@0@expectedArguments', $directive->key());
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Directive/ExpectedReturnValuesTest.php:
--------------------------------------------------------------------------------
1 | build();
22 | $expected = <<<'TXT'
23 | expectedReturnValues(
24 | \Cake\ORM\Table::addBehavior(),
25 | \Foo\Bar::class,
26 | "string",
27 | );
28 | TXT;
29 | $this->assertSame($expected, $result);
30 | $this->assertSame('\\' . Table::class . '::addBehavior()@expectedReturnValues', $directive->key());
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/ValueObject/StringName.php:
--------------------------------------------------------------------------------
1 | value = $value;
17 | }
18 |
19 | /**
20 | * Creates itself from a string.
21 | *
22 | * @param string $value
23 | *
24 | * @return static
25 | */
26 | public static function create(string $value) {
27 | return new static($value);
28 | }
29 |
30 | /**
31 | * @return string
32 | */
33 | public function raw(): string {
34 | return $this->value;
35 | }
36 |
37 | /**
38 | * @return string
39 | */
40 | public function __toString(): string {
41 | return '\'' . $this->value . '\'';
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/Annotator/Traits/ModelTrait.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | protected function getUsedModels(string $content): array {
16 | preg_match_all('/\$this->([a-z]+)\s*=\s*\$this->fetchTable\(\'([a-z.]+)\'/i', $content, $matches);
17 | if (empty($matches[1])) {
18 | return [];
19 | }
20 |
21 | $properties = $matches[1];
22 | $tables = $matches[2];
23 | $models = array_combine($properties, $tables);
24 |
25 | preg_match_all('/\b(public|protected|private) \$([a-z]+)\b/i', $content, $propertyMatches);
26 | $excluded = $propertyMatches[2];
27 | foreach ($excluded as $property) {
28 | if (isset($models[$property])) {
29 | unset($models[$property]);
30 | }
31 | }
32 |
33 | return $models;
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/tests/Fixture/HousesFixture.php:
--------------------------------------------------------------------------------
1 | ['type' => 'integer', 'length' => 11, 'unsigned' => false, 'null' => false, 'default' => null, 'comment' => '', 'autoIncrement' => true, 'precision' => null],
14 | 'name' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null],
15 | '_constraints' => [
16 | 'primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []],
17 | ],
18 | '_options' => [
19 | 'engine' => 'InnoDB',
20 | 'collation' => 'utf8_general_ci',
21 | ],
22 | ];
23 |
24 | /**
25 | * Records
26 | */
27 | public array $records = [
28 | [
29 | 'id' => 1,
30 | 'name' => 'Lorem ipsum dolor sit amet',
31 | ],
32 | ];
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/tests/Fixture/WindowsFixture.php:
--------------------------------------------------------------------------------
1 | ['type' => 'integer', 'length' => 11, 'unsigned' => false, 'null' => false, 'default' => null, 'comment' => '', 'autoIncrement' => true, 'precision' => null],
14 | 'name' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null],
15 | '_constraints' => [
16 | 'primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []],
17 | ],
18 | '_options' => [
19 | 'engine' => 'InnoDB',
20 | 'collation' => 'utf8_general_ci',
21 | ],
22 | ];
23 |
24 | /**
25 | * Records
26 | */
27 | public array $records = [
28 | [
29 | 'id' => 1,
30 | 'name' => 'Lorem ipsum dolor sit amet',
31 | ],
32 | ];
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/Utility/TranslationParser.php:
--------------------------------------------------------------------------------
1 | poFileParser = new PoFileParser();
13 | }
14 |
15 | /**
16 | * @param string $path File path
17 | *
18 | * @return array
19 | */
20 | public function parse(string $path): array {
21 | $result = $this->poFileParser->parse($path);
22 | $resultKeys = array_keys($result);
23 |
24 | $domainKeys = [];
25 | foreach ($resultKeys as $resultKey) {
26 | $resultKey = $this->escapeSlashes($resultKey);
27 |
28 | $domainKeys[$resultKey] = $resultKey;
29 | }
30 |
31 | return $domainKeys;
32 | }
33 |
34 | /**
35 | * @param string $key
36 | *
37 | * @return string
38 | */
39 | protected function escapeSlashes(string $key): string {
40 | return addcslashes($key, '\'');
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/Annotator/Traits/ComponentTrait.php:
--------------------------------------------------------------------------------
1 | task = new CellTask();
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | public function testCollect() {
25 | $result = $this->task->collect();
26 |
27 | $this->assertCount(1, $result);
28 |
29 | /** @var \IdeHelper\Generator\Directive\Override $directive */
30 | $directive = array_shift($result);
31 | $this->assertSame('\Cake\View\CellTrait::cell()', $directive->toArray()['method']);
32 |
33 | $map = $directive->toArray()['map'];
34 |
35 | $expected = '\TestApp\View\Cell\TestCell::class';
36 | $this->assertSame($expected, (string)$map['Test']);
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Task/MailerTaskTest.php:
--------------------------------------------------------------------------------
1 | task = new MailerTask();
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | public function testCollect() {
25 | $result = $this->task->collect();
26 |
27 | $this->assertCount(1, $result);
28 |
29 | /** @var \IdeHelper\Generator\Directive\Override $directive */
30 | $directive = array_shift($result);
31 | $this->assertSame('\Cake\Mailer\MailerAwareTrait::getMailer(0)', $directive->toArray()['method']);
32 |
33 | $map = $directive->toArray()['map'];
34 |
35 | $expected = '\TestApp\Mailer\UserMailer::class';
36 | $this->assertSame($expected, (string)$map['User']);
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/ValueObject/ClassName.php:
--------------------------------------------------------------------------------
1 | className = $className;
17 | }
18 |
19 | /**
20 | * Creates itself from a fully qualified class name.
21 | *
22 | * @param string $className
23 | * @return static
24 | */
25 | public static function create(string $className) {
26 | if (str_starts_with($className, '\\')) {
27 | $className = substr($className, 1);
28 | }
29 |
30 | return new static($className);
31 | }
32 |
33 | /**
34 | * @return string
35 | */
36 | public function raw(): string {
37 | return $this->className;
38 | }
39 |
40 | /**
41 | * @return string
42 | */
43 | public function __toString(): string {
44 | return '\\' . $this->className . '::class';
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/CodeCompletion/Task/ViewEventsTask.php:
--------------------------------------------------------------------------------
1 | $iterator
9 | */
10 | $iterator = new DirectoryIterator(__DIR__ . DS . 'Fixture');
11 | foreach ($iterator as $file) {
12 | if (!preg_match('/(\w+)Fixture.php$/', (string)$file, $matches)) {
13 | continue;
14 | }
15 |
16 | $name = $matches[1];
17 | $tableName = Inflector::underscore($name);
18 | $class = 'IdeHelper\\Test\\Fixture\\' . $name . 'Fixture';
19 | try {
20 | $object = (new ReflectionClass($class))->getProperty('fields');
21 | } catch (ReflectionException $e) {
22 | continue;
23 | }
24 |
25 | $array = $object->getDefaultValue();
26 | $constraints = $array['_constraints'] ?? [];
27 | $indexes = $array['_indexes'] ?? [];
28 | unset($array['_constraints'], $array['_indexes'], $array['_options']);
29 | $table = [
30 | 'table' => $tableName,
31 | 'columns' => $array,
32 | 'constraints' => $constraints,
33 | 'indexes' => $indexes,
34 | ];
35 | $tables[$tableName] = $table;
36 | }
37 |
38 | return $tables;
39 |
--------------------------------------------------------------------------------
/tests/TestCase/Utility/TranslationParserTest.php:
--------------------------------------------------------------------------------
1 | translationParser = new TranslationParser();
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | public function testParse() {
25 | $path = TEST_FILES . 'locales' . DS . 'default.po';
26 |
27 | $result = $this->translationParser->parse($path);
28 |
29 | $expected = [
30 | 'A "quoted" string' => 'A "quoted" string',
31 | 'A ""escape-quoted"" string' => 'A ""escape-quoted"" string',
32 | 'A \\\'literally quoted\\\' string' => 'A \\\'literally quoted\\\' string',
33 | 'A variable \\\'\\\'{0}\\\'\\\' be replaced.' => 'A variable \\\'\\\'{0}\\\'\\\' be replaced.',
34 | ];
35 | $this->assertSame($expected, $result);
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/ValueObject/ArgumentsSet.php:
--------------------------------------------------------------------------------
1 | value = $value;
21 | }
22 |
23 | /**
24 | * Creates itself from a string.
25 | *
26 | * @param string $value
27 | *
28 | * @return static
29 | */
30 | public static function create(string $value) {
31 | return new static($value);
32 | }
33 |
34 | /**
35 | * @return string
36 | */
37 | public function raw(): string {
38 | return $this->value;
39 | }
40 |
41 | /**
42 | * @return string
43 | */
44 | public function __toString(): string {
45 | return 'argumentsSet(\'' . $this->value . '\')';
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/tests/TestCase/Utility/GenericStringTest.php:
--------------------------------------------------------------------------------
1 | assertSame('Foo[]', $result);
17 |
18 | Configure::write('IdeHelper.arrayAsGenerics', true);
19 |
20 | $result = GenericString::generate('Foo');
21 |
22 | Configure::delete('IdeHelper.arrayAsGenerics');
23 |
24 | $this->assertSame('array', $result);
25 | }
26 |
27 | /**
28 | * @return void
29 | */
30 | public function testClassNameObject() {
31 | $result = GenericString::generate('\Foo', '\Bar');
32 | $this->assertSame('\Foo[]|\Bar', $result);
33 |
34 | Configure::write('IdeHelper.objectAsGenerics', true);
35 |
36 | $result = GenericString::generate('\Foo', '\Bar');
37 |
38 | Configure::delete('IdeHelper.objectAsGenerics');
39 |
40 | $this->assertSame('\Bar<\Foo>', $result);
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Task/ConnectionTaskTest.php:
--------------------------------------------------------------------------------
1 | task = new ConnectionTask();
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | public function testCollect() {
25 | $result = $this->task->collect();
26 |
27 | $this->assertCount(1, $result);
28 |
29 | /** @var \IdeHelper\Generator\Directive\ExpectedArguments $directive */
30 | $directive = array_shift($result);
31 | $this->assertSame('\Cake\Datasource\ConnectionManager::get()', $directive->toArray()['method']);
32 |
33 | $list = $directive->toArray()['list'];
34 | $list = array_map(function ($className) {
35 | return (string)$className;
36 | }, $list);
37 |
38 | $expected = [
39 | 'test' => "'test'",
40 | ];
41 | $this->assertSame($expected, $list);
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/tests/TestCase/CodeCompletion/Task/BehaviorTaskTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['MyNamespace/MyPlugin', 'Shim']);
19 | $this->task = new BehaviorTask();
20 | }
21 |
22 | /**
23 | * @return void
24 | */
25 | public function testCollect() {
26 | $result = $this->task->create();
27 |
28 | $expected = <<<'TXT'
29 | abstract class BehaviorRegistry extends \Cake\Core\ObjectRegistry {
30 |
31 | /**
32 | * MyNamespace/MyPlugin.My behavior.
33 | *
34 | * @var \MyNamespace\MyPlugin\Model\Behavior\MyBehavior
35 | */
36 | public $My;
37 |
38 | /**
39 | * Shim.Nullable behavior.
40 | *
41 | * @var \Shim\Model\Behavior\NullableBehavior
42 | */
43 | public $Nullable;
44 |
45 | }
46 |
47 | TXT;
48 | $this->assertTextEquals($expected, $result);
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/Generator/Task/ConnectionTask.php:
--------------------------------------------------------------------------------
1 |
18 | */
19 | public function collect(): array {
20 | $result = [];
21 |
22 | $keys = $this->connectionKeys();
23 |
24 | ksort($keys);
25 |
26 | $directive = new ExpectedArguments(static::METHOD_GET, 0, $keys);
27 | $result[$directive->key()] = $directive;
28 |
29 | return $result;
30 | }
31 |
32 | /**
33 | * @return array<\IdeHelper\ValueObject\StringName>
34 | */
35 | protected function connectionKeys(): array {
36 | $configured = ConnectionManager::configured();
37 |
38 | $list = [];
39 | foreach ($configured as $key) {
40 | $list[$key] = StringName::create($key);
41 | }
42 |
43 | return $list;
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Task/LayoutTaskTest.php:
--------------------------------------------------------------------------------
1 | task = new LayoutTask();
22 | }
23 |
24 | /**
25 | * @return void
26 | */
27 | public function testCollect() {
28 | $result = $this->task->collect();
29 |
30 | $this->assertCount(1, $result);
31 |
32 | /** @var \IdeHelper\Generator\Directive\Override $directive */
33 | $directive = array_shift($result);
34 | $this->assertSame('\Cake\View\ViewBuilder::setLayout()', $directive->toArray()['method']);
35 |
36 | $list = $directive->toArray()['list'];
37 | $list = array_map(function ($className) {
38 | return (string)$className;
39 | }, $list);
40 |
41 | $expectedMap = [
42 | 'ajax' => '\'ajax\'',
43 | ];
44 | $this->assertSame($expectedMap, $list);
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Task/ConsoleHelperTaskTest.php:
--------------------------------------------------------------------------------
1 | task = new ConsoleHelperTask();
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | public function testCollect() {
25 | $result = $this->task->collect();
26 |
27 | $this->assertCount(1, $result);
28 |
29 | /** @var \IdeHelper\Generator\Directive\Override $directive */
30 | $directive = array_shift($result);
31 | $this->assertSame('\Cake\Console\ConsoleIo::helper(0)', $directive->toArray()['method']);
32 |
33 | $map = $directive->toArray()['map'];
34 |
35 | $expected = '\Cake\Command\Helper\ProgressHelper::class';
36 | $this->assertSame($expected, (string)$map['Progress']);
37 |
38 | $expected = '\Cake\Command\Helper\TableHelper::class';
39 | $this->assertSame($expected, (string)$map['Table']);
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Task/EnvTaskTest.php:
--------------------------------------------------------------------------------
1 | task = new EnvTask();
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | public function testCollect() {
25 | $result = $this->task->collect();
26 |
27 | $this->assertCount(1, $result);
28 |
29 | /** @var \IdeHelper\Generator\Directive\ExpectedArguments $directive */
30 | $directive = array_shift($result);
31 | $this->assertSame('\env()', $directive->toArray()['method']);
32 |
33 | $list = $directive->toArray()['list'];
34 | $list = array_map(function ($className) {
35 | return (string)$className;
36 | }, $list);
37 |
38 | $expected = [
39 | 'HTTP_HOST' => "'HTTP_HOST'",
40 | 'REMOTE_ADDR' => "'REMOTE_ADDR'",
41 | ];
42 | foreach ($expected as $key => $value) {
43 | $this->assertSame($value, $list[$key]);
44 | }
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Task/ComponentTaskTest.php:
--------------------------------------------------------------------------------
1 | task = new ComponentTask();
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | public function testCollect() {
25 | $result = $this->task->collect();
26 |
27 | $this->assertCount(2, $result);
28 |
29 | /** @var \IdeHelper\Generator\Directive\Override $directive */
30 | $directive = array_shift($result);
31 | $this->assertSame('\Cake\Controller\Controller::loadComponent(0)', $directive->toArray()['method']);
32 |
33 | $map = $directive->toArray()['map'];
34 |
35 | $expected = '\Cake\Controller\Component\FormProtectionComponent::class';
36 | $this->assertSame($expected, (string)$map['FormProtection']);
37 |
38 | $expected = '\TestApp\Controller\Component\MyOtherComponent::class';
39 | $this->assertSame($expected, (string)$map['MyOther']);
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/tests/TestCase/CodeCompletion/Task/ViewEventsTaskTest.php:
--------------------------------------------------------------------------------
1 | task = new ViewEventsTask();
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | public function testCollect() {
25 | $result = $this->task->create();
26 |
27 | $expected = <<<'TXT'
28 |
29 | use Cake\Event\EventInterface;
30 |
31 | if (false) {
32 | class Helper {
33 | public function beforeRenderFile(EventInterface $event): void {}
34 | public function afterRenderFile(EventInterface $event): void {}
35 | public function beforeRender(EventInterface $event): void {}
36 | public function afterRender(EventInterface $event): void {}
37 | public function beforeLayout(EventInterface $event): void {}
38 | public function afterLayout(EventInterface $event): void {}
39 | }
40 | }
41 |
42 | TXT;
43 |
44 | $this->assertTextEquals($expected, $result);
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/Utility/GenericString.php:
--------------------------------------------------------------------------------
1 | ', $value);
19 | }
20 | if (Configure::read('IdeHelper.objectAsGenerics') && $type !== null) {
21 | return sprintf($type . '<%s>', $value);
22 | }
23 |
24 | if ($type !== null && str_starts_with($type, '\\')) {
25 | $typeCheck = substr($type, 1);
26 | } else {
27 | $typeCheck = $type;
28 | }
29 |
30 | if ($typeCheck === ResultSetInterface::class) {
31 | if (Configure::read('IdeHelper.concreteEntitiesInParam')) {
32 | return sprintf($type . '<%s>', $value);
33 | }
34 |
35 | return $value . '[]|' . $type . '<' . $value . '>';
36 | }
37 |
38 | $value .= '[]';
39 | if ($type) {
40 | $value .= '|' . $type;
41 | }
42 |
43 | return $value;
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/tests/TestCase/Utility/PluginTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['IdeHelper', 'Awesome', 'MyNamespace/MyPlugin']);
18 | Configure::delete('IdeHelper.plugins');
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | protected function tearDown(): void {
25 | parent::tearDown();
26 |
27 | Configure::delete('IdeHelper.plugins');
28 | }
29 |
30 | /**
31 | * @return void
32 | */
33 | public function testAll() {
34 | $result = Plugin::all();
35 | $this->assertArrayHasKey('IdeHelper', $result);
36 | $this->assertArrayHasKey('Awesome', $result);
37 | $this->assertArrayHasKey('MyNamespace/MyPlugin', $result);
38 | $this->assertArrayNotHasKey('FooBar', $result);
39 |
40 | Configure::write('IdeHelper.plugins', ['FooBar', '-MyNamespace/MyPlugin']);
41 |
42 | $result = Plugin::all();
43 | $this->assertArrayHasKey('FooBar', $result);
44 | $this->assertArrayNotHasKey('MyNamespace/MyPlugin', $result);
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/Generator/Directive/ExitPoint.php:
--------------------------------------------------------------------------------
1 | method = $method;
30 | }
31 |
32 | /**
33 | * Key for sorting inside collection.
34 | *
35 | * @return string
36 | */
37 | public function key() {
38 | return $this->method . '@' . static::NAME;
39 | }
40 |
41 | /**
42 | * @return array
43 | */
44 | public function toArray() {
45 | return [
46 | 'method' => $this->method,
47 | ];
48 | }
49 |
50 | /**
51 | * @return string
52 | */
53 | public function build() {
54 | $method = $this->method;
55 |
56 | $result = <<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 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Task/ElementTaskTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Awesome']);
22 | $this->task = new ElementTask();
23 | }
24 |
25 | /**
26 | * @return void
27 | */
28 | public function testCollect() {
29 | $result = $this->task->collect();
30 |
31 | $this->assertCount(1, $result);
32 |
33 | /** @var \IdeHelper\Generator\Directive\Override $directive */
34 | $directive = array_shift($result);
35 | $this->assertSame('\Cake\View\View::element()', $directive->toArray()['method']);
36 |
37 | $list = $directive->toArray()['list'];
38 | $list = array_map(function ($className) {
39 | return (string)$className;
40 | }, $list);
41 |
42 | $expectedMap = [
43 | 'Awesome.pagination' => '\'Awesome.pagination\'',
44 | 'deeply/nested' => '\'deeply/nested\'',
45 | 'example' => '\'example\'',
46 | ];
47 | $this->assertSame($expectedMap, $list);
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/tests/Fixture/WheelsFixture.php:
--------------------------------------------------------------------------------
1 | ['type' => 'integer', 'length' => 11, 'unsigned' => false, 'null' => false, 'default' => null, 'comment' => '', 'autoIncrement' => true, 'precision' => null],
14 | 'name' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null],
15 | 'content' => ['type' => 'text', 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null],
16 | 'created' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null],
17 | '_constraints' => [
18 | 'primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []],
19 | ],
20 | '_options' => [
21 | 'engine' => 'InnoDB',
22 | 'collation' => 'utf8_general_ci',
23 | ],
24 | ];
25 |
26 | /**
27 | * Records
28 | */
29 | public array $records = [
30 | [
31 | 'id' => 1,
32 | 'name' => 'Lorem ipsum dolor sit amet',
33 | 'content' => 'Lorem ipsum dolor sit amet',
34 | 'created' => '2016-06-23 14:59:54',
35 | ],
36 | ];
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/tests/TestCase/Command/GenerateCodeCompletionCommandTest.php:
--------------------------------------------------------------------------------
1 | files as $file) {
25 | if (file_exists($file)) {
26 | unlink($file);
27 | }
28 | }
29 | $this->loadPlugins(['IdeHelper']);
30 | }
31 |
32 | /**
33 | * @return void
34 | */
35 | protected function tearDown(): void {
36 | parent::tearDown();
37 |
38 | foreach ($this->files as $file) {
39 | if (file_exists($file)) {
40 | unlink($file);
41 | }
42 | }
43 | }
44 |
45 | /**
46 | * @return void
47 | */
48 | public function testGenerate() {
49 | $this->exec('generate code_completion');
50 | $this->assertOutputContains('CodeCompletion files generated: Cake\Controller, Cake\ORM, Cake\View');
51 | $this->assertExitSuccess();
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/tests/Fixture/BarBarsFixture.php:
--------------------------------------------------------------------------------
1 | ['type' => 'integer', 'length' => 11, 'unsigned' => false, 'null' => false, 'default' => null, 'comment' => '', 'autoIncrement' => true, 'precision' => null],
14 | 'name' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null],
15 | 'content' => ['type' => 'text', 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null],
16 | 'created' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null],
17 | '_constraints' => [
18 | 'primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []],
19 | ],
20 | '_options' => [
21 | 'engine' => 'InnoDB',
22 | 'collation' => 'utf8_general_ci',
23 | ],
24 | ];
25 |
26 | /**
27 | * Records
28 | */
29 | public array $records = [
30 | [
31 | 'id' => 1,
32 | 'name' => 'Lorem ipsum dolor sit amet',
33 | 'content' => 'Lorem ipsum dolor sit amet',
34 | 'created' => '2016-06-23 14:59:54',
35 | ],
36 | ];
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/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/ValueObject/KeyValue.php:
--------------------------------------------------------------------------------
1 | key = $key;
20 | $this->value = $value;
21 | }
22 |
23 | /**
24 | * Creates itself from a ValueObjectInterface key and value.
25 | *
26 | * @param \IdeHelper\ValueObject\ValueObjectInterface $key
27 | * @param \IdeHelper\ValueObject\ValueObjectInterface $value
28 | *
29 | * @return static
30 | */
31 | public static function create(ValueObjectInterface $key, ValueObjectInterface $value) {
32 | return new static($key, $value);
33 | }
34 |
35 | /**
36 | * @return \IdeHelper\ValueObject\ValueObjectInterface
37 | */
38 | public function key(): ValueObjectInterface {
39 | return $this->key;
40 | }
41 |
42 | /**
43 | * @return \IdeHelper\ValueObject\ValueObjectInterface
44 | */
45 | public function value(): ValueObjectInterface {
46 | return $this->value;
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/tests/TestCase/Illuminator/IlluminatorTest.php:
--------------------------------------------------------------------------------
1 | out = new ConsoleOutput();
29 | $this->err = new ConsoleOutput();
30 | $consoleIo = new ConsoleIo($this->out, $this->err);
31 | $consoleIo->level($consoleIo::VERBOSE);
32 | $this->io = new Io($consoleIo);
33 |
34 | $taskCollection = new TaskCollection($this->io, ['dry-run' => true]);
35 |
36 | $this->illuminator = new Illuminator($taskCollection);
37 | }
38 |
39 | /**
40 | * @return void
41 | */
42 | public function testIlluminate() {
43 | $path = TEST_FILES;
44 | $count = $this->illuminator->illuminate($path, null);
45 |
46 | $this->assertSame(14, $count);
47 |
48 | $out = $this->out->output();
49 |
50 | $this->assertTextContains('public const FIELD_ID = \'id\';', $out);
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/Annotator/Traits/FileTrait.php:
--------------------------------------------------------------------------------
1 | config = $config;
30 | $phpcs->init();
31 |
32 | $ruleset = new Ruleset($config);
33 |
34 | $fileObject = new File($file, $ruleset, $config);
35 | $fileObject->setContent($content ?? (string)file_get_contents($file));
36 | $fileObject->parse();
37 |
38 | return $fileObject;
39 | }
40 |
41 | /**
42 | * @param \PHP_CodeSniffer\Files\File $file
43 | *
44 | * @return \PHP_CodeSniffer\Fixer
45 | */
46 | protected function getFixer(File $file): Fixer {
47 | $fixer = new Fixer();
48 |
49 | $fixer->startFile($file);
50 |
51 | return $fixer;
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/Generator/Task/TableAssociationTask.php:
--------------------------------------------------------------------------------
1 |
19 | */
20 | protected array $aliases = [
21 | '\\' . self::CLASS_TABLE . '::belongsTo(0)' => BelongsTo::class,
22 | '\\' . self::CLASS_TABLE . '::hasOne(0)' => HasOne::class,
23 | '\\' . self::CLASS_TABLE . '::hasMany(0)' => HasMany::class,
24 | '\\' . self::CLASS_TABLE . '::belongToMany(0)' => BelongsToMany::class,
25 | ];
26 |
27 | /**
28 | * @return array
29 | */
30 | public function collect(): array {
31 | $models = $this->collectModels();
32 |
33 | $result = [];
34 | foreach ($this->aliases as $alias => $className) {
35 | $map = [];
36 | foreach ($models as $model => $modelClassName) {
37 | $map[$model] = ClassName::create($className);
38 | }
39 |
40 | ksort($map);
41 |
42 | $directive = new Override($alias, $map);
43 | $result[$directive->key()] = $directive;
44 | }
45 |
46 | return $result;
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Task/CacheTaskTest.php:
--------------------------------------------------------------------------------
1 | task = new CacheTask();
20 | }
21 |
22 | /**
23 | * @return void
24 | */
25 | public function testCollect() {
26 | $result = $this->task->collect();
27 |
28 | $this->assertCount(12, $result);
29 |
30 | /** @var \IdeHelper\Generator\Directive\RegisterArgumentsSet $directive */
31 | $directive = array_shift($result);
32 | $this->assertInstanceOf(RegisterArgumentsSet::class, $directive);
33 | $this->assertSame(CacheTask::SET_CACHE_ENGINES, $directive->toArray()['set']);
34 |
35 | /** @var \IdeHelper\Generator\Directive\ExpectedArguments $directive */
36 | $directive = array_shift($result);
37 | $this->assertSame('\Cake\Cache\Cache::clear()', $directive->toArray()['method']);
38 |
39 | $list = $directive->toArray()['list'];
40 | $list = array_map(function ($className) {
41 | return (string)$className;
42 | }, $list);
43 |
44 | $expected = [
45 | 'argumentsSet(\'cacheEngines\')',
46 | ];
47 | $this->assertSame($expected, $list);
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Task/FixtureTaskTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['MyNamespace/MyPlugin', 'IdeHelper']);
19 | $this->task = new FixtureTask();
20 | }
21 |
22 | /**
23 | * @return void
24 | */
25 | public function testCollect() {
26 | $result = $this->task->collect();
27 |
28 | $this->assertCount(1, $result);
29 |
30 | /** @var \IdeHelper\Generator\Directive\ExpectedArguments $directive */
31 | $directive = array_shift($result);
32 | $this->assertSame('\\' . TestCase::class . '::addFixture()', $directive->toArray()['method']);
33 |
34 | $list = $directive->toArray()['list'];
35 | $list = array_map(function ($className) {
36 | return (string)$className;
37 | }, $list);
38 |
39 | $expected = [
40 | 'app.SmallWindows' => '\'app.SmallWindows\'',
41 | 'core.Posts' => '\'core.Posts\'',
42 | 'plugin.IdeHelper.Windows' => '\'plugin.IdeHelper.Windows\'',
43 | 'plugin.MyNamespace/MyPlugin.Sub/My' => '\'plugin.MyNamespace/MyPlugin.Sub/My\'',
44 | ];
45 | foreach ($expected as $key => $value) {
46 | $this->assertSame($value, $list[$key]);
47 | }
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/tests/Fixture/FoosFixture.php:
--------------------------------------------------------------------------------
1 | ['type' => 'integer', 'length' => 11, 'unsigned' => false, 'null' => false, 'default' => null, 'comment' => '', 'autoIncrement' => true, 'precision' => null],
14 | 'name' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null],
15 | 'content' => ['type' => 'text', 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null],
16 | 'created' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null],
17 | 'params' => ['type' => 'json', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null],
18 | '_constraints' => [
19 | 'primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []],
20 | ],
21 | '_options' => [
22 | 'engine' => 'InnoDB',
23 | 'collation' => 'utf8_general_ci',
24 | ],
25 | ];
26 |
27 | /**
28 | * Records
29 | */
30 | public array $records = [
31 | [
32 | 'id' => 1,
33 | 'name' => 'Lorem ipsum dolor sit amet',
34 | 'content' => 'Lorem ipsum dolor sit amet',
35 | 'created' => '2016-06-23 14:59:54',
36 | 'params' => '[]',
37 | ],
38 | ];
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Task/PluginTaskTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Bake', 'Migrations', 'Shim']);
20 | $this->task = new PluginTask();
21 | }
22 |
23 | /**
24 | * @return void
25 | */
26 | public function testCollect() {
27 | $result = $this->task->collect();
28 |
29 | $this->assertCount(1, $result);
30 |
31 | /** @var \IdeHelper\Generator\Directive\Override $directive */
32 | $directive = array_shift($result);
33 | $this->assertSame('\Cake\Core\PluginApplicationInterface::addPlugin(0)', $directive->toArray()['method']);
34 |
35 | $map = $directive->toArray()['map'];
36 | $map = array_map(function ($className) {
37 | return (string)$className;
38 | }, $map);
39 |
40 | $expected = [
41 | 'Bake' => '\Cake\Http\BaseApplication::class',
42 | 'Cake/TwigView' => '\Cake\Http\BaseApplication::class',
43 | 'Migrations' => '\Cake\Http\BaseApplication::class',
44 | 'Shim' => '\Cake\Http\BaseApplication::class',
45 | ];
46 | if (version_compare(Configure::version(), '5.1.0', '<')) {
47 | $expected = [];
48 | }
49 | $this->assertSame($expected, $map);
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/tests/TestCase/Annotator/DiffHelperTrait.php:
--------------------------------------------------------------------------------
1 | diffToArray($expected, $actual);
21 |
22 | $begin = null;
23 | $end = null;
24 | foreach ($array as $key => $row) {
25 | if ($row[1] === 0) {
26 | continue;
27 | }
28 |
29 | if ($begin === null) {
30 | $begin = $key;
31 | }
32 | $end = $key;
33 | }
34 | if ($begin === null) {
35 | return;
36 | }
37 | $firstLineOfOutput = $begin > 0 ? $begin - 1 : 0;
38 | $lastLineOfOutput = count($array) - 1 > $end ? $end + 1 : $end;
39 |
40 | $out = [];
41 | for ($i = $firstLineOfOutput; $i <= $lastLineOfOutput; $i++) {
42 | $row = $array[$i];
43 | $char = ' ';
44 | if ($row[1] === 1) {
45 | $char = '+';
46 | $out[] = $char . $row[0];
47 | } elseif ($row[1] === 2) {
48 | $char = '-';
49 | $out[] = $char . $row[0];
50 | } else {
51 | $out[] = $char . $row[0];
52 | }
53 | }
54 |
55 | echo PHP_EOL . '####### diff #######' . PHP_EOL . (implode(PHP_EOL, $out)) . PHP_EOL . '##### diff end #####' . PHP_EOL;
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/Illuminator/Illuminator.php:
--------------------------------------------------------------------------------
1 | taskCollection = $taskCollection;
16 | }
17 |
18 | /**
19 | * @param string $path
20 | * @param string|null $filter
21 | * @return int
22 | */
23 | public function illuminate($path, $filter) {
24 | $files = $this->getFiles($path);
25 |
26 | $count = 0;
27 | foreach ($files as $file) {
28 | $name = pathinfo($file, PATHINFO_FILENAME);
29 | if ($this->shouldSkip($name, $filter)) {
30 | continue;
31 | }
32 |
33 | if (!$this->taskCollection->run($file)) {
34 | continue;
35 | }
36 |
37 | $count++;
38 | }
39 |
40 | return $count;
41 | }
42 |
43 | /**
44 | * @param string $fileName
45 | * @param string|null $filter
46 | *
47 | * @return bool
48 | */
49 | protected function shouldSkip($fileName, $filter) {
50 | if (!$filter) {
51 | return false;
52 | }
53 |
54 | return !preg_match('/' . preg_quote($filter, '/') . '/i', $fileName);
55 | }
56 |
57 | /**
58 | * @param string $path
59 | * @return array
60 | */
61 | protected function getFiles($path) {
62 | $folder = new Folder($path);
63 | $result = $folder->findRecursive('.*\.php', true);
64 |
65 | return $result;
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/Utility/ControllerActionParser.php:
--------------------------------------------------------------------------------
1 | |null
15 | */
16 | protected static $appControllerActions;
17 |
18 | /**
19 | * @param string $path
20 | *
21 | * @return array
22 | */
23 | public function parse(string $path): array {
24 | $actions = $this->parseFile($path);
25 |
26 | if (static::$appControllerActions === null) {
27 | try {
28 | $class = new ReflectionClass(AppController::class);
29 | $methods = $class->getMethods(ReflectionMethod::IS_PUBLIC);
30 | } catch (Throwable $exception) {
31 | return [];
32 | }
33 |
34 | static::$appControllerActions = [];
35 | foreach ($methods as $method) {
36 | static::$appControllerActions[] = $method->getName();
37 | }
38 | }
39 |
40 | $actions = array_diff($actions, static::$appControllerActions);
41 |
42 | return array_values($actions);
43 | }
44 |
45 | /**
46 | * @param string $path
47 | *
48 | * @return array
49 | */
50 | protected function parseFile($path): array {
51 | $content = file_get_contents($path);
52 | if ($content === false) {
53 | throw new RuntimeException('Cannot read file');
54 | }
55 |
56 | preg_match_all('/public function (.+)\(/', $content, $matches);
57 | if (empty($matches[1])) {
58 | return [];
59 | }
60 |
61 | return $matches[1];
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Task/BehaviorTaskTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Shim']);
19 | $this->task = new BehaviorTask();
20 | }
21 |
22 | /**
23 | * @return void
24 | */
25 | public function testCollect() {
26 | $result = $this->task->collect();
27 |
28 | $this->assertCount(4, $result);
29 |
30 | /** @var \IdeHelper\Generator\Directive\Override $directive */
31 | $directive = array_shift($result);
32 | $this->assertSame('\Cake\ORM\Table::addBehavior()', $directive->toArray()['method']);
33 |
34 | $list = $directive->toArray()['list'];
35 |
36 | $expected = '\'Timestamp\'';
37 | $this->assertSame($expected, (string)$list['Timestamp']);
38 |
39 | $expected = '\'Shim.Nullable\'';
40 | $this->assertSame($expected, (string)$list['Shim.Nullable']);
41 |
42 | /** @var \IdeHelper\Generator\Directive\Override $directive */
43 | $directive = array_shift($result);
44 | $this->assertSame('\Cake\ORM\Table::removeBehavior()', $directive->toArray()['method']);
45 |
46 | $list = $directive->toArray()['list'];
47 |
48 | $expected = '\'Timestamp\'';
49 | $this->assertSame($expected, (string)$list['Timestamp']);
50 |
51 | $expected = '\'Nullable\'';
52 | $this->assertSame($expected, (string)$list['Nullable']);
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Task/DatabaseTypeTaskTest.php:
--------------------------------------------------------------------------------
1 | task = new DatabaseTypeTask();
21 | }
22 |
23 | /**
24 | * @return void
25 | */
26 | public function testCollect() {
27 | TypeFactory::set('uuid', new UuidType());
28 |
29 | $result = $this->task->collect();
30 |
31 | $this->assertCount(2, $result);
32 |
33 | /** @var \IdeHelper\Generator\Directive\Override $directive */
34 | $directive = array_shift($result);
35 | $this->assertSame('\Cake\Database\TypeFactory::build(0)', $directive->toArray()['method']);
36 |
37 | $map = $directive->toArray()['map'];
38 |
39 | $expected = '\Cake\Database\Type\BinaryType::class';
40 | $this->assertSame($expected, (string)$map['binary']);
41 |
42 | $expected = '\TestApp\Database\Type\UuidType::class';
43 | $this->assertSame($expected, (string)$map['uuid']);
44 |
45 | /** @var \IdeHelper\Generator\Directive\Override $directive */
46 | $directive = array_shift($result);
47 | $this->assertSame('\Cake\Database\TypeFactory::map()', $directive->toArray()['method']);
48 |
49 | $list = $directive->toArray()['list'];
50 | $this->assertSame('\'json\'', (string)$list['json']);
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/Generator/Task/PluginTask.php:
--------------------------------------------------------------------------------
1 |
22 | */
23 | protected array $aliases = [
24 | '\\' . self::INTERFACE_APPLICATION . '::addPlugin(0)',
25 | ];
26 |
27 | /**
28 | * @return array
29 | */
30 | public function collect(): array {
31 | $map = [];
32 |
33 | $plugins = $this->collectPlugins();
34 | foreach ($plugins as $name) {
35 | $map[$name] = ClassName::create(static::CLASS_APPLICATION);
36 | }
37 |
38 | ksort($map);
39 |
40 | $result = [];
41 | foreach ($this->aliases as $alias) {
42 | $directive = new Override($alias, $map);
43 | $result[$directive->key()] = $directive;
44 | }
45 |
46 | return $result;
47 | }
48 |
49 | /**
50 | * Read from PluginCollection loaded config.
51 | *
52 | * @return array
53 | */
54 | protected function collectPlugins(): array {
55 | $plugins = (array)Configure::read('plugins');
56 |
57 | $names = array_keys($plugins);
58 |
59 | sort($names);
60 |
61 | return $names;
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/tests/Fixture/CarsFixture.php:
--------------------------------------------------------------------------------
1 | ['type' => 'integer', 'length' => 11, 'unsigned' => false, 'null' => false, 'default' => null, 'comment' => '', 'autoIncrement' => true, 'precision' => null],
15 | 'name' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null],
16 | 'content' => ['type' => 'text', 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null],
17 | 'created' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null],
18 | 'modified' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null],
19 | 'status' => ['type' => 'tinyinteger', 'length' => 2, 'null' => false, 'default' => '0'],
20 | '_constraints' => [
21 | 'primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []],
22 | ],
23 | '_options' => [
24 | 'engine' => 'InnoDB',
25 | 'collation' => 'utf8_general_ci',
26 | ],
27 | ];
28 |
29 | /**
30 | * Records
31 | */
32 | public array $records = [
33 | [
34 | 'id' => 1,
35 | 'name' => 'Lorem ipsum dolor sit amet',
36 | 'content' => 'Lorem ipsum dolor sit amet',
37 | 'created' => '2016-06-23 14:59:54',
38 | 'modified' => '2016-06-23 14:59:54',
39 | 'status' => CarStatus::NEW,
40 | ],
41 | ];
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/Command/Annotate/ViewCommand.php:
--------------------------------------------------------------------------------
1 | getOption('plugin') || $args->getOption('filter')) {
29 | $io->error('Plugin or filter option not supported for this command');
30 | $this->abort();
31 | }
32 |
33 | $className = App::className('App', 'View', 'View');
34 | $file = APP . 'View' . DS . 'AppView.php';
35 | if (!$className || !file_exists($file)) {
36 | $io->warning('You need to create `AppView.php` first in `' . APP_DIR . DS . 'View' . DS . '`.');
37 |
38 | return static::CODE_SUCCESS;
39 | }
40 |
41 | $folder = pathinfo($file, PATHINFO_DIRNAME);
42 | $io->out(str_replace(ROOT . DS, '', $folder));
43 | $io->out(' -> ' . pathinfo($file, PATHINFO_BASENAME));
44 |
45 | $annotator = $this->getAnnotator(ViewAnnotator::class);
46 | $annotator->annotate($file);
47 |
48 | if ($args->getOption('ci') && $this->_annotatorMadeChanges()) {
49 | return static::CODE_CHANGES;
50 | }
51 |
52 | return static::CODE_SUCCESS;
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/Annotation/SeeAnnotation.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/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/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 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Task/RequestTaskTest.php:
--------------------------------------------------------------------------------
1 | task = new RequestTask();
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | public function testCollect() {
25 | $result = $this->task->collect();
26 |
27 | $this->assertCount(2, $result);
28 |
29 | /** @var \IdeHelper\Generator\Directive\ExpectedArguments $directive */
30 | $directive = array_shift($result);
31 | $this->assertSame('\Cake\Http\ServerRequest::getParam()', $directive->toArray()['method']);
32 |
33 | $list = $directive->toArray()['list'];
34 | $list = array_map(function ($className) {
35 | return (string)$className;
36 | }, $list);
37 |
38 | $expected = [
39 | '_ext' => "'_ext'",
40 | '_matchedRoute' => "'_matchedRoute'",
41 | 'action' => "'action'",
42 | 'controller' => "'controller'",
43 | 'pass' => "'pass'",
44 | 'plugin' => "'plugin'",
45 | 'prefix' => "'prefix'",
46 | ];
47 | $this->assertSame($expected, $list);
48 |
49 | /** @var \IdeHelper\Generator\Directive\Override $directive */
50 | $directive = array_shift($result);
51 | $this->assertSame('\Cake\Http\ServerRequest::getAttribute(0)', $directive->toArray()['method']);
52 |
53 | $map = $directive->toArray()['map'];
54 |
55 | $expected = '\Cake\Http\Session::class';
56 | $this->assertSame($expected, (string)$map['session']);
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/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/Generator/Directive/Override.php:
--------------------------------------------------------------------------------
1 | \MyClass::class,
14 | * '' => '@|\Iterator',
15 | * ])
16 | * );
17 | *
18 | * @see https://www.jetbrains.com/help/phpstorm/ide-advanced-metadata.html#override
19 | */
20 | class Override extends BaseDirective {
21 |
22 | /**
23 | * @var string
24 | */
25 | public const NAME = 'override';
26 |
27 | protected string $method;
28 |
29 | /**
30 | * @var array
31 | */
32 | protected array $map;
33 |
34 | /**
35 | * @param string $method
36 | * @param array $map
37 | */
38 | public function __construct($method, array $map) {
39 | $this->method = $method;
40 | $this->map = $map;
41 | }
42 |
43 | /**
44 | * @return array
45 | */
46 | public function toArray() {
47 | return [
48 | 'method' => $this->method,
49 | 'map' => $this->map,
50 | ];
51 | }
52 |
53 | /**
54 | * Key for sorting inside collection.
55 | *
56 | * @return string
57 | */
58 | public function key() {
59 | return $this->method . '@' . static::NAME;
60 | }
61 |
62 | /**
63 | * @return string
64 | */
65 | public function build() {
66 | $method = $this->method;
67 | $mapDefinitions = $this->buildKeyValueMap($this->map);
68 |
69 | $result = <<task = new ValidationTask();
20 | }
21 |
22 | /**
23 | * @return void
24 | */
25 | public function testCollect() {
26 | $result = $this->task->collect();
27 |
28 | $this->assertCount(15, $result);
29 |
30 | /** @var \IdeHelper\Generator\Directive\RegisterArgumentsSet $directive */
31 | $directive = array_shift($result);
32 | $this->assertInstanceOf(RegisterArgumentsSet::class, $directive);
33 | $this->assertSame(ValidationTask::SET_VALIDATION_WHEN, $directive->toArray()['set']);
34 |
35 | $list = $directive->toArray()['list'];
36 | $list = array_map(function ($className) {
37 | return (string)$className;
38 | }, $list);
39 |
40 | $expected = [
41 | "'create'",
42 | "'update'",
43 | ];
44 | $this->assertSame($expected, $list);
45 |
46 | /** @var \IdeHelper\Generator\Directive\Override $directive */
47 | $directive = array_shift($result);
48 | $this->assertSame('\Cake\Validation\Validator::requirePresence()', $directive->toArray()['method']);
49 |
50 | $list = $directive->toArray()['list'];
51 | $list = array_map(function ($className) {
52 | return (string)$className;
53 | }, $list);
54 |
55 | $expected = [
56 | 'argumentsSet(\'validationWhen\')',
57 | ];
58 | $this->assertSame($expected, $list);
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/src/Annotation/LinkAnnotation.php:
--------------------------------------------------------------------------------
1 | isInUse = true;
28 | }
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\MixinAnnotation $annotation
51 | *
52 | * @return bool
53 | */
54 | public function matches(AbstractAnnotation $annotation): bool {
55 | if (!$annotation instanceof self) {
56 | return false;
57 | }
58 | if ($annotation->getType() !== $this->type) {
59 | return false;
60 | }
61 |
62 | return true;
63 | }
64 |
65 | /**
66 | * @param \IdeHelper\Annotation\AbstractAnnotation|\IdeHelper\Annotation\MixinAnnotation $annotation
67 | * @return void
68 | */
69 | public function replaceWith(AbstractAnnotation $annotation): void {
70 | $this->type = $annotation->getType();
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/src/Command/Annotate/ModelsCommand.php:
--------------------------------------------------------------------------------
1 | getPaths('Model/Table');
28 | foreach ($paths as $plugin => $pluginPaths) {
29 | foreach ($pluginPaths as $path) {
30 | $this->setPlugin($plugin);
31 | $this->_models($path);
32 | }
33 | }
34 |
35 | if ($args->getOption('ci') && $this->_annotatorMadeChanges()) {
36 | return static::CODE_CHANGES;
37 | }
38 |
39 | return static::CODE_SUCCESS;
40 | }
41 |
42 | /**
43 | * @param string $folder
44 | * @return void
45 | */
46 | protected function _models(string $folder) {
47 | $this->io->out(str_replace(ROOT . DS, '', $folder), 1, ConsoleIo::VERBOSE);
48 |
49 | $folderContent = glob($folder . '*') ?: [];
50 | foreach ($folderContent as $path) {
51 | if (!is_file($path)) {
52 | continue;
53 | }
54 | $name = pathinfo($path, PATHINFO_FILENAME);
55 | if ($this->_shouldSkip($name, $path)) {
56 | continue;
57 | }
58 |
59 | $this->io->out('-> ' . $name, 1, ConsoleIo::VERBOSE);
60 |
61 | $annotator = $this->getAnnotator(ModelAnnotator::class);
62 | $annotator->annotate($path);
63 | }
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/Command/Annotate/CommandsCommand.php:
--------------------------------------------------------------------------------
1 | getPaths('Command');
28 |
29 | foreach ($paths as $plugin => $pluginPaths) {
30 | $this->setPlugin($plugin);
31 | foreach ($pluginPaths as $path) {
32 | $this->_commands($path);
33 | }
34 | }
35 |
36 | if ($args->getOption('ci') && $this->_annotatorMadeChanges()) {
37 | return static::CODE_CHANGES;
38 | }
39 |
40 | return static::CODE_SUCCESS;
41 | }
42 |
43 | /**
44 | * @param string $folder
45 | * @return void
46 | */
47 | protected function _commands(string $folder) {
48 | $this->io->out(str_replace(ROOT . DS, '', $folder), 1, ConsoleIo::VERBOSE);
49 |
50 | $folderContent = glob($folder . '*') ?: [];
51 | foreach ($folderContent as $path) {
52 | if (is_dir($path)) {
53 | continue;
54 | }
55 | $name = pathinfo($path, PATHINFO_FILENAME);
56 | if ($this->_shouldSkip($name, $path)) {
57 | continue;
58 | }
59 |
60 | $this->io->out('-> ' . $name, 1, ConsoleIo::VERBOSE);
61 | $annotator = $this->getAnnotator(CommandAnnotator::class);
62 | $annotator->annotate($path);
63 | }
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/Command/Annotate/HelpersCommand.php:
--------------------------------------------------------------------------------
1 | getPaths('View/Helper');
28 | foreach ($paths as $plugin => $pluginPaths) {
29 | $this->setPlugin($plugin);
30 | foreach ($pluginPaths as $path) {
31 | $this->_helpers($path);
32 | }
33 | }
34 |
35 | if ($args->getOption('ci') && $this->_annotatorMadeChanges()) {
36 | return static::CODE_CHANGES;
37 | }
38 |
39 | return static::CODE_SUCCESS;
40 | }
41 |
42 | /**
43 | * @param string $folder
44 | * @return void
45 | */
46 | protected function _helpers($folder) {
47 | $this->io->out(str_replace(ROOT . DS, '', $folder), 1, ConsoleIo::VERBOSE);
48 |
49 | $folderContent = glob($folder . '*') ?: [];
50 | foreach ($folderContent as $path) {
51 | if (is_dir($path)) {
52 | $this->_helpers($path);
53 | } else {
54 | $name = pathinfo($path, PATHINFO_FILENAME);
55 | if ($this->_shouldSkip($name, $path)) {
56 | continue;
57 | }
58 |
59 | $this->io->out('-> ' . $name, 1, ConsoleIo::VERBOSE);
60 | $annotator = $this->getAnnotator(HelperAnnotator::class);
61 | $annotator->annotate($path);
62 | }
63 | }
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/Annotation/ExtendsAnnotation.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 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Directive/OverrideTest.php:
--------------------------------------------------------------------------------
1 | ClassName::create(Table::class),
19 | 'CounterCache' => ClassName::create(Table::class),
20 | ];
21 | $directive = new Override('\\' . Table::class . '::addBehavior(0)', $map);
22 |
23 | $result = $directive->build();
24 | $expected = <<<'TXT'
25 | override(
26 | \Cake\ORM\Table::addBehavior(0),
27 | map([
28 | 'Tree' => \Cake\ORM\Table::class,
29 | 'CounterCache' => \Cake\ORM\Table::class,
30 | ]),
31 | );
32 | TXT;
33 | $this->assertSame($expected, $result);
34 | $this->assertSame('\\' . Table::class . '::addBehavior(0)@override', $directive->key());
35 | }
36 |
37 | /**
38 | * @return void
39 | */
40 | public function testBuildLiteralKey() {
41 | $key = ClassName::create(Table::class);
42 |
43 | $value = ClassName::create(Table::class);
44 | $keyValue = KeyValue::create($key, $value);
45 | $map = [
46 | '\\' . Table::class . '::class' => $keyValue,
47 | ];
48 | $directive = new Override('\\' . Table::class . '::addBehavior(0)', $map);
49 |
50 | $result = $directive->build();
51 | $expected = <<<'TXT'
52 | override(
53 | \Cake\ORM\Table::addBehavior(0),
54 | map([
55 | \Cake\ORM\Table::class => \Cake\ORM\Table::class,
56 | ]),
57 | );
58 | TXT;
59 | $this->assertSame($expected, $result);
60 | $this->assertSame('\\' . Table::class . '::addBehavior(0)@override', $directive->key());
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Task/EntityTaskTest.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | protected array $fixtures = [
16 | 'plugin.IdeHelper.Cars',
17 | 'plugin.IdeHelper.Wheels',
18 | ];
19 |
20 | protected EntityTask $task;
21 |
22 | /**
23 | * @return void
24 | */
25 | protected function setUp(): void {
26 | parent::setUp();
27 |
28 | $this->loadPlugins(['Awesome', 'Controllers', 'MyNamespace/MyPlugin', 'Relations', 'Shim', 'IdeHelper']);
29 | $this->task = new EntityTask();
30 | }
31 |
32 | /**
33 | * @return void
34 | */
35 | public function testCollect() {
36 | $result = $this->task->collect();
37 |
38 | $this->assertCount(99, $result);
39 |
40 | /** @var \IdeHelper\Generator\Directive\RegisterArgumentsSet $directive */
41 | $directive = array_shift($result);
42 | $this->assertInstanceOf(RegisterArgumentsSet::class, $directive);
43 | $this->assertStringContainsString(EntityTask::SET_ENTITY_FIELDS, $directive->toArray()['set']);
44 |
45 | $list = $directive->toArray()['list'];
46 | $list = array_map(function ($className) {
47 | return (string)$className;
48 | }, $list);
49 |
50 | $expected = [
51 | 'content' => "'content'",
52 | 'created' => "'created'",
53 | 'foo' => "'foo'",
54 | 'houses' => "'houses'",
55 | 'id' => "'id'",
56 | 'name' => "'name'",
57 | ];
58 | $this->assertSame($expected, $list);
59 |
60 | $directive = array_shift($result);
61 | $this->assertInstanceOf(ExpectedArguments::class, $directive);
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/Generator/Directive/ExpectedReturnValues.php:
--------------------------------------------------------------------------------
1 |
36 | */
37 | protected array $list;
38 |
39 | /**
40 | * @param string $method
41 | * @param array $list
42 | */
43 | public function __construct($method, array $list) {
44 | $this->method = $method;
45 | $this->list = $list;
46 | }
47 |
48 | /**
49 | * Key for sorting inside collection.
50 | *
51 | * @return string
52 | */
53 | public function key() {
54 | return $this->method . '@' . static::NAME;
55 | }
56 |
57 | /**
58 | * @return array
59 | */
60 | public function toArray() {
61 | return [
62 | 'method' => $this->method,
63 | 'list' => $this->list,
64 | ];
65 | }
66 |
67 | /**
68 | * @return string
69 | */
70 | public function build() {
71 | $method = $this->method;
72 | $list = $this->buildList($this->list);
73 |
74 | $result = <<loadPlugins(['Shim']);
19 | $this->task = new HelperTask();
20 | }
21 |
22 | /**
23 | * @return void
24 | */
25 | public function testCollect() {
26 | $result = $this->task->collect();
27 |
28 | $this->assertCount(3, $result);
29 |
30 | /** @var \IdeHelper\Generator\Directive\Override $directive */
31 | $directive = array_shift($result);
32 | $this->assertSame('\Cake\View\View::loadHelper(0)', $directive->toArray()['method']);
33 |
34 | /** @var \IdeHelper\Generator\Directive\Override $directive */
35 | $directive = array_shift($result);
36 | $this->assertSame('\Cake\View\View::addHelper(0)', $directive->toArray()['method']);
37 |
38 | $map = $directive->toArray()['map'];
39 |
40 | $expected = '\Cake\View\Helper\FormHelper::class';
41 | $this->assertSame($expected, (string)$map['Form']);
42 |
43 | $expected = '\Shim\View\Helper\ConfigureHelper::class';
44 | $this->assertSame($expected, (string)$map['Shim.Configure']);
45 |
46 | /** @var \IdeHelper\Generator\Directive\ExpectedArguments $directive */
47 | $directive = array_shift($result);
48 | $this->assertSame('\Cake\View\ViewBuilder::addHelper()', $directive->toArray()['method']);
49 |
50 | $list = $directive->toArray()['list'];
51 |
52 | $expected = "'Form'";
53 | $this->assertSame($expected, (string)$list['Form']);
54 |
55 | $expected = "'Shim.Configure'";
56 | $this->assertSame($expected, (string)$list['Shim.Configure']);
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/src/Command/Annotate/ComponentsCommand.php:
--------------------------------------------------------------------------------
1 | getPaths('Controller/Component');
28 | foreach ($paths as $plugin => $pluginPaths) {
29 | $this->setPlugin($plugin);
30 | foreach ($pluginPaths as $path) {
31 | $this->_components($path);
32 | }
33 | }
34 |
35 | if ($args->getOption('ci') && $this->_annotatorMadeChanges()) {
36 | return static::CODE_CHANGES;
37 | }
38 |
39 | return static::CODE_SUCCESS;
40 | }
41 |
42 | /**
43 | * @param string $folder
44 | * @return void
45 | */
46 | protected function _components(string $folder) {
47 | $this->io->out(str_replace(ROOT . DS, '', $folder), 1, ConsoleIo::VERBOSE);
48 |
49 | $folderContent = glob($folder . '*') ?: [];
50 | foreach ($folderContent as $path) {
51 | if (is_dir($path)) {
52 | $this->_components($path);
53 | } else {
54 | $name = pathinfo($path, PATHINFO_FILENAME);
55 | if ($this->_shouldSkip($name, $path)) {
56 | continue;
57 | }
58 |
59 | $this->io->out('-> ' . $name, 1, ConsoleIo::VERBOSE);
60 | $annotator = $this->getAnnotator(ComponentAnnotator::class);
61 | $annotator->annotate($path);
62 | }
63 | }
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/tests/TestCase/Generator/Task/FormHelperTaskTest.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | protected array $fixtures = [
15 | 'plugin.IdeHelper.Cars',
16 | 'plugin.IdeHelper.Wheels',
17 | ];
18 |
19 | protected FormHelperTask $task;
20 |
21 | /**
22 | * @return void
23 | */
24 | protected function setUp(): void {
25 | parent::setUp();
26 |
27 | $this->loadPlugins(['Awesome', 'Controllers', 'MyNamespace/MyPlugin', 'Relations', 'Shim', 'IdeHelper']);
28 | $this->fetchTable('Cars');
29 | $this->fetchTable('Wheels');
30 |
31 | $this->task = new FormHelperTask();
32 | }
33 |
34 | /**
35 | * @return void
36 | */
37 | protected function tearDown(): void {
38 | parent::tearDown();
39 |
40 | unset($this->task);
41 | }
42 |
43 | /**
44 | * @return void
45 | */
46 | public function testCollect() {
47 | $result = $this->task->collect();
48 |
49 | $this->assertCount(1, $result);
50 |
51 | /** @var \IdeHelper\Generator\Directive\ExpectedArguments $directive */
52 | $directive = array_shift($result);
53 | $this->assertInstanceOf(ExpectedArguments::class, $directive);
54 |
55 | $list = $directive->toArray()['list'];
56 | $list = array_map(function ($className) {
57 | return (string)$className;
58 | }, $list);
59 |
60 | $expectedList = [
61 | 'content' => "'content'",
62 | 'created' => "'created'",
63 | 'id' => "'id'",
64 | 'modified' => "'modified'",
65 | 'name' => "'name'",
66 | 'params' => "'params'",
67 | 'status' => "'status'",
68 | 'user_id' => "'user_id'",
69 | ];
70 | $this->assertSame($expectedList, $list);
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/src/CodeCompletion/CodeCompletionGenerator.php:
--------------------------------------------------------------------------------
1 | taskCollection = $taskCollection;
16 | }
17 |
18 | /**
19 | * @return array
20 | */
21 | public function generate(): array {
22 | $map = $this->taskCollection->getMap();
23 |
24 | foreach ($map as $namespace => $array) {
25 | $content = $this->buildContent($array);
26 |
27 | $template = <<path();
38 | $filename = $path . 'CodeCompletion' . $this->type($namespace) . '.php';
39 |
40 | if (!file_exists($filename) || md5_file($filename) !== md5($template)) {
41 | file_put_contents($filename, $template);
42 | }
43 | }
44 |
45 | return array_keys($map);
46 | }
47 |
48 | /**
49 | * @param array $array
50 | *
51 | * @return string
52 | */
53 | protected function buildContent(array $array): string {
54 | return implode('', $array);
55 | }
56 |
57 | /**
58 | * @param string $namespace
59 | *
60 | * @return string
61 | */
62 | protected function type(string $namespace): string {
63 | return (string)preg_replace('/[^\da-z]/i', '', $namespace);
64 | }
65 |
66 | /**
67 | * @return string
68 | */
69 | protected function path(): string {
70 | $path = Configure::read('IdeHelper.codeCompletionPath') ?: TMP;
71 | if (!is_dir($path)) {
72 | mkdir($path, 0770, true);
73 | }
74 |
75 | return $path;
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/Generator/Task/DatabaseTypeTask.php:
--------------------------------------------------------------------------------
1 |
21 | */
22 | public function collect(): array {
23 | $result = [];
24 |
25 | $types = $this->getTypes();
26 |
27 | $map = [];
28 | foreach ($types as $type => $className) {
29 | $map[$type] = ClassName::create($className);
30 | }
31 | ksort($map);
32 |
33 | $method = '\\' . static::CLASS_TYPE . '::build(0)';
34 | $directive = new Override($method, $map);
35 | $result[$directive->key()] = $directive;
36 |
37 | $list = [];
38 | foreach ($types as $type => $className) {
39 | $list[$type] = StringName::create($type);
40 | }
41 | ksort($list);
42 |
43 | $method = '\\' . static::CLASS_TYPE . '::map()';
44 | $directive = new ExpectedArguments($method, 0, $list);
45 | $result[$directive->key()] = $directive;
46 |
47 | return $result;
48 | }
49 |
50 | /**
51 | * @return array
52 | */
53 | protected function getTypes(): array {
54 | $types = [];
55 |
56 | try {
57 | $allTypes = TypeFactory::buildAll();
58 | } catch (Throwable $exception) {
59 | return $types;
60 | }
61 |
62 | foreach ($allTypes as $key => $type) {
63 | if (str_starts_with($key, 'enum-')) {
64 | continue;
65 | }
66 |
67 | $types[$key] = get_class($type);
68 | }
69 |
70 | ksort($types);
71 |
72 | return $types;
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/src/Generator/Directive/RegisterArgumentsSet.php:
--------------------------------------------------------------------------------
1 |
31 | */
32 | protected array $list;
33 |
34 | /**
35 | * @param string $set
36 | * @param array $list
37 | */
38 | public function __construct($set, array $list) {
39 | $this->set = $set;
40 | $this->list = $list;
41 | }
42 |
43 | /**
44 | * Key for sorting inside collection.
45 | *
46 | * @return string
47 | */
48 | public function key() {
49 | return $this->set . '@' . static::NAME;
50 | }
51 |
52 | /**
53 | * @return array
54 | */
55 | public function toArray() {
56 | return [
57 | 'set' => $this->set,
58 | 'list' => $this->list,
59 | ];
60 | }
61 |
62 | /**
63 | * @return string
64 | */
65 | public function build() {
66 | $set = "'" . $this->set . "'";
67 | $list = $this->buildList($this->list);
68 |
69 | $result = <<set . '\')';
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/src/Generator/Task/ValidationTask.php:
--------------------------------------------------------------------------------
1 |
19 | */
20 | protected static array $methods = [
21 | 'requirePresence' => 1,
22 | 'allowEmptyFor' => 2,
23 | 'allowEmptyString' => 2,
24 | 'allowEmptyFile' => 2,
25 | 'allowEmptyArray' => 2,
26 | 'allowEmptyDate' => 2,
27 | 'allowEmptyTime' => 2,
28 | 'allowEmptyDateTime' => 2,
29 | 'notEmptyString' => 2,
30 | 'notEmptyFile' => 2,
31 | 'notEmptyArray' => 2,
32 | 'notEmptyDate' => 2,
33 | 'notEmptyTime' => 2,
34 | 'notEmptyDateTime' => 2,
35 | ];
36 |
37 | /**
38 | * @return array
39 | */
40 | public function collect(): array {
41 | $result = [];
42 |
43 | $list = $this->getValidatorRequirePresence();
44 | $registerArgumentsSet = new RegisterArgumentsSet(static::SET_VALIDATION_WHEN, $list);
45 | $result[$registerArgumentsSet->key()] = $registerArgumentsSet;
46 |
47 | foreach (static::$methods as $method => $position) {
48 | $method = '\\' . Validator::class . '::' . $method . '()';
49 | $directive = new ExpectedArguments($method, $position, [$registerArgumentsSet]);
50 | $result[$directive->key()] = $directive;
51 | }
52 |
53 | return $result;
54 | }
55 |
56 | /**
57 | * @return array<\IdeHelper\ValueObject\ValueObjectInterface>
58 | */
59 | protected function getValidatorRequirePresence(): array {
60 | return [
61 | StringName::create('create'),
62 | StringName::create('update'),
63 | ];
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/Utility/App.php:
--------------------------------------------------------------------------------
1 | task = new ConfigureTask();
23 | }
24 |
25 | /**
26 | * @return void
27 | */
28 | public function testCollect() {
29 | $result = $this->task->collect();
30 |
31 | $this->assertCount(8, $result);
32 |
33 | /** @var \IdeHelper\Generator\Directive\RegisterArgumentsSet $directive */
34 | $directive = array_shift($result);
35 | $this->assertInstanceOf(RegisterArgumentsSet::class, $directive);
36 | $this->assertSame(ConfigureTask::SET_CONFIGURE_KEYS, $directive->toArray()['set']);
37 |
38 | /** @var \IdeHelper\Generator\Directive\ExpectedArguments $directive */
39 | $directive = array_shift($result);
40 | $this->assertSame('\Cake\Core\Configure::read()', $directive->toArray()['method']);
41 |
42 | $list = $directive->toArray()['list'];
43 | $list = array_map(function ($className) {
44 | return (string)$className;
45 | }, $list);
46 |
47 | $expected = [
48 | 'argumentsSet(\'configureKeys\')',
49 | ];
50 | $this->assertSame($expected, $list);
51 | }
52 |
53 | /**
54 | * @return void
55 | */
56 | public function testCollectKeys(): void {
57 | $result = $this->invokeMethod($this->task, 'collectKeys');
58 |
59 | $this->assertArrayHasKey('App.paths.templates', $result);
60 | $this->assertArrayNotHasKey('paths', $result);
61 | $this->assertArrayNotHasKey('templates', $result);
62 |
63 | $this->assertSame('\'debug\'', (string)$result['debug']);
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/Generator/Task/FormHelperTask.php:
--------------------------------------------------------------------------------
1 |
19 | */
20 | public function collect(): array {
21 | $result = [];
22 |
23 | $list = $this->collectFieldNames();
24 |
25 | ksort($list);
26 |
27 | $method = '\\' . static::CLASS_FORM_HELPER . '::control()';
28 | $directive = new ExpectedArguments($method, 0, $list);
29 | $result[$directive->key()] = $directive;
30 |
31 | return $result;
32 | }
33 |
34 | /**
35 | * @return array
36 | */
37 | protected function collectFieldNames(): array {
38 | $models = $this->collectModels();
39 |
40 | $allFields = [];
41 | foreach ($models as $model => $className) {
42 | /** @phpstan-var class-string