├── .editorconfig
├── .gitattributes
├── .gitignore
├── Makefile
├── README.md
├── app
├── Application
│ └── UseCases
│ │ ├── InspectHandler.php
│ │ ├── InspectInput.php
│ │ └── InspectOutput.php
├── Domain
│ ├── Aggregators
│ │ ├── FileAggregator.php
│ │ └── WordAggregator.php
│ ├── Entities
│ │ ├── FileEntity.php
│ │ └── WordEntity.php
│ ├── Ports
│ │ ├── AnalyzeService.php
│ │ └── FileService.php
│ └── ValueObjects
│ │ └── Path.php
├── Infrastructure
│ ├── Analyze
│ │ ├── Adapters
│ │ │ └── AnalyzeServiceAdapter.php
│ │ ├── Mappers
│ │ │ └── AnalyzeServiceMapper.php
│ │ ├── Nodes
│ │ │ ├── NodeExtractor.php
│ │ │ └── NodeValidator.php
│ │ └── Visitors
│ │ │ └── ClassVisitor.php
│ └── File
│ │ ├── Adapters
│ │ └── FileServiceAdapter.php
│ │ └── Mappers
│ │ └── FileFinderMapper.php
├── Presenter
│ └── Commands
│ │ ├── .gitkeep
│ │ ├── InspectCommand.php
│ │ ├── InspectCommandInput.php
│ │ └── InspectCommandOutput.php
└── Providers
│ └── AppServiceProvider.php
├── bootstrap
└── app.php
├── box.json
├── builds
└── php-wording-detector
├── composer.json
├── composer.lock
├── config
├── app.php
├── commands.php
└── view.php
├── php-wording-detector
├── phpunit.xml.dist
├── resources
└── views
│ └── inspect.blade.php
├── storage
├── app
│ └── .gitignore
└── framework
│ └── views
│ └── .gitignore
└── tests
├── Builders
├── FileAggregatorBuilder.php
├── builders.php
└── stubs
│ └── Foo.php
├── CreatesApplication.php
├── TestCase.php
└── Unit
├── Application
└── InspectHandlerTest.php
├── Domain
├── Aggregators
│ └── WordAggregatorTest.php
└── Entities
│ └── WordEntityTest.php
└── Infrastructure
└── Nodes
├── NodeExtractorTest.php
└── NodeValidatorTest.php
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 4
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
14 | [*.yml]
15 | indent_style = space
16 | indent_size = 2
17 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 | /.github export-ignore
3 | .scrutinizer.yml export-ignore
4 | BACKERS.md export-ignore
5 | CONTRIBUTING.md export-ignore
6 | CHANGELOG.md export-ignore
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | /.idea
3 | /.vscode
4 | /.vagrant
5 | .phpunit.result.cache
6 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | test:
2 | vendor/bin/phpunit
3 |
4 | cov:
5 | vendor/bin/phpunit --coverage-text | grep -A 4 "Summary:"
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [](https://github.com/DeGraciaMathieu/php-wording-detector/actions/workflows/phpunit.yml)
6 | 
7 | 
8 |
9 | # php-wording-detector
10 |
11 | Simple tool to analyze and split the words contained in your code to check your DDD approach.
12 |
13 | # Installation
14 |
15 | ```
16 | Requires >= PHP 8.1
17 | ```
18 |
19 | ## Phar
20 | This tool is distributed as a [PHP Archive (PHAR)](https://www.php.net/phar):
21 |
22 | ```
23 | wget https://github.com/DeGraciaMathieu/php-wording-detector/raw/master/builds/php-wording-detector
24 | ```
25 |
26 | ```
27 | php php-wording-detector --version
28 | ```
29 |
30 | ## Composer
31 | Alternately, you can directly use composer :
32 |
33 | ```
34 | composer require degraciamathieu/php-wording-detector --dev
35 | ```
36 | # Usage
37 |
38 | By default only variables are analyzed :
39 |
40 | ```
41 | php php-wording-detector inspect {path}
42 | ```
43 |
44 | The `--with-method` option allows to parse the name of the methods :
45 |
46 | ```
47 | php php-wording-detector inspect {path} --with-method
48 | ```
49 |
50 | ```
51 | $ php php-wording-detector inspect app/Domains/Activity
52 | ❀ PHP Wording Detector ❀
53 | +-------------+-----------------------+-------------+
54 | | total words | total distincts words | average use |
55 | +-------------+-----------------------+-------------+
56 | | 2'166 | 52 | 42 |
57 | +-------------+-----------------------+-------------+
58 | +--------------+-------+------------+
59 | | words | usage | percentage |
60 | +--------------+-------+------------+
61 | | activity | 667 | 31% |
62 | | data | 154 | 7% |
63 | | code | 150 | 7% |
64 | | item | 143 | 7% |
65 | | query | 128 | 6% |
66 | | request | 88 | 4% |
67 | | mode | 85 | 4% |
68 | | translations | 78 | 4% |
69 | | id | 77 | 4% |
70 | | type | 63 | 3% |
71 | | new | 46 | 2% |
72 | | product | 41 | 2% |
73 | | translation | 41 | 2% |
74 | | types | 40 | 2% |
75 | | master | 33 | 2% |
76 | | filters | 29 | 1% |
77 | | language | 24 | 1% |
78 | | builder | 23 | 1% |
79 | | items | 22 | 1% |
80 | | section | 21 | under 1% |
81 | +--------------+-------+------------+
82 | ```
83 |
--------------------------------------------------------------------------------
/app/Application/UseCases/InspectHandler.php:
--------------------------------------------------------------------------------
1 | hello();
27 |
28 | $fileAggregator = $this->fileService->all(
29 | path: $input->path(),
30 | );
31 |
32 | $wordsAggregator = $this->analyzeService->getWords(
33 | fileAggregator: $fileAggregator,
34 | withMethod: $input->withMethod(),
35 | );
36 |
37 | $output->present($wordsAggregator);
38 |
39 | } catch (Throwable $th) {
40 | $output->error($th);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/Application/UseCases/InspectInput.php:
--------------------------------------------------------------------------------
1 | files[] = $fileEntity;
14 | }
15 |
16 | public function files(): array
17 | {
18 | return $this->files;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/Domain/Aggregators/WordAggregator.php:
--------------------------------------------------------------------------------
1 | canBeIgnored()) {
15 | return;
16 | }
17 |
18 | $this->increment($wordEntity);
19 | }
20 |
21 | public function sort(): void
22 | {
23 | uasort($this->words, function ($a, $b) {
24 | return $this->sortWordsByDesc($a, $b);
25 | });
26 | }
27 |
28 | public function words(): array
29 | {
30 | return $this->words;
31 | }
32 |
33 | private function increment(WordEntity $wordEntity): void
34 | {
35 | $word = $wordEntity->value();
36 |
37 | /**
38 | * Wow, it works well, don't judge me
39 | */
40 | @$this->words[$word]++;
41 | }
42 |
43 | private function sortWordsByDesc($a, $b): int
44 | {
45 | if ($a == $b) {
46 | return 0;
47 | }
48 |
49 | return ($a < $b) ? 1 : -1;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/Domain/Entities/FileEntity.php:
--------------------------------------------------------------------------------
1 | value === 'this';
21 | }
22 |
23 | public function value(): string
24 | {
25 | return $this->value;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/Domain/Ports/AnalyzeService.php:
--------------------------------------------------------------------------------
1 | value;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/Infrastructure/Analyze/Adapters/AnalyzeServiceAdapter.php:
--------------------------------------------------------------------------------
1 | files();
24 |
25 | foreach ($files as $file) {
26 | $this->getFileWords($file, $withMethod);
27 | }
28 |
29 | return $this->mapper->aggregate();
30 | }
31 |
32 | private function getFileWords(FileEntity $file, bool $withMethod)
33 | {
34 | $traverser = new NodeTraverser();
35 |
36 | $visitor = $this->addVisitor($traverser, $withMethod);
37 |
38 | $this->traverseVisitor($traverser, $file);
39 |
40 | $this->mapper->map($visitor->words);
41 | }
42 |
43 | private function addVisitor(NodeTraverser $traverser, bool $withMethod): ClassVisitor
44 | {
45 | $visitor = new ClassVisitor($withMethod);
46 |
47 | $traverser->addVisitor($visitor);
48 |
49 | return $visitor;
50 | }
51 |
52 | private function traverseVisitor(NodeTraverser $traverser, $file): void
53 | {
54 | $contents = $file->contents;
55 |
56 | $nodes = $this->parser->parse($contents);
57 |
58 | $traverser->traverse($nodes);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/Infrastructure/Analyze/Mappers/AnalyzeServiceMapper.php:
--------------------------------------------------------------------------------
1 | add($word);
19 | }
20 | }
21 |
22 | public function aggregate(): WordAggregator
23 | {
24 | return $this->wordAggregator;
25 | }
26 |
27 | private function add(string $word): void
28 | {
29 | $this->wordAggregator->add(
30 | WordEntity::from($word),
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/Infrastructure/Analyze/Nodes/NodeExtractor.php:
--------------------------------------------------------------------------------
1 | name;
22 |
23 | if ($name instanceof Variable) {
24 | return $name->name;
25 | }
26 |
27 | return $name;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/Infrastructure/Analyze/Nodes/NodeValidator.php:
--------------------------------------------------------------------------------
1 | mustParseThisNode($node)) {
21 |
22 | $words = NodeExtractor::cutNameIntoWords($node);
23 |
24 | $this->words = array_merge($this->words, $words);
25 | }
26 | }
27 |
28 | private function mustParseThisNode(Node $node): bool
29 | {
30 | if (NodeValidator::isAVariable($node)) {
31 | return true;
32 | }
33 |
34 | return $this->withMethod
35 | ? NodeValidator::isAMethod($node)
36 | : false;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/Infrastructure/File/Adapters/FileServiceAdapter.php:
--------------------------------------------------------------------------------
1 | getFiles(
24 | $path->value(),
25 | );
26 |
27 | return $this->mapper->map($files);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/Infrastructure/File/Mappers/FileFinderMapper.php:
--------------------------------------------------------------------------------
1 | makeEntity($file);
21 |
22 | $this->fileAggregator->add($fileEntity);
23 | }
24 |
25 | return $this->fileAggregator;
26 | }
27 |
28 | private function makeEntity(File $file): FileEntity
29 | {
30 | return FileEntity::from([
31 | 'fullPath' => $file->fullPath,
32 | 'displayPath' => $file->displayPath,
33 | 'contents' => $file->contents(),
34 | ]);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/Presenter/Commands/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeGraciaMathieu/php-wording-detector/4a5b25ebeae0287beb8725b2264bdfca7e6211eb/app/Presenter/Commands/.gitkeep
--------------------------------------------------------------------------------
/app/Presenter/Commands/InspectCommand.php:
--------------------------------------------------------------------------------
1 | handle(
36 | new InspectCommandInput(
37 | path: $this->argument('path'),
38 | withMethod: $this->option('with-method'),
39 | ),
40 | $inspectCommandOutput
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/Presenter/Commands/InspectCommandInput.php:
--------------------------------------------------------------------------------
1 | path);
18 | }
19 |
20 | public function withMethod(): bool
21 | {
22 | return $this->withMethod;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/Presenter/Commands/InspectCommandOutput.php:
--------------------------------------------------------------------------------
1 | sort();
31 |
32 | $html = $this->makeHtml($wordAggregator);
33 |
34 | $this->renderHtml($html);
35 | }
36 |
37 | public function error(Throwable $th): void
38 | {
39 | error($th);
40 | }
41 |
42 | private function makeHtml(WordAggregator $wordAggregator): ViewContract
43 | {
44 | $words = $wordAggregator->words();
45 |
46 | return $this->view->make('inspect', [
47 | 'words' => $words,
48 | ]);
49 | }
50 |
51 | private function renderHtml(ViewContract $html): void
52 | {
53 | $this->htmlRenderer->render($html, OutputInterface::OUTPUT_NORMAL);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/Providers/AppServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->singleton(Parser::class, function ($app) {
29 |
30 | $parserFactory = new ParserFactory();
31 |
32 | return $parserFactory->create(ParserFactory::PREFER_PHP7);
33 | });
34 |
35 | $this->app->bind(FileService::class, FileServiceAdapter::class);
36 | $this->app->bind(AnalyzeService::class, AnalyzeServiceAdapter::class);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | singleton(
30 | Illuminate\Contracts\Console\Kernel::class,
31 | LaravelZero\Framework\Kernel::class
32 | );
33 |
34 | $app->singleton(
35 | Illuminate\Contracts\Debug\ExceptionHandler::class,
36 | Illuminate\Foundation\Exceptions\Handler::class
37 | );
38 |
39 | /*
40 | |--------------------------------------------------------------------------
41 | | Return The Application
42 | |--------------------------------------------------------------------------
43 | |
44 | | This script returns the application instance. The instance is given to
45 | | the calling script so we can separate the building of the instances
46 | | from the actual running of the application and sending responses.
47 | |
48 | */
49 |
50 | return $app;
51 |
--------------------------------------------------------------------------------
/box.json:
--------------------------------------------------------------------------------
1 | {
2 | "chmod": "0755",
3 | "directories": [
4 | "app",
5 | "bootstrap",
6 | "config",
7 | "vendor",
8 | "resources"
9 | ],
10 | "files": [
11 | "composer.json"
12 | ],
13 | "exclude-composer-files": false,
14 | "compression": "GZ",
15 | "compactors": [
16 | "KevinGH\\Box\\Compactor\\Php",
17 | "KevinGH\\Box\\Compactor\\Json"
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/builds/php-wording-detector:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeGraciaMathieu/php-wording-detector/4a5b25ebeae0287beb8725b2264bdfca7e6211eb/builds/php-wording-detector
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "degraciamathieu/php-wording-detector",
3 | "description": "check your DDD approach by analyzing your variables",
4 | "keywords": ["php", "quality", "ci", "metrics", "static-analyzer", "ddd"],
5 | "type": "package",
6 | "license": "MIT",
7 | "support": {
8 | "issues": "https://github.com/DeGraciaMathieu/php-wording-detector/issues",
9 | "source": "https://github.com/DeGraciaMathieu/php-wording-detector"
10 | },
11 | "authors": [
12 | {
13 | "name": "De Gracia Mathieu",
14 | "email": "dev@degracia-mathieu.fr"
15 | }
16 | ],
17 | "require": {
18 | "php": "^8.1",
19 | "degraciamathieu/php-file-explorer": "0.4.*",
20 | "illuminate/view": "^10.0",
21 | "laravel-zero/framework": "^10.0",
22 | "laravel/prompts": "^0.1.21",
23 | "nikic/php-parser": "^4.13",
24 | "nunomaduro/termwind": "^1.15"
25 | },
26 | "require-dev": {
27 | "laravel/pint": "^1.5",
28 | "mockery/mockery": "^1.5.1",
29 | "phpunit/phpunit": "^10.0"
30 | },
31 | "autoload": {
32 | "psr-4": {
33 | "App\\": "app/",
34 | "Database\\Factories\\": "database/factories/",
35 | "Database\\Seeders\\": "database/seeders/"
36 | }
37 | },
38 | "autoload-dev": {
39 | "files": [
40 | "./tests/Builders/builders.php"
41 | ],
42 | "psr-4": {
43 | "Tests\\": "tests/"
44 | }
45 | },
46 | "config": {
47 | "preferred-install": "dist",
48 | "sort-packages": true,
49 | "optimize-autoloader": true,
50 | "allow-plugins": {
51 | "pestphp/pest-plugin": true
52 | }
53 | },
54 | "minimum-stability": "stable",
55 | "prefer-stable": true,
56 | "bin": ["php-wording-detector"]
57 | }
58 |
--------------------------------------------------------------------------------
/config/app.php:
--------------------------------------------------------------------------------
1 | 'Php-wording-detector',
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Application Version
21 | |--------------------------------------------------------------------------
22 | |
23 | | This value determines the "version" your application is currently running
24 | | in. You may want to follow the "Semantic Versioning" - Given a version
25 | | number MAJOR.MINOR.PATCH when an update happens: https://semver.org.
26 | |
27 | */
28 |
29 | 'version' => app('git.version'),
30 |
31 | /*
32 | |--------------------------------------------------------------------------
33 | | Application Environment
34 | |--------------------------------------------------------------------------
35 | |
36 | | This value determines the "environment" your application is currently
37 | | running in. This may determine how you prefer to configure various
38 | | services the application utilizes. This can be overridden using
39 | | the global command line "--env" option when calling commands.
40 | |
41 | */
42 |
43 | 'env' => 'development',
44 |
45 | /*
46 | |--------------------------------------------------------------------------
47 | | Application Timezone
48 | |--------------------------------------------------------------------------
49 | |
50 | | Here you may specify the default timezone for your application, which
51 | | will be used by the PHP date and date-time functions. We have gone
52 | | ahead and set this to a sensible default for you out of the box.
53 | |
54 | */
55 |
56 | 'timezone' => 'UTC',
57 |
58 | /*
59 | |--------------------------------------------------------------------------
60 | | Autoloaded Service Providers
61 | |--------------------------------------------------------------------------
62 | |
63 | | The service providers listed here will be automatically loaded on the
64 | | request to your application. Feel free to add your own services to
65 | | this array to grant expanded functionality to your applications.
66 | |
67 | */
68 |
69 | 'providers' => [
70 | App\Providers\AppServiceProvider::class,
71 | ],
72 |
73 | ];
74 |
--------------------------------------------------------------------------------
/config/commands.php:
--------------------------------------------------------------------------------
1 | NunoMaduro\LaravelConsoleSummary\SummaryCommand::class,
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Commands Paths
21 | |--------------------------------------------------------------------------
22 | |
23 | | This value determines the "paths" that should be loaded by the console's
24 | | kernel. Foreach "path" present on the array provided below the kernel
25 | | will extract all "Illuminate\Console\Command" based class commands.
26 | |
27 | */
28 |
29 | 'paths' => [app_path('Presenter/Commands')],
30 |
31 | /*
32 | |--------------------------------------------------------------------------
33 | | Added Commands
34 | |--------------------------------------------------------------------------
35 | |
36 | | You may want to include a single command class without having to load an
37 | | entire folder. Here you can specify which commands should be added to
38 | | your list of commands. The console's kernel will try to load them.
39 | |
40 | */
41 |
42 | 'add' => [
43 | // ..
44 | ],
45 |
46 | /*
47 | |--------------------------------------------------------------------------
48 | | Hidden Commands
49 | |--------------------------------------------------------------------------
50 | |
51 | | Your application commands will always be visible on the application list
52 | | of commands. But you can still make them "hidden" specifying an array
53 | | of commands below. All "hidden" commands can still be run/executed.
54 | |
55 | */
56 |
57 | 'hidden' => [
58 | NunoMaduro\LaravelConsoleSummary\SummaryCommand::class,
59 | Symfony\Component\Console\Command\DumpCompletionCommand::class,
60 | Symfony\Component\Console\Command\HelpCommand::class,
61 | Illuminate\Console\Scheduling\ScheduleRunCommand::class,
62 | Illuminate\Console\Scheduling\ScheduleListCommand::class,
63 | Illuminate\Console\Scheduling\ScheduleFinishCommand::class,
64 | Illuminate\Foundation\Console\VendorPublishCommand::class,
65 | LaravelZero\Framework\Commands\StubPublishCommand::class,
66 | ],
67 |
68 | /*
69 | |--------------------------------------------------------------------------
70 | | Removed Commands
71 | |--------------------------------------------------------------------------
72 | |
73 | | Do you have a service provider that loads a list of commands that
74 | | you don't need? No problem. Laravel Zero allows you to specify
75 | | below a list of commands that you don't to see in your app.
76 | |
77 | */
78 |
79 | 'remove' => [
80 | // ..
81 | ],
82 |
83 | ];
84 |
--------------------------------------------------------------------------------
/config/view.php:
--------------------------------------------------------------------------------
1 | [
5 | resource_path('views'),
6 | ],
7 | 'compiled' => \Phar::running()
8 | ? getcwd()
9 | : env('VIEW_COMPILED_PATH', realpath(storage_path('framework/views'))),
10 | ];
11 |
--------------------------------------------------------------------------------
/php-wording-detector:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | make(Illuminate\Contracts\Console\Kernel::class);
34 |
35 | $status = $kernel->handle(
36 | $input = new Symfony\Component\Console\Input\ArgvInput,
37 | new Symfony\Component\Console\Output\ConsoleOutput
38 | );
39 |
40 | /*
41 | |--------------------------------------------------------------------------
42 | | Shutdown The Application
43 | |--------------------------------------------------------------------------
44 | |
45 | | Once Artisan has finished running, we will fire off the shutdown events
46 | | so that any final work may be done by the application before we shut
47 | | down the process. This is the last thing to happen to the request.
48 | |
49 | */
50 |
51 | $kernel->terminate($input, $status);
52 |
53 | exit($status);
54 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ./tests/Unit
6 |
7 |
8 |
9 |
10 |
11 | ./app
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/resources/views/inspect.blade.php:
--------------------------------------------------------------------------------
1 |
2 | @if($words)
3 |
4 |
5 |
6 | total words |
7 | total distincts words |
8 | average use |
9 |
10 |
11 |
12 | {{ number_format(array_sum($words), 0, ".", "'") }} |
13 | {{ count($words) }} |
14 | {{ number_format(array_sum($words) / count($words)) }} |
15 |
16 |
17 |
18 |
19 |
20 | words |
21 | usage |
22 | percentage |
23 |
24 |
25 | @foreach(array_slice($words, 0, 20) as $name => $usage)
26 |
27 | {{ $name }} |
28 | {{ $usage }} |
29 |
30 | @php
31 | $percentage = $usage * 100 / array_sum($words);
32 | @endphp
33 | @if($percentage < 1)
34 | under 1%
35 | @else
36 | {{ number_format($percentage) }}%
37 | @endif
38 | |
39 |
40 | @endforeach
41 |
42 | @else
43 | No files found.
44 | @endif
45 |
46 |
--------------------------------------------------------------------------------
/storage/app/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/storage/framework/views/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/tests/Builders/FileAggregatorBuilder.php:
--------------------------------------------------------------------------------
1 | fileAggregator = new FileAggregator();
15 | }
16 |
17 | public function withFile(): FileAggregatorBuilder
18 | {
19 | $this->fileAggregator->add($this->makeFileEntity());
20 |
21 | return $this;
22 | }
23 |
24 | public function build(): FileAggregator
25 | {
26 | return $this->fileAggregator;
27 | }
28 |
29 | private function makeFileEntity(): FileEntity
30 | {
31 | return FileEntity::from([
32 | 'fullPath' => '.',
33 | 'displayPath' => '.',
34 | 'contents' => file_get_contents(__DIR__ . '/stubs/Foo.php'),
35 | ]);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tests/Builders/builders.php:
--------------------------------------------------------------------------------
1 | make(Kernel::class)->bootstrap();
18 |
19 | return $app;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | mockFileService(),
24 | $this->mockAnalyzeService(),
25 | );
26 |
27 | $handler->handle(
28 | $this->mockInputInterface(),
29 | $this->mockOutputInterface(),
30 | );
31 | }
32 |
33 | private function mockFileService(): FileService
34 | {
35 | $fileService = Mockery::mock(FileService::class);
36 |
37 | $fileService->expects('all')->andReturn($this->makeFileAggregator());
38 |
39 | return $fileService;
40 | }
41 |
42 | private function makeFileAggregator(): FileAggregator
43 | {
44 | return fileAggregator()->withFile()->build();
45 | }
46 |
47 | private function mockAnalyzeService(): AnalyzeService
48 | {
49 | return Mockery::mock(AnalyzeService::class)->shouldIgnoreMissing();
50 | }
51 |
52 | private function mockInputInterface(): InspectInput
53 | {
54 | $input = Mockery::mock(InspectInput::class);
55 |
56 | $input->expects('path')->andReturn(Path::from('.'));
57 | $input->expects('withMethod')->andReturn(false);
58 |
59 | return $input;
60 | }
61 |
62 | private function mockOutputInterface(): InspectOutput
63 | {
64 | $output = Mockery::mock(InspectOutput::class)->shouldIgnoreMissing();
65 |
66 | $output->expects('error')
67 | ->never()
68 | ->withAnyArgs()
69 | ->andReturnUsing(function ($th) {
70 | $this->fail('Unexpected call to error with exception: ' . $th->getMessage());
71 | });
72 |
73 | $output->expects('present')
74 | ->withArgs(function ($argument) {
75 | return $argument instanceof WordAggregator;
76 | });
77 |
78 | return $output;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/tests/Unit/Domain/Aggregators/WordAggregatorTest.php:
--------------------------------------------------------------------------------
1 | add(WordEntity::from('a'));
18 | $aggregator->add(WordEntity::from('b'));
19 | $aggregator->add(WordEntity::from('b'));
20 |
21 | $this->assertEquals([
22 | 'a' => 1,
23 | 'b' => 2,
24 | ], $aggregator->words());
25 | }
26 |
27 | #[Test]
28 | public function it_can_sort_words(): void
29 | {
30 | $aggregator = new WordAggregator();
31 |
32 | $aggregator->add(WordEntity::from('a'));
33 | $aggregator->add(WordEntity::from('b'));
34 | $aggregator->add(WordEntity::from('b'));
35 |
36 | $aggregator->sort();
37 |
38 | $this->assertEquals([
39 | 'b' => 2,
40 | 'a' => 1,
41 | ], $aggregator->words());
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/tests/Unit/Domain/Entities/WordEntityTest.php:
--------------------------------------------------------------------------------
1 | assertSame($word->value(), 'foo');
18 | }
19 |
20 | #[Test]
21 | public function it_accepts_common_words(): void
22 | {
23 | $word = WordEntity::from('Foo');
24 |
25 | $this->assertFalse($word->canBeIgnored());
26 | }
27 |
28 |
29 | #[Test]
30 | #[DataProvider('words')]
31 | public function it_can_ignore_some_words(string $word): void
32 | {
33 | $word = WordEntity::from($word);
34 |
35 | $this->assertTrue($word->canBeIgnored());
36 | }
37 |
38 | public static function words(): array
39 | {
40 | return [
41 | ['this'],
42 | ];
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tests/Unit/Infrastructure/Nodes/NodeExtractorTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($expectedWords, $words);
26 | }
27 |
28 | /**
29 | * @test
30 | * @dataProvider nameProvider
31 | */
32 | public function it_can_cut_method_into_words(string $name, array $expectedWords): void
33 | {
34 | $method = new ClassMethod(
35 | name: $name,
36 | );
37 |
38 | $words = NodeExtractor::cutNameIntoWords($method);
39 |
40 | $this->assertEquals($expectedWords, $words);
41 | }
42 |
43 | public static function nameProvider(): array
44 | {
45 | return [
46 | ['getAllFoo', ['get', 'All', 'Foo']],
47 | ['get_all_foo', ['get', 'all', 'foo']],
48 | ['Get_All_Foo', ['Get', 'All', 'Foo']],
49 | ['get_All_Foo', ['get', 'All', 'Foo']],
50 | ['getallfoo', ['getallfoo']],
51 | ['get', ['get']],
52 | ];
53 | }
54 |
55 | /**
56 | * @test
57 | */
58 | public function it_can_get_the_name_even_if_its_a_variable(): void
59 | {
60 | $variable = new Variable(
61 | name: new Variable(
62 | name: 'getAllFoo',
63 | ),
64 | );
65 |
66 | $words = NodeExtractor::cutNameIntoWords($variable);
67 |
68 | $this->assertEquals(['get', 'All', 'Foo'], $words);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/tests/Unit/Infrastructure/Nodes/NodeValidatorTest.php:
--------------------------------------------------------------------------------
1 | assertTrue($isAVariable);
26 | }
27 |
28 | /**
29 | * @test
30 | */
31 | public function it_able_to_check_node_is_a_method(): void
32 | {
33 | $method = new ClassMethod(
34 | name: 'getAllFoo',
35 | );
36 |
37 | $isAVariable = NodeValidator::isAMethod($method);
38 |
39 | $this->assertTrue($isAVariable);
40 | }
41 |
42 | /**
43 | * @test
44 | */
45 | public function it_able_to_handle_an_unexpected_node(): void
46 | {
47 | $variable = new Array_(
48 | items: [],
49 | attributes: [],
50 | );
51 |
52 | $isAVariable = NodeValidator::isAVariable($variable);
53 |
54 | $this->assertFalse($isAVariable);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------