Skip to content

Commit

Permalink
Implement coding standards and static analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
benr77 committed May 23, 2024
1 parent bd58a6c commit 35e63e6
Show file tree
Hide file tree
Showing 13 changed files with 168 additions and 42 deletions.
38 changes: 35 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

name: Headsnet Doctrine Tools Test
name: CI Pipeline

on:
push: ~
Expand All @@ -11,7 +11,7 @@ jobs:
name: PHP ${{ matrix.php }} and Symfony ${{ matrix.symfony }}
strategy:
matrix:
operating-system: [ 'ubuntu-22.04', 'windows-2022' ]
operating-system: [ 'ubuntu-22.04' ]
php: [ '8.1', '8.2', '8.3' ]
symfony: ['6.4.*', '7.0.*']
exclude:
Expand All @@ -26,11 +26,43 @@ jobs:
with:
php-version: ${{ matrix.php }}
tools: flex
coverage: pcov

- name: Download dependencies
env:
SYMFONY_REQUIRE: ${{ matrix.symfony }}
uses: ramsey/composer-install@v3

- name: Run test suite on PHP ${{ matrix.php }} and Symfony ${{ matrix.symfony }}
run: ./vendor/bin/phpunit
run: ./vendor/bin/phpunit --coverage-clover clover.xml

- name: Make code coverage badge
uses: timkrase/[email protected]
with:
coverage_badge_path: output/coverage.svg
push_badge: false

- name: Git push to image-data branch
uses: peaceiris/actions-gh-pages@v4
with:
publish_dir: ./output
publish_branch: image-data
github_token: ${{ secrets.GITHUB_TOKEN }}
user_name: 'github-actions[bot]'
user_email: 'github-actions[bot]@users.noreply.github.com'

ecs:
name: Easy Coding Standard
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ramsey/composer-install@v3
- run: vendor/bin/ecs

phpstan:
name: PHPStan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ramsey/composer-install@v3
- run: vendor/bin/phpstan
27 changes: 27 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.PHONY: help
.DEFAULT_GOAL := help

PHP = php
PHPUNIT = ${PHP} vendor/bin/phpunit

cs: ## Run ECS Coding Standards analysis
@${PHP} vendor/bin/ecs check

cs-fix: ## Fix ECS Coding Standards
@${PHP} vendor/bin/ecs check --fix

static: ## Run PHPStan static analysis
@${PHP} vendor/bin/phpstan analyse

test: ## Run PHPUnit tests
@${PHPUNIT}

test-coverage: ## Run PHPUnit tests with code coverage
@${PHPUNIT} --coverage-html=var/coverage

########################################################################################################################
# https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
help:
@echo "\nMakefile is used to run various utilities related to this project\n"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
########################################################################################################################
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ Doctrine Tools
====

![Build Status](https://github.com/headsnet/doctrine-tools-bundle/actions/workflows/ci.yml/badge.svg)
![Coverage](https://raw.githubusercontent.com/headsnet/doctrine-tools-bundle/image-data/coverage.svg)
[![Latest Stable Version](https://poser.pugx.org/headsnet/doctrine-tools-bundle/v)](//packagist.org/packages/headsnet/doctrine-tools-bundle)
[![Total Downloads](https://poser.pugx.org/headsnet/doctrine-tools-bundle/downloads)](//packagist.org/packages/headsnet/doctrine-tools-bundle)
[![License](https://poser.pugx.org/headsnet/doctrine-tools-bundle/license)](//packagist.org/packages/headsnet/doctrine-tools-bundle)
Expand Down
9 changes: 8 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
},
"require-dev": {
"phpunit/phpunit": "^10.0",
"nyholm/symfony-bundle-test": "^3.0"
"nyholm/symfony-bundle-test": "^3.0",
"phpstan/phpstan": "^1.11",
"symplify/easy-coding-standard": "^12.2"
},
"autoload": {
"psr-4": {
Expand All @@ -30,5 +32,10 @@
"psr-4": {
"Headsnet\\DoctrineToolsBundle\\Tests\\": "tests/"
}
},
"config": {
"allow-plugins": {
"phpstan/extension-installer": true
}
}
}
33 changes: 33 additions & 0 deletions ecs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);

use PhpCsFixer\Fixer\Import\NoUnusedImportsFixer;
use PhpCsFixer\Fixer\Operator\NotOperatorWithSuccessorSpaceFixer;
use PhpCsFixer\Fixer\PhpTag\BlankLineAfterOpeningTagFixer;
use Symplify\EasyCodingStandard\Config\ECSConfig;
use Symplify\EasyCodingStandard\ValueObject\Set\SetList;

return function (ECSConfig $ecsConfig): void {
$ecsConfig->paths([
__DIR__ . '/src',
__DIR__ . '/tests',
]);

$ecsConfig->skip([
BlankLineAfterOpeningTagFixer::class,
NotOperatorWithSuccessorSpaceFixer::class,
]);

$ecsConfig->rules([
NoUnusedImportsFixer::class,
]);

$ecsConfig->sets([
SetList::SPACES,
SetList::ARRAY,
SetList::DOCBLOCK,
SetList::NAMESPACES,
SetList::COMMENTS,
SetList::PSR_12,
]);
};
10 changes: 10 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
parameters:

level: 9

paths:
- src/
- tests/

ignoreErrors:
- '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::children\(\).#'
10 changes: 6 additions & 4 deletions src/CustomTypes/CustomTypeNamer.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

namespace Headsnet\DoctrineToolsBundle\CustomTypes;

use Doctrine\DBAL\Types\Type;
use ReflectionClass;

/**
Expand All @@ -12,20 +13,21 @@
*/
final class CustomTypeNamer
{
/**
* @param ReflectionClass<Type> $reflection
*/
public static function getTypeName(ReflectionClass $reflection): string
{
$attribute = $reflection->getAttributes(CustomType::class)[0];


$attributeArgs = $attribute->getArguments();

if (isset($attributeArgs['name']))
{
if (isset($attributeArgs['name'])) {
return $attributeArgs['name'];
}

$typeName = str_replace('Type', '', $reflection->getShortName());
$typeName = preg_replace("/(?<=.)([A-Z])/", "_$1", $typeName);
$typeName = (string) preg_replace("/(?<=.)([A-Z])/", "_$1", $typeName);

return strtolower($typeName);
}
Expand Down
31 changes: 14 additions & 17 deletions src/CustomTypes/RegisterDoctrineTypesCompilerPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,66 +21,63 @@ final class RegisterDoctrineTypesCompilerPass implements CompilerPassInterface

public function process(ContainerBuilder $container): void
{
if (!$container->hasParameter(self::TYPE_DEFINITION_PARAMETER))
{
if (!$container->hasParameter(self::TYPE_DEFINITION_PARAMETER)) {
return;
}

/** @var array<string, array{class: class-string}> $typeDefinitions */
$typeDefinitions = $container->getParameter(self::TYPE_DEFINITION_PARAMETER);
/** @var array<string> $scanDirs */
$scanDirs = $container->getParameter('headsnet_doctrine_tools.custom_types.scan_dirs');

$types = $this->findTypesInApplication($scanDirs);

foreach ($types as $type)
{
foreach ($types as $type) {
$name = $type['name'];
$class = $type['class'];

// Do not add the type if it's been manually defined already
if (array_key_exists($name, $typeDefinitions))
{
if (array_key_exists($name, $typeDefinitions)) {
continue;
}

$typeDefinitions[$name] = ['class' => $class];
$typeDefinitions[$name] = [
'class' => $class,
];
}

$container->setParameter(self::TYPE_DEFINITION_PARAMETER, $typeDefinitions);
}

/**
* @param array<string> $scanDirs
*
* @return Generator<int, array{class: class-string, name: string}>
*/
private function findTypesInApplication($scanDirs): iterable
private function findTypesInApplication(array $scanDirs): iterable
{
$classNames = ConstructFinder::locatedIn(...$scanDirs)->findClassNames();

foreach ($classNames as $className)
{
foreach ($classNames as $className) {
$reflection = new ReflectionClass($className);

// If the class is not a Doctrine Type
if (!$reflection->isSubclassOf(Type::class))
{
if (!$reflection->isSubclassOf(Type::class)) {
continue;
}

// Skip any abstract parent types
if ($reflection->isAbstract())
{
if ($reflection->isAbstract()) {
continue;
}

// Only register types that have the #[CustomType] attribute
if ($reflection->getAttributes(CustomType::class))
{
if ($reflection->getAttributes(CustomType::class)) {
yield [
'name' => CustomTypeNamer::getTypeName($reflection),
'class' => $className,
];
}
}
}

}
19 changes: 11 additions & 8 deletions src/HeadsnetDoctrineToolsBundle.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

namespace Headsnet\DoctrineToolsBundle;

use Headsnet\DoctrineToolsBundle\CustomTypes\RegisterDoctrineTypesCompilerPass;
Expand All @@ -13,23 +14,25 @@ public function configure(DefinitionConfigurator $definition): void
{
$definition->rootNode()
->children()
->arrayNode('custom_types')
->children()
->arrayNode('scan_dirs')
->scalarPrototype()->end()
->end()
->end()
->end()
->arrayNode('custom_types')
->children()
->arrayNode('scan_dirs')
->scalarPrototype()->end()
->end()
->end()
->end()
->end()
;
}

/**
* @param array{custom_types: array{scan_dirs: array<string>}} $config
*/
public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
{
$container->parameters()
->set('headsnet_doctrine_tools.custom_types.scan_dirs', $config['custom_types']['scan_dirs'])
;

}

public function build(ContainerBuilder $container): void
Expand Down
1 change: 0 additions & 1 deletion tests/CustomTypes/CustomTypeNamerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

namespace Headsnet\DoctrineToolsBundle\Tests\CustomTypes;

use Headsnet\DoctrineToolsBundle\CustomTypes\CustomType;
use Headsnet\DoctrineToolsBundle\CustomTypes\CustomTypeNamer;
use Headsnet\DoctrineToolsBundle\Tests\CustomTypes\Fixtures\DummyCustomType;
use Headsnet\DoctrineToolsBundle\Tests\CustomTypes\Fixtures\DummyCustomTypeWithName;
Expand Down
4 changes: 3 additions & 1 deletion tests/CustomTypes/Fixtures/NotADoctrineType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
namespace Headsnet\DoctrineToolsBundle\Tests\CustomTypes\Fixtures;

use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
use Headsnet\DoctrineToolsBundle\CustomTypes\CustomType;

#[CustomType]
class NotADoctrineType
{
/**
* @param array{length?: int, fixed?: bool} $column
*/
public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
{
return '';
Expand Down
24 changes: 17 additions & 7 deletions tests/CustomTypes/RegisterDoctrineTypesCompilerPassTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,20 @@ public function can_find_and_register_types(): void
$container = new ContainerBuilder();
$container->setParameter('doctrine.dbal.connection_factory.types', []);
$container->setParameter('headsnet_doctrine_tools.custom_types.scan_dirs', [
__DIR__ . '/Fixtures'
__DIR__ . '/Fixtures',
]);
$sut = new RegisterDoctrineTypesCompilerPass();

$sut->process($container);

$result = $container->getParameter('doctrine.dbal.connection_factory.types');
$expected = [
'dummy_custom' => ['class' => DummyCustomType::class],
'my_custom_name' => ['class' => DummyCustomTypeWithName::class]
'dummy_custom' => [
'class' => DummyCustomType::class,
],
'my_custom_name' => [
'class' => DummyCustomTypeWithName::class,
],
];
$this->assertEquals($expected, $result);
}
Expand All @@ -41,19 +45,25 @@ public function ignores_types_that_are_manually_registered(): void
{
$container = new ContainerBuilder();
$container->setParameter('doctrine.dbal.connection_factory.types', [
'dummy_custom' => ['class' => DummyCustomType::class]
'dummy_custom' => [
'class' => DummyCustomType::class,
],
]);
$container->setParameter('headsnet_doctrine_tools.custom_types.scan_dirs', [
__DIR__ . '/Fixtures'
__DIR__ . '/Fixtures',
]);
$sut = new RegisterDoctrineTypesCompilerPass();

$sut->process($container);

$result = $container->getParameter('doctrine.dbal.connection_factory.types');
$expected = [
'dummy_custom' => ['class' => DummyCustomType::class],
'my_custom_name' => ['class' => DummyCustomTypeWithName::class]
'dummy_custom' => [
'class' => DummyCustomType::class,
],
'my_custom_name' => [
'class' => DummyCustomTypeWithName::class,
],
];
$this->assertEquals($expected, $result);
}
Expand Down
Loading

0 comments on commit 35e63e6

Please sign in to comment.