Skip to content

Commit

Permalink
Composer dependency analyser (#286)
Browse files Browse the repository at this point in the history
1. Install [shipmonk/composer-dependency-analyser](https://github.com/shipmonk-rnd/composer-dependency-analyser)
2. Add missing `require`s
3. Add configuration
4. Also check `services.neon` and `extension.neon` and add the classes to the config
  • Loading branch information
spaze authored Mar 8, 2024
2 parents d020313 + de4df3c commit de471f2
Show file tree
Hide file tree
Showing 40 changed files with 3,487 additions and 13 deletions.
14 changes: 14 additions & 0 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,17 @@ jobs:
coverage: none
php-version: ${{ matrix.php-version }}
- run: make --directory=site psalm

composer-dependency-analyser:
runs-on: ubuntu-latest
strategy:
matrix:
php-version:
- "8.3"
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
coverage: none
php-version: ${{ matrix.php-version }}
- run: make --directory=site composer-dependency-analyser
7 changes: 5 additions & 2 deletions site/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.PHONY: test audit cs-fix check-file-patterns check-makefile lint-php lint-latte lint-neon lint-xml lint-xml-auto-install phpcs phpstan phpstan-latte-templates phpstan-vendor psalm tester tester-include-skipped gitleaks
.PHONY: test audit cs-fix check-file-patterns check-makefile lint-php lint-latte lint-neon lint-xml lint-xml-auto-install phpcs phpstan phpstan-latte-templates phpstan-vendor psalm tester tester-include-skipped gitleaks composer-dependency-analyser

test: audit check-file-patterns check-makefile lint-php lint-latte lint-neon lint-xml phpcs phpstan tester psalm phpstan-vendor
test: audit check-file-patterns check-makefile lint-php lint-latte lint-neon lint-xml phpcs phpstan tester psalm phpstan-vendor composer-dependency-analyser

audit:
composer audit
Expand Down Expand Up @@ -53,3 +53,6 @@ tester-include-skipped:

gitleaks:
gitleaks detect --verbose --source $(realpath ..)

composer-dependency-analyser:
vendor/bin/composer-dependency-analyser --verbose
5 changes: 1 addition & 4 deletions site/app/Application/Bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
use Nette\CommandLine\Parser;
use Nette\DI\Container;
use PHP_Parallel_Lint\PhpConsoleColor\ConsoleColor;
use Tester\Environment;

class Bootstrap
{
Expand Down Expand Up @@ -56,9 +55,7 @@ public static function bootTest(): Container
$configurator->addStaticParameters([
'wwwDir' => self::SITE_DIR . '/tests',
]);
$container = $configurator->createContainer();
Environment::setup();
return $container;
return $configurator->createContainer();
}


Expand Down
81 changes: 81 additions & 0 deletions site/app/DependencyInjection/DiServices.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php
declare(strict_types = 1);

namespace MichalSpacekCz\DependencyInjection;

use MichalSpacekCz\DependencyInjection\Exceptions\DiServicesConfigInvalidException;
use Nette\Neon\Entity;
use Nette\Neon\Neon;

class DiServices
{

/** @var array<non-empty-string, non-empty-lowercase-string> */
private array $configFiles = [
__DIR__ . '/../../config/services.neon' => 'services',
__DIR__ . '/../../config/extensions.neon' => 'extensions',
];


/**
* @return list<class-string>
*/
public function getAllClasses(): array
{
$allServices = [];
foreach ($this->configFiles as $file => $section) {
$decoded = Neon::decodeFile($file);
if (!is_array($decoded)) {
throw new DiServicesConfigInvalidException($file, null, 'not an array');
}
if (!isset($decoded[$section])) {
throw new DiServicesConfigInvalidException($file, $section, "section doesn't exist");
}
$services = $decoded[$section];
if (!is_array($services)) {
throw new DiServicesConfigInvalidException($file, $section, "section not iterable");
}
$services = array_filter($services, function (mixed $value): bool {
return is_string($value) || is_array($value) || $value instanceof Entity;
});
foreach ($services as $service) {
$classString = $this->getString($service, $file, $section);
if (str_starts_with($classString, '@')) {
continue;
}
if (class_exists($classString) || interface_exists($classString)) {
$allServices[] = $classString;
} else {
throw new DiServicesConfigInvalidException($file, $section, "class or interface '{$classString}' doesn't exist");
}
}
}
return $allServices;
}


/**
* @param string|Entity|array<array-key, mixed> $item
*/
private function getString(string|Entity|array $item, string $file, string $section): string
{
if (is_string($item)) {
return $item;
} elseif (is_array($item)) {
if (isset($item['create'])) {
if (is_string($item['create'])) {
return $item['create'];
} elseif ($item['create'] instanceof Entity && is_string($item['create']->value)) {
return $item['create']->value;
}
} elseif (isset($item['type']) && is_string($item['type'])) {
return $item['type'];
}
} elseif (is_string($item->value)) {
return $item->value;
}
$message = is_array($item) ? sprintf("Unsupported array '%s'", json_encode($item)) : sprintf("Unsupported item '%s'", get_debug_type($item));
throw new DiServicesConfigInvalidException($file, $section, $message);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php
declare(strict_types = 1);

namespace MichalSpacekCz\DependencyInjection\Exceptions;

use Exception;
use Throwable;

class DiServicesConfigInvalidException extends Exception
{

public function __construct(?string $file, ?string $section, string $message, ?Throwable $previous = null)
{
$ident = $file !== null ? "{$file}:" : '';
if ($section !== null) {
$ident .= "{$section}:";
}
parent::__construct("{$ident} {$message}", previous: $previous);
}

}
1 change: 1 addition & 0 deletions site/app/Test/TestCaseRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public static function run(string $test): void
try {
$method = new ReflectionMethod($test, '__construct');
$container = Bootstrap::bootTest();
Environment::setup();
foreach ($method->getParameters() as $parameter) {
$type = Type::fromReflection($parameter);
$paramIdent = "Parameter #{$parameter->getPosition()} \${$parameter->getName()}";
Expand Down
24 changes: 24 additions & 0 deletions site/composer-dependency-analyser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php
declare(strict_types = 1);

use MichalSpacekCz\Application\Bootstrap;
use MichalSpacekCz\Application\Cli\NoCliArgs;
use MichalSpacekCz\DependencyInjection\DiServices;
use ShipMonk\ComposerDependencyAnalyser\Config\Configuration;
use ShipMonk\ComposerDependencyAnalyser\Config\ErrorType;

$allClasses = Bootstrap::bootCli(NoCliArgs::class)->getByType(DiServices::class)->getAllClasses();

return (new Configuration())
// Add classes from services.neon and extensions.neon
->addForceUsedSymbols($allClasses)

// Attributes used for development only
->ignoreErrorsOnPackage('jetbrains/phpstorm-attributes', [ErrorType::DEV_DEPENDENCY_IN_PROD])

// It's used, believe me
->ignoreErrorsOnPackage('latte/latte', [ErrorType::UNUSED_DEPENDENCY])

// TestCaseRunner is used only in tests
->ignoreErrorsOnPackageAndPath('nette/tester', __DIR__ . '/app/Test/TestCaseRunner.php', [ErrorType::DEV_DEPENDENCY_IN_PROD]);
;
6 changes: 5 additions & 1 deletion site/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@
"latte/latte": "^3.0.3",
"nette/application": "^3.1.10",
"nette/bootstrap": "^3.1.2",
"nette/caching": "^3.2",
"nette/command-line": "^1.6",
"nette/component-model": "^3.0.2",
"nette/database": "^3.1.5",
"nette/di": "^3.1.8",
"nette/forms": "^3.1.7",
"nette/http": "3.2.4",
"nette/mail": "^4.0",
"nette/neon": "^3.3.3",
"nette/routing": "^3.1",
"nette/schema": "^1.3",
"nette/security": "^3.1.8",
"nette/utils": "^4.0",
"paragonie/halite": "^5.1",
Expand All @@ -41,6 +43,7 @@
"spaze/sri-macros": "^2.0",
"spaze/svg-icons-latte": "^1.0",
"symfony/cache": "^7.0",
"symfony/cache-contracts": "^3.4",
"texy/texy": "^3.1.7",
"tracy/tracy": "^2.9.4"
},
Expand All @@ -65,6 +68,7 @@
"phpstan/phpstan-nette": "^1.2.1",
"psalm/phar": "^5.14",
"roave/security-advisories": "dev-latest",
"shipmonk/composer-dependency-analyser": "^1.3",
"spaze/coding-standard": "^1.7.1",
"spaze/phpcs-phar": "^3.8",
"spaze/phpstan-disallowed-calls": "^3.0",
Expand Down
64 changes: 63 additions & 1 deletion site/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions site/config/services.neon
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ services:
- MichalSpacekCz\DateTime\DateTimeFactory
- MichalSpacekCz\DateTime\DateTimeFormatter(@translation.translator::getDefaultLocale())
- MichalSpacekCz\DateTime\DateTimeZoneFactory
- MichalSpacekCz\DependencyInjection\DiServices
- MichalSpacekCz\EasterEgg\CrLfUrlInjections
- MichalSpacekCz\EasterEgg\FourOhFourButFound
- MichalSpacekCz\EasterEgg\NetteCve202015227
Expand Down
3 changes: 3 additions & 0 deletions site/phpstan-vendor.neon
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ parameters:
- vendor/paragonie/constant_time_encoding/tests/* # phpunit/phpunit not installed, PHPUnit\Framework\TestCase missing
- vendor/paragonie/hidden-string/test/* # phpunit/phpunit not installed, PHPUnit\Framework\TestCase missing
- vendor/nette/forms/src/compatibility.php # throws `Class 'Nette\Forms\SubmitterControl' not found` for some reason
- vendor/shipmonk/composer-dependency-analyser/src/Analyser.php # throws No error to ignore is reported on line
- vendor/shipmonk/composer-dependency-analyser/src/Cli.php # throws No error to ignore is reported on line
- vendor/shipmonk/composer-dependency-analyser/src/ComposerJson.php # throws No error to ignore is reported on line
- vendor/spaze/sri-macros/src/Bridges/Latte/Nodes/SriNode.php # throws No error to ignore is reported on line
- vendor/spaze/svg-icons-latte/src/Nodes/IconNode.php # throws No error to ignore is reported on line
- vendor/symfony/translation-contracts/Test/* # Symfony packages not installed
Expand Down
6 changes: 3 additions & 3 deletions site/psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
maxStringLength="2600"
>
<projectFiles>
<directory name="app" />
<directory name="public" />
<directory name="tests" />
<directory name="." />
<ignoreFiles>
<directory name="bin" />
<directory name="temp" />
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
Expand Down
Loading

0 comments on commit de471f2

Please sign in to comment.