PHP Architecture Tester - Easy to use architectural testing tool for PHP :heavy_check_mark:

Overview

PHP Architecture Tester

Easy to use architecture testing tool for PHP

Version PHP Version Contributions welcome


Introduction ๐Ÿ“œ

PHP Architecture Tester is a static analysis tool to verify architectural requirements.

It provides a natural language abstraction to define your own architectural rules and test them against your software. You can also integrate phpat easily into your toolchain.

There are four groups of supported assertions: Dependency, Inheritance, Composition and Mixin.

โ„น๏ธ Check out the section WHAT TO TEST to see some examples of typical use cases.

Installation ๐Ÿ’ฝ

Just require phpat with Composer:

composer require --dev phpat/phpat
Manual download

If you have dependency conflicts, you can also download the latest PHAR file from Releases.

You will have to use it executing php phpat.phar phpat.yaml and declare your tests in XML or YAML.

Configuration ๐Ÿ”ง

You might want to setup a basic configuration:

# phpat.yaml
src:
  path: src/
tests:
  path: tests/architecture/
Complete list of options
Name Description Default
src path The root path of your application no default
src include Files you want to be tested excluding the rest all files
src exclude Files you want to be excluded in the tests no files
composer $ALIAS json Path of your composer.json file (multiple) no files
composer $ALIAS lock Path of your composer.lock file (multiple) no files
tests path The path where your tests are no default
options verbosity Output verbosity level (0/1/2) 1
options dry-run Report failed suite without error exit code (T/F) false
options ignore_docblocks Ignore relations on docblocks (T/F) false
options ignore_php_extensions Ignore relations to core and extensions classes (T/F) true

Test definition ๐Ÿ““

There are different Selectors to choose which classes will intervene in a rule and a wide range of Assertions.

This could be a test with a couple of rules:

<?php

use PhpAT\Rule\Rule;
use PhpAT\Selector\Selector;
use PhpAT\Test\ArchitectureTest;
use App\Domain\BlackMagicInterface;

class ExampleTest extends ArchitectureTest
{
    public function testDomainDoesNotDependOnOtherLayers(): Rule
    {
        return $this->newRule
            ->classesThat(Selector::haveClassName('App\Domain\*'))
            ->excludingClassesThat(Selector::implementInterface(BlackMagicInterface::class))
            ->canOnlyDependOn()
            ->classesThat(Selector::havePath('Domain/*'))
            ->andClassesThat(Selector::haveClassName('App\Application\Shared\Service\KnownBadApproach'))
            ->build();
    }
    
    public function testAllHandlersExtendAbstractCommandHandler(): Rule
    {
        return $this->newRule
            ->classesThat(Selector::havePath('Application/*/UseCase/*Handler.php'))
            ->excludingClassesThat(Selector::extendClass('App\Application\Shared\UseCase\DifferentHandler'))
            ->andExcludingClassesThat(Selector::includeTrait('App\Legacy\LegacyTrait'))
            ->andExcludingClassesThat(Selector::haveClassName(\App\Application\Shared\UseCase\AbstractCommandHandler::class))
            ->mustExtend()
            ->classesThat(Selector::haveClassName('App\Application\Shared\UseCase\AbstractCommandHandler'))
            ->build();
    }
}
YAML / XML test definition

You can also define tests whether in YAML or XML.

rules:
  testAssertionsImplementAssertionInterface:
    - classes:
        - havePath: Rule/Assertion/*
    - excluding:
        - haveClassName: PhpAT\Rule\Assertion\*\MustNot*
        - havePath: Rule/Assertion/MatchResult.php
    - assert: mustExtend
    - classes:
        - haveClassName: PhpAT\Rule\Assertion\AbstractAssertion
<?xml version="1.0" encoding="UTF-8" ?>
<test xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="https://raw.githubusercontent.com/carlosas/phpat/master/src/Test/Test.xsd">
    <rule name="testAssertionsDoNotDependOnVendors">
        <classes>
            <selector type="havePath">Rule/Assertion/*</selector>
        </classes>
        <assert>canOnlyDependOn</assert>
        <classes>
            <selector type="haveClassName">PhpAT\*</selector>
            <selector type="haveClassName">Psr\*</selector>
        </classes>
    </rule>
</test>

Usage ๐Ÿš€

Run the bin with your configuration file:

vendor/bin/phpat phpat.yaml

โš  Launching early stage releases (0.x.x) could break the API according to Semantic Versioning 2.0. We are using minor for breaking changes. This will change with the release of the stable 1.0.0 version.

PHP Architecture Tester is in a very early stage, contributions are welcome. Please have a look to the Contribution docs.

Comments
  • [Vendor class] could not be found in the located source

    [Vendor class] could not be found in the located source

    Hi!

    I'm trying out v0.8.x-dev, but I'm unable to run any test. Do I need to include my vendor folder or autoload file somewhere as well?

    When I run: vendor/bin/phpat phpat.yaml

    I get:

    In IdentifierNotFound.php line 30:
                                                                                                                                               
      PHPStan\BetterReflection\Reflection\ReflectionClass "Symfony\Component\HttpFoundation\Request" could not be found in the located source  
    

    phpat.yaml

    src:
      path: src/
    composer:
      app:
        json: composer.json
        lock: composer.lock
    tests:
      path: tests/Architecture/
    

    composer.json

    {
        "require": {
            "php": ">=8.0",
        },
        "autoload": {
            "files": [
                "src/Domain/Shared/Translation/functions.php"
            ],
            "psr-4": {
                "Application\\": "src/Application",
                "Domain\\": "src/Domain",
                "Infrastructure\\": "src/Infrastructure",
                "UserInterface\\": "src/UserInterface"
            }
        },
        "autoload-dev": {
            "psr-4": {
                "Tests\\": "tests/"
            }
        }
    }
    

    DomainTest.php

    <?php
    
    namespace Tests\Architecture;
    
    use PhpAT\Rule\Rule;
    use PhpAT\Selector\Selector;
    use PhpAT\Test\ArchitectureTest;
    
    class DomainTest extends ArchitectureTest
    {
        public function testDomainDoesNotDependOnOtherLayers(): Rule
        {
            return $this->newRule
                ->classesThat(Selector::haveClassName('Domain\*'))
                ->canOnlyDependOn()
                ->classesThat(Selector::havePath('Domain/*'))
                ->build();
        }
    }
    
    opened by darthf1 12
  • `self` and `static` are not resolved as their instance types

    `self` and `static` are not resolved as their instance types

    Inspecting code like following:

    namespace Shared\Domain;
    
    interface EventSourcedAggregateRoot extends AggregateRoot
    {
        /**
         * @param iterable<DomainEvent> $history
         *
         * @return static
         *
         * @psalm-pure
         */
        public static function reconstituteFromHistory(iterable $history) : self;
        // ... snip ...
    }
    

    Current tooling detects self and static as class names, while they should probably be resolved as self in the context of dependency tracking:

    X Shared\Domain\EventSourcedAggregateRoot depends on Shared\Domain\static
    X Shared\Domain\EventSourcedAggregateRoot depends on Shared\Domain\self
    
    bug :computer: in progress 
    opened by Ocramius 7
  • Test error output shown too early

    Test error output shown too early

    Bug Description

    The test output seems to get shown before the actual test was run.

    We would expect the output with the green/red marker to get shown right below the test where the errors originate from (at the bottom green/red marker).

    image

    Additional context

    We run phpat using Docker, with the basic config yaml from the README

    $ docker run --rm -v ${PWD}:/project -w /project jakzal/phpqa phpat

    bug :grey_question: question 
    opened by rvanlaak 7
  • PHAR file generator

    PHAR file generator

    Enhancement description It would be great to provide a phar version of the tool to avoid dependency collisions with other projects.

    Suggested approach or solution Investigate and create a script to generate updated versions every time a new release is published.

    enhancement high priority 
    opened by carlosas 7
  • Support PHP 8 and its new features

    Support PHP 8 and its new features

    This list is based on https://github.com/phpactor/phpactor/issues/1135

    Package Support

    Ensuring that all packages support 8.0 and phpat can be composer-installed in a PHP 8.0 environment โœ…

    Language Support

    Constructor promotion โœ…

    class CustomerDTO
    {
        public function __construct(
            public Email $email, 
            public DateTimeImmutable $birth_date,
        ) {}
    }
    

    Named Parameters โœ…

    $data = new CustomerData(
        email: new Email('[email protected]')
    );
    

    Union Types โœ…

    Currently supported in docblocks

    class Example {
        private int|float $foo;
        public function squareAndAdd(float|int $bar): int|float {
            return $bar ** 2 + $foo;
        }
    }
    
    $class is instanceof Foo|Bar
    

    Attributes โœ… :warning:

    TODO:

    • Attribute arguments might use types
    • Currently only class and method attributes are catched

    Match and Throw Expression โœ…

    match ($class) {
        $class instanceof Foo => 'one',
        $class instanceof Bar => 'bar',
        default => throw new Exception('error');
    }
    
    enhancement high priority 
    opened by carlosas 6
  • Add MustOnlyDependOnComposerDependencies

    Add MustOnlyDependOnComposerDependencies

    Enhancement description In a Mono-Repo setup it is common that each module has it's own composer.json file that defines the dependencies. However it is easy to depend on a dependency from another module. Also dependencies between modules in the same mono-repo should also be defined inside each modules composer.json.

    It would be great if we don't have to replicate the whole information from the composer.json, but rather use the composer.json as a source for our architecture testing.

    **Suggested approach or solution ** Didn't look at the source yet, however phparch has a similar feature: https://github.com/j6s/phparch#shorthand-for-monorepos-addcomposerbasedcomponent

    The phparch solution misses one important thing, as it expects a composer.lock file inside each module, in a mono-repo setup especially for development purposes you mostly just have one lock file in the mono-repo root containing the dependencies of all modules. https://github.com/symfony/symfony being a good example of such setup

    enhancement :lock: on hold 
    opened by keulinho 6
  • Assertions on a class structure without relations

    Assertions on a class structure without relations

    Enhancement description At the moment, every assertion requires specifying another selector for a relation. There are no assertions which can assert a certain characteristic of the selected classes. For example, one could want to assert that every class matching a selector is final.

    Example usage:

     <?php
    
    use PhpAT\Rule\Rule;
    use PhpAT\Selector\Selector;
    use PhpAT\Test\ArchitectureTest;
    
    class ExampleTest extends ArchitectureTest
    {
        public function testEntitesAreFinal(): Rule
        {
            return $this->newRule
                ->classesThat(Selector::haveClassName('App\Entity\*'))
                ->mustBeFinal()
                ->build();
        }
    }
    

    Would such assertions be accepted as pull requests? Do you think phpat is the best tool for that (vs rector, php-cs-fixer and tests)?

    Suggested approach or solution

    Creating a new collection of assertions which look only at the AST of the matched classes and not depend on its relations. Perhaps for these assertions a simple AST processing would be needed.

    enhancement :lock: on hold :+1: 
    opened by hkdobrev 5
  • Add composer selectors

    Add composer selectors

    This adds 2 more selectors:

    • One for source files declared in composer.json
    • One for dependencies declared in composer.json

    The functionality is pretty much a 1:1 copy from phparch with some additional tweaks. Example usage can be found in tests/architecture/ComposerTest

    I am not happy with the naming since areComposerSource does not really communicate the intent of the selector very well.

    opened by j6s 5
  • Inherited relations

    Inherited relations

    Enhancement description It would be great to have the option of checking if a class has some dependency/inheritance/mixin/composition not only by itself but coming from a parent.

    Suggested approach or solution The feature should be enabled by default, but with the ability to disable it with ignore-indirect-behaviour. This check should be performed by the parser.

    enhancement :computer: in progress 
    opened by carlosas 5
  • Selector::all() vs global scope

    Selector::all() vs global scope

    i have a legacy Symfony 1 Application and want to enforce some rules within the legacy code. starting with SfContext (which i replaced and aliased with a new implementation), so i configured my phpstan like this:

      paths:
        - lib
        - src
        - tests
    
      bootstrapFiles:
        - .phpstan/aliases.php
    
    # aliases.php
    <?php
    
        class_alias(\SymfonyOne\SfContext::class, \sfContext::class );
    

    with a simple test enforcing this:

    <?php namespace Tests\Architecture;
    
        use SymfonyOne\SfContext;
        use PHPat\Selector\Selector;
        use PHPat\Test\PHPat;
        use PHPat\Test\Builder\Rule;
    
        class ExampleTest {
            public function test_no_dependency_on_sfContext(): Rule
            {
                return PHPat::rule()
                    ->classes(Selector::all())
                    ->shouldNotDependOn()
                    ->classes(
                        Selector::classname(SfContext::class),
                        Selector::classname(\sfContext::class),
                    );
            }
        }
    

    which works fine for the test itself, i get errors which i would expect:

     ------ --------------------------------------------------------------------------------------------------------
      Line   tests/Architecture/ExampleTest.php
     ------ --------------------------------------------------------------------------------------------------------
      15     \Tests\Architecture\ExampleTest should not depend on \SymfonyOne\SfContext
      16     \Tests\Architecture\ExampleTest should not depend on sfContext
     ------ --------------------------------------------------------------------------------------------------------
    

    but i do not get any errors from classes without namespace living in the lib folder which is scanned by phpstan :thinking:

    is that a limitation of phpat?

    bug 
    opened by verfriemelt-dot-org 4
  • Fix for issue 182 - broken phar file

    Fix for issue 182 - broken phar file

    This pull requests fixes 3 issues:

    • The path to the binary was incorrect, so the file in phar project was empty
    • As the phpat binary is put at the top level the path to vendor/autload.php was incorrect
    • After fixing the above I was still getting autoload errors about missing PHPUnit files failed to open stream: phar error: "vendor/phpunit/phpunit/src/Framework/Assert/Functions.php" is not a file in phar "/home/lefty/Workspace/phpat/dist/phpat.phar" in phar:///.../phpat/dist/phpat.phar/vendor/composer/autoload_real.php on line 55 As those files are not necessary for the library to work and increase the size of the phar archive I instructed workflow not to install dev dependencies
    opened by dabrowiecki 4
  • Attributes are not detected

    Attributes are not detected

    Hi!

    I have a class in the Domain namespace, which has Doctrine's attributes for ORM mapping. My classes have the following imports:

    <?php
    
    declare(strict_types=1);
    
    namespace Domain\MyDomain;
    
    use Doctrine\Common\Collections\ArrayCollection;
    use Doctrine\Common\Collections\Collection;
    use Doctrine\DBAL\Types\Types;
    use Doctrine\ORM\Mapping as ORM;
    

    With the test below:

    public function testDomainLayerOnlyDependsOnDomainLayer(): Rule
    {
        return PHPat::rule()
            ->classes(
                Selector::namespace('/^Domain.*/', true),
            )
            ->shouldNotDependOn()
            ->classes(
                Selector::NOT(Selector::namespace('/^Domain.*/', true)),
            )
        ;
    }
    

    I get:

     ------ ------------------------------------------------------------------------------------------------- 
      Line   src/Domain/MyDomain\MyClass.php                                                           
     ------ ------------------------------------------------------------------------------------------------- 
      43     Domain\MyDomain\MyClass should not depend on Doctrine\DBAL\Types\Types                    
      48     Domain\MyDomain\MyClass should not depend on Doctrine\DBAL\Types\Types                    
      51     Domain\MyDomain\MyClass should not depend on Doctrine\DBAL\Types\Types                    
      54     Domain\MyDomain\MyClass should not depend on Doctrine\DBAL\Types\Types                    
      57     Domain\MyDomain\MyClass should not depend on Doctrine\DBAL\Types\Types                    
      60     Domain\MyDomain\MyClass should not depend on Doctrine\DBAL\Types\Types                    
      63     Domain\MyDomain\MyClass should not depend on Doctrine\DBAL\Types\Types                    
      69     Domain\MyDomain\MyClass should not depend on Doctrine\Common\Collections\Collection       
      75     Domain\MyDomain\MyClass should not depend on Doctrine\Common\Collections\Collection       
      95     Domain\MyDomain\MyClass should not depend on Doctrine\Common\Collections\ArrayCollection  
      102    Domain\MyDomain\MyClass should not depend on Doctrine\Common\Collections\ArrayCollection  
     ------ ------------------------------------------------------------------------------------------------- 
    

    There is no mention of the import:

    use Doctrine\ORM\Mapping as ORM;
    

    Using a direct import also is not reported:

    use Doctrine\ORM\Mapping\Column;
    
    bug 
    opened by darthf1 1
  • How to stack NOT selectors?

    How to stack NOT selectors?

    Hi,

    I'm trying to define the rule:

    • Classes from the Application namespace, can only depend on classes in the Application namespace, and the Domain namespace.

    In PHPat's language:

    • Classes with namespace Application, should not depend on classes that NOT have namespace Application AND should not depend on classes that NOT have namespace Domain.

    With the test below:

        public function testApplicationLayerOnlyDependsOnDomainAndApplicationLayer(): Rule
        {
            return PHPat::rule()
                ->classes(
                    Selector::namespace('/^Application.*/', true),
                )
                ->shouldNotDependOn()
                ->classes(
                    Selector::NOT(Selector::namespace('/^Application.*/', true)),
                    Selector::NOT(Selector::namespace('/^Domain.*/', true)),
                )
            ;
        }
    

    Both classes from Domain and Application namespace are reported.

    Below works, but feels counterintuitive?

        public function testApplicationLayerOnlyDependsOnDomainAndApplicationLayer(): Rule
        {
            return PHPat::rule()
                ->classes(
                    Selector::namespace('/^Application.*/', true),
                )
                ->shouldNotDependOn()
                ->classes(Selector::all())
                ->excluding(
                    Selector::namespace('/^Application.*/', true),
                    Selector::namespace('/^Domain.*/', true),
                )
            ;
        }
    
    enhancement 
    opened by darthf1 1
  • How to select classes from the global namespace?

    How to select classes from the global namespace?

    Hi,

    I'm trying to define the rule:

    • Classes in the Domain namespace, can only depend on classes in the Domain namespace, and classes from php's global namespace.

    In PHPat's language:

    • Classes with namespace Domain, should not depend on classes that NOT have namespace Domain, excluding classes from the global php namespace.

    How can I define the "global namespace" part? I tried an empty string, or a regex where the namespace stars with a backslash, but without result.

    public function testDomainLayerOnlyDependsOnDomainLayer(): Rule
        {
            return PHPat::rule()
                ->classes(
                    Selector::namespace('/^Domain.*/', true),
                )
                ->shouldNotDependOn()
                ->classes(
                    Selector::NOT(Selector::namespace('/^Domain.*/', true)),
                )
                ->excluding(
                    Selector::classname('/^\\*./', true), <-- how to do this?
                )
            ;
        }
    ``
    opened by darthf1 0
  • [0.10] canOnlyDependOn

    [0.10] canOnlyDependOn

    Hi!

    Great job on 0.10!

    Most of my tests included the canOnlyDependOn assertion, which I guess is removed in 0.10. Was this done on purpose and do you maybe have an alternative way of writing these rules? :)

    Edit:

    Ah. I guess I can nest it like this?

    return PHPat::rule()
        ->classes(Selector::haveClassName('...'))
        ->shouldNotDependOn()
        ->classes(
            Selector::NOT(
                Selector::classname('...')
            )
        )
    
    opened by darthf1 2
  • Add combination operators to selectors

    Add combination operators to selectors

    Enhancement description While defining my rules, I need to define some fine grained rules such as:

    • If class implements interface X, I want it to use trait A or B or C
    • if class uses trait A, I want its class to implement interface A and one of ( B or C )

    Currently, all rules are combining as an and operator. Those operators may be useful:

    • all of (and operator)
    • any of (or operator)
    • none of (not operator)
    • one of (xor operator)
    • at least X of
    • at most X of

    Suggested approach or solution By creating new selectors, I can now define more precise rules

    $this->newRule
        ->classesThat(Selector::implementInterface('EventSauce\EventSourcing\AggregateRoot'))
        ->mustInclude()
        ->classesThat(
            Selector::oneOf(
                Selector::haveClassName('EventSauce\EventSourcing\AggregateRootWithAggregates'),
                Selector::haveClassName('EventSauce\EventSourcing\AggregateRootBehaviour'),
                Selector::haveClassName('EventSauce\EventSourcing\AggregateAlwaysAppliesEvents'),
            )
        )
        ->build();
    
    enhancement 
    opened by gplanchat 0
Releases(0.10.2)
Owner
Carlos A Sastre
Software Engineer
Carlos A Sastre
Keep your architecture clean.

Deptrac What is Deptrac Deptrac is a static code analysis tool that helps to enforce rules for dependencies between software layers in your PHP projec

QOSSMIC GmbH 2.2k Jan 5, 2023
All In 1 Spam Tool For Termux Users Subscribe Us (Noob Hackers) some shit heads are trying to abuse this script so don't worry about them ...let them hallucinate ...but you are free to use this script

ABOUT TOOL : SPAMX is a all in one Bombing+Spam tool from this tool you can send anonymous messages to your target without showing your real number an

N17R0 449 Jan 7, 2023
A project to add Psalm support for Drupal for security testing, focused only on taint analysis.

psalm-plugin-drupal A Drupal integration for Psalm focused on security scanning (SAST) taint analysis. Features Stubs for sinks, sources, and sanitize

Samuel Mortenson 38 Aug 29, 2022
A fake mailer for Laravel Applications for testing mail.

MailThief MailThief is a fake mailer for Laravel applications (5.0+) that makes it easy to test mail without actually sending any emails. Note: Due to

Tighten 688 Nov 29, 2022
PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD. PHPMD can be seen as an user friendly frontend application for the raw metrics stream measured by PHP Depend.

PHPMD PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD. PHPMD can be seen as an user friendly

PHP Mess Detector 2.1k Jan 8, 2023
A tool to automatically fix PHP Coding Standards issues

PHP Coding Standards Fixer The PHP Coding Standards Fixer (PHP CS Fixer) tool fixes your code to follow standards; whether you want to follow PHP codi

null 11.6k Jan 3, 2023
PHP Static Analysis Tool - discover bugs in your code without running it!

PHPStan - PHP Static Analysis Tool PHPStan focuses on finding errors in your code without actually running it. It catches whole classes of bugs even b

PHPStan 11.6k Dec 30, 2022
A PHP code-quality tool

GrumPHP Sick and tired of defending code quality over and over again? GrumPHP will do it for you! This composer plugin will register some git hooks in

PHPro 3.9k Jan 1, 2023
Beautiful and understandable static analysis tool for PHP

PhpMetrics PhpMetrics provides metrics about PHP project and classes, with beautiful and readable HTML report. Documentation | Twitter | Contributing

PhpMetrics 2.3k Dec 22, 2022
A tool for quickly measuring the size of a PHP project.

PHPLOC phploc is a tool for quickly measuring the size and analyzing the structure of a PHP project. Installation This tool is distributed as a PHP Ar

Sebastian Bergmann 2.3k Jan 4, 2023
A command line refactoring tool for PHP

PHP Refactoring Browser Note: This software is under development and in alpha state. Refactorings do not contain all necessary pre-conditions and migh

QafooLabs 562 Dec 30, 2022
A static php code analysis tool using the Graph Theory

Mondrian Ok guyz, you have a master degree in Graph Theory, you follow Law of Demeter and you live on S.O.L.I.D principles ? Let's have some Fun ! (^ฯ‰

Florent Genette 391 Nov 30, 2022
PHPCheckstyle is an open-source tool that helps PHP programmers adhere to certain coding conventions.

PHPCheckstyle Overview PHPCheckstyle is an open-source tool that helps PHP programmers adhere to certain coding conventions. The tools checks the inpu

PHPCheckstyle 157 Dec 5, 2022
PHP completion, refactoring, introspection tool and language server.

Phpactor This project aims to provide heavy-lifting refactoring and introspection tools which can be used standalone or as the backend for a text edit

Phpactor 882 Jan 1, 2023
A static analysis tool for finding errors in PHP applications

Psalm Psalm is a static analysis tool for finding errors in PHP applications. Installation To get started, check out the installation guide. Live Demo

Vimeo 5k Jan 2, 2023
A web tool to explore the ASTs generated by PHP-Parser.

phpast.com A web tool to explore the ASTs generated by PHP-Parser. About This web tool provides a GUI for exploring the AST of your PHP code. You can

Ryan Chandler 23 Nov 29, 2022
A tool to automatically fix PHP Coding Standards issues by Dragon Code.

A tool to automatically fix PHP Coding Standards issues by Dragon Code.

The Dragon Code 24 Aug 27, 2022
Tool helping us to analyze software projects

Qafoo Quality Analyzer This software is a tool to visualize metrics and source code. We use this software for Code Reviews together with our customers

Qafoo GmbH 494 Dec 29, 2022
Baseline tool for PHP_CodeSniffer

PHP_CodeSniffer Baseliner This tool enables you to integrate PHP_CodeSniffer into an existing project by automatically adding phpcs:ignore and phpcs:d

ISAAC 13 Nov 26, 2022