PHPArch is a work in progress architectural testing library for PHP projects

Related tags

Testing phparch
Overview

PHPArch Build Status

What is this?

PHPArch is a work in progress architectural testing library for PHP projects. It is inspired by archlint (C#) and archunit (java).

It can be used to help enforce architectural boundaries in an application in order to prevent the architecture from rotting over time by introducing dependencies across previously well defined architectural boundaries.

Installation

You can install PHPArch using composer. If you don't know what composer is then you probably don't need a library for architectural testing.

$ composer require j6s/phparch

Simple Namespace validation

The most simple type of check PHPArch can help you with are simple namespace based checks: Setup rules for which namespace is allowed or forbidden to depend on which other namespace.

public function testSimpleNamespaces()
{
    (new PhpArch())
        ->fromDirectory(__DIR__ . '/../../app')
        ->validate(new ForbiddenDependency('Lib\\', 'App\\'))
        ->validate(new MustBeSelfContained('App\\Utility'))
        ->validate(new MustOnlyDependOn('App\\Mailing', 'PHPMailer\\PHPMailer'))
        ->assertHasNoErrors();
}

Available Validators

Currently the following validators are available:

  • ForbiddenDependency Lets you declare that one namespace is not allowed to depend on another namespace.
  • MustBeSelfContained Lets you declare that a namespace must be self-contained meaning that it may not have any external dependencies.
  • MustOnlyDependOn Lets you declare that one namespace must only depend on another namespace.
  • MustOnlyHaveAutoloadableDependencies checks if all dependencies are autoloadable in the current environment. This can be helpful if two packages should not have any dependencies on each other but they still sneak in because the packages are often used together.
  • AllowInterfaces is a wrapper for validators that allows dependencies if they are to interfaces.
  • MustOnlyDependOnComposerDependencies checks if all dependencies in a given namespace are declared in the given composer.json file. This is useful to prevent accidental dependencies if one repository contains multiple packages.
  • ExplicitlyAllowDependency is a wrapper for validators that allows a specific dependency.

Most architectural boundaries can be described with these rules.

Defining an architecture

PHPArch also contains a fluent API that allows you to define a component based architecture which is then validated. The API is based on components which are identified by one or more namespaces instead of Layers or 'Onion Peels' because it is the simplest way to communicate any architecture - no matter what the implementation details of it are.

public function testArchitecture()
{
    $architecture = (new Architecture())
        ->component('Components')->identifiedByNamespace('J6s\\PhpArch\\Component')
        ->mustNotDependOn('Validation')->identifiedByNamespace('J6s\\PhpArch\\Validation');
    
    (new PhpArch())
        ->fromDirectory(__DIR__ . '/../../app')
        ->validate($architecture)
        ->assertHasNoErrors();
}

Most of defining an architecture is only syntactic sugar over the namespace validators above. The following methods allow you to add assertions to your component structure:

  • mustNotDependOn
  • mustNotDependDirectlyOn
  • mustNotBeDependedOnBy
  • mustOnlyDependOn
  • mustNotDependOnAnyOtherComponent
  • mustOnlyDependOnComposerDependencies
  • dissallowInterdependence
  • isAllowedToDependOn

Syntactic sugar: Bulk definition of components

While the speaking Api for defining an architecture is great it can get convoluted and hard to read if you have a lot of components. The components method can be used to define components using a simple associative array where the key is the component name and the value is the namespaces that define the component. This way definitions of components and setting up dependency rules can be split into 2 steps for better readability.

// This
$architecture->components([
     'Foo' => 'Vendor\\Foo',
     'Bar' => [ 'Vendor\\Bar', 'Vendor\\Deep\\Bar' ]
]);

// Is the same as this
$architecture->component('Foo')
    ->identifiedByNamespace('Vendor\\Foo')
    ->component('Bar')
    ->identifierByNamespace('Vendor\\Bar')
    ->identifiedByNamespace('Vendor\\Deep\\Bar')

Syntactic sugar: Chaining multiple dependency rules

If a non-existing component is referenced in one of these methods then it will be created. These methods will also set the referenced component as the currently active one - so when using ->mustNotDependOn('FooBar') all future operations reference the FooBar component.

In order to chain multiple dependency rules for a single component there are some convenience methods available:

  • andMustNotDependOn
  • andMustNotBeDependedOnBy
// This
(new Architecture)
    ->component('Foo')
    ->mustNotDependOn('Bar')
    ->andMustNotDependOn('Baz')

// Is this same as this:
(new Architecture())
    ->component('Foo')->mustNotDependOn('Bar')
    ->component('Foo')->mustNotDependOn('Baz')

Shorthand for monorepos: addComposerBasedComponent

In case one repository contains multiple packages that all have their own composer.json file it is easy to accidentally use a method or class of something that is not in the composer.json file of the current package.

To prevent this the Architecture->mustOnlyDependOnComposerDependencies method and the MustOnlyDependOnComposerDependencies validator can be used to check if all used namespaces are declared in a given composer.json file:

$architecture = (new Architecture)
    ->component('vendor/subpackage')
    ->identifierByNamespace('Vendor\\Subpackage\\')
    ->mustOnlyDependOnComposerDependencies('packages/subpackage/composer.json');

However, composer.json already contains information about the package name and namespaces. Therefore the addComposerBasedComponent method can be used in order to make things easier:

$architecture = (new Architecture)
    ->addComposerBasedComponent('packages/subpackage/composer.json');

Examples

Comments
  • TASK | Optimizing multiple composer file loading by caching ability

    TASK | Optimizing multiple composer file loading by caching ability

    I've added caching to ComposerFileParser in Architecture because in the case of a scenario when we have a modular monolith with separated modules that we want to separate and force them to use only main Composer dependencies, for every module same composer file was loaded again and again.

    Thanks to that changes it will be loaded once and then use cache in a single run.

    opened by gkawka 5
  • Support symfony/finder 6

    Support symfony/finder 6

    Problem 1
    - Root composer.json requires j6s/phparch ^3.1 -> satisfiable by j6s/phparch[3.1.0].
    - j6s/phparch 3.1.0 requires symfony/finder ^4.1|^5.0 -> found symfony/finder[v4.1.0, ..., v4.4.37, v5.0.0, ..., v5.4.3] but the package is fixed to v6.0.3 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.
    
    opened by marc-mabe 3
  • DocBlockTypeAnnotations: null may be passed into ParserException()

    DocBlockTypeAnnotations: null may be passed into ParserException()

    I think the second argument in the ParserException() is causing a failing test in Docker (#23) https://github.com/j6s/phparch/blob/54f163b0b5d87a6c8113de0db91cd2af1713918d/src/Parser/Visitor/DocBlockTypeAnnotations.php#L49-L58

    php_1  | Installing dependencies from lock file (including require-dev)    
    php_1  | Verifying lock file contents can be installed on current platform.
    php_1  | Nothing to install, update or remove
    php_1  | Generating autoload files
    php_1  | 29 packages you are using are looking for funding.
    php_1  | Use the `composer fund` command to find out more! 
    php_1  | PHPUnit 9.5.10 by Sebastian Bergmann and contributors.
    php_1  |
    php_1  | Runtime:       PHP 7.4.24
    php_1  | Configuration: /phparch/phpunit.xml
    php_1  | Warning:       Your XML configuration validates against a deprecated schema.  
    php_1  | Suggestion:    Migrate your XML configuration using "--migrate-configuration"!
    php_1  |
    php_1  | E................................................................ 65 / 91 ( 71%)
    php_1  | ..........................                                        91 / 91 (100%)
    php_1  |
    php_1  | Time: 00:02.573, Memory: 26.00 MB
    php_1  |
    php_1  | There was 1 error:
    php_1  |
    php_1  | 1) J6s\PhpArch\Tests\ArchitectureTest::testArchitectureRot
    php_1  | J6s\PhpArch\Parser\ParserException: Error parsing dockblock 
    php_1  |
    php_1  |  /**
    php_1  |      * Calls the given method for all combinations of 2 elements of the input array
    php_1  |      * (excluding the combination of an element with each self).
    php_1  |      *
    php_1  |      * A call of this method with the input array [ 1, 2, 3 ] will call the given block
    php_1  |      * 6 times with the following arguments: (1, 2) (1, 3) (2, 1) (2, 3) (3, 1) (3,2 ).
    php_1  |      *
    php_1  |      * Scalability warning: From the description of this method it should be obvious that
    php_1  |      * this method scales roughly as O(n^2) - as such it quickly becomes very expensive
    php_1  |      * for large arrays: An element with 50 elements will result in roughly 2500 callback
    php_1  |      * calls.
    php_1  |      *
    php_1  |      * @template T
    php_1  |      * @param T[] $elements
    php_1  |      * @param (callable(T, T): void) $block
    php_1  |      */
    php_1  |
    php_1  |  array_map(): Expected parameter 2 to be an array, null given
    php_1  |
    php_1  | /phparch/src/Parser/Visitor/DocBlockTypeAnnotations.php:53
    php_1  | /phparch/src/Parser/Visitor/DocBlockTypeAnnotations.php:38
    php_1  | /phparch/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:200
    php_1  | /phparch/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:114
    php_1  | /phparch/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:223
    php_1  | /phparch/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:114
    php_1  | /phparch/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:223
    php_1  | /phparch/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:91
    php_1  | /phparch/src/Parser/Parser.php:62
    php_1  | /phparch/src/Parser/Parser.php:39
    php_1  | /phparch/src/PhpArch.php:68
    php_1  | /phparch/src/PhpArch.php:33
    php_1  | /phparch/tests/ArchitectureTest.php:44
    php_1  | /root/.composer/vendor/phpunit/phpunit/src/TextUI/Command.php:143
    php_1  | /root/.composer/vendor/phpunit/phpunit/src/TextUI/Command.php:96
    php_1  |
    php_1  | Caused by
    php_1  | PHPUnit\Framework\Error\Warning: array_map(): Expected parameter 2 to be an array, null given
    php_1  |
    php_1  | /phparch/src/Parser/Visitor/DocBlockTypeAnnotations.php:51
    php_1  | /phparch/src/Parser/Visitor/DocBlockTypeAnnotations.php:38
    php_1  | /phparch/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:200
    php_1  | /phparch/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:114
    php_1  | /phparch/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:223
    php_1  | /phparch/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:114
    php_1  | /phparch/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:223
    php_1  | /phparch/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:91
    php_1  | /phparch/src/Parser/Parser.php:62
    php_1  | /phparch/src/Parser/Parser.php:39
    php_1  | /phparch/src/PhpArch.php:68
    php_1  | /phparch/src/PhpArch.php:33
    php_1  | /phparch/tests/ArchitectureTest.php:44
    php_1  | /root/.composer/vendor/phpunit/phpunit/src/TextUI/Command.php:143
    php_1  | /root/.composer/vendor/phpunit/phpunit/src/TextUI/Command.php:96
    php_1  |
    php_1  | ERRORS!
    php_1  | Tests: 91, Assertions: 124, Errors: 1.
    phparch_php_1 exited with code 2
    
    opened by swharden 3
  • A simple check doesn't capture dependency

    A simple check doesn't capture dependency

    I think this project is super useful and will have many companies. In my case, we have a monolithic application with many different projects inside that 70+ engineers are working together. To figure out how to break it in pieces, I need to understand dependencies and control them a little bit.

    I've tried phparch today, but didn't manage to figure out how to use it.

    Here's my test:

    public function testCoreDoesNotHaveExternalDependencies(): void
        {
            $components = [
                'Core' => 'Core\\Accounting',
                'Desktop Bundle' => 'DesktopBundle'
            ];
    
            $architecture = new Architecture();
            $architecture->components($components);
    
            $architecture
                ->component('Core')
                ->mustNotDependOn('Desktop Bundle');
    
    
            (new PhpArch())
                ->fromDirectory(__DIR__ . '/../../src/Core/Accounting')
                ->validate($architecture)
                ->assertHasNoErrors();
        }
    
    

    Inside of the accounting, I have a class that creates an instance of DesktopBundle\Experiment, but the test passes. Any idea where should I dig? I'll of course contribute back if I'll find the issue.

    needs feedback 
    opened by sergekukharev 3
  • Wrong error message shown in mustOnlyDependOn validators

    Wrong error message shown in mustOnlyDependOn validators

    Currently, when the mustOnlyDependOn validation fails, it shows and error like Component(A) must not depend on Component(B) but A/whatever depends on C/whatever

    while it should show something like Component(A) must only depend on Component(B) but A/whatever depends on C/whatever

    opened by JavierMF 2
  • Which TestCase should I extend

    Which TestCase should I extend

    Following code:

    <?php
    
    namespace Tests\Unit\Architecture;
    
    use J6s\PhpArch\Component\Architecture;
    use J6s\PhpArch\PhpArch;
    use PHPUnit\Framework\TestCase;
    
    class ArchitectureTest extends TestCase
    {
    

    works OK, but as I see there is also J6s\PhpArch\Tests\TestCase; and

    <?php
    
    namespace Tests\Unit\Architecture;
    
    use J6s\PhpArch\Component\Architecture;
    use J6s\PhpArch\PhpArch;
    use J6s\PhpArch\Tests\TestCase;
    
    class ArchitectureTest extends TestCase
    {
    

    gives me

    PHP Fatal error:  Uncaught Error: Class 'J6s\PhpArch\Tests\TestCase' not found in /app/tests/Unit/Architecture/ArchitectureTest.php:11
    Stack trace:
    #0 /app/vendor/bin/.phpunit/phpunit-9.5-0/src/Util/FileLoader.php(65): include_once()
    #1 /app/vendor/bin/.phpunit/phpunit-9.5-0/src/Util/FileLoader.php(49): PHPUnit\Util\FileLoader::load('/app/tests/Unit...')
    #2 /app/vendor/bin/.phpunit/phpunit-9.5-0/src/Framework/TestSuite.php(402): PHPUnit\Util\FileLoader::checkAndLoad('/app/tests/Unit...')
    #3 /app/vendor/bin/.phpunit/phpunit-9.5-0/src/Framework/TestSuite.php(530): PHPUnit\Framework\TestSuite->addTestFile('/app/tests/Unit...')
    #4 /app/vendor/bin/.phpunit/phpunit-9.5-0/src/TextUI/TestSuiteMapper.php(67): PHPUnit\Framework\TestSuite->addTestFiles(Array)
    #5 /app/vendor/bin/.phpunit/phpunit-9.5-0/src/TextUI/Command.php(389): PHPUnit\TextUI\TestSuiteMapper->map(Object(PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection), '')
    #6 /app/vendor/bin/.phpunit/phpunit-9.5-0/src/TextUI/Command.php(110): PHPUnit\TextUI\Command->handleArguments( in /app/vendor/bin/.phpunit/phpunit-9.5-0/src/TextUI/Command.php on line 97
    

    Am I doing something wrong here?

    opened by j4r3kb 1
  • Phive package

    Phive package

    Hi, I currently discovered this project and I would like to integrate it into my project. I am using Phive https://phar.io/ for my tool packages in order to avoid dependency hell between various analyzers, is there any chance you would create phive package for this project? :+1:

    Thank you.

    opened by peldax 1
  • Generics in DocBlocks should be tracked correctly

    Generics in DocBlocks should be tracked correctly

    Currently phparch fails to detect Generic syntax such as Generator<FilterItem>.

    A docblock such as the following:

        /**
         * @param FilterItem $item
         * @return \Generator<Result>
         */
    

    should detect a dependency to \Generator and Result. However at the moment it fails with the following error message:

    J6s\PhpArch\Parser\ParserException: Error parsing dockblock 
    
     /**
         * @param FilterItem $item
         * @return \Generator<Result>
         */ 
    
     "\Generator<FilterItem>" is not a valid Fqsen.
    
    /var/www/html/Packages/Libraries/j6s/phparch/src/Parser/Visitor/DocBlockTypeAnnotations.php:46
    /var/www/html/Packages/Libraries/j6s/phparch/src/Parser/Visitor/DocBlockTypeAnnotations.php:32
    /var/www/html/Packages/Libraries/nikic/php-parser/lib/PhpParser/NodeTraverser.php:200
    /var/www/html/Packages/Libraries/nikic/php-parser/lib/PhpParser/NodeTraverser.php:114
    /var/www/html/Packages/Libraries/nikic/php-parser/lib/PhpParser/NodeTraverser.php:223
    /var/www/html/Packages/Libraries/nikic/php-parser/lib/PhpParser/NodeTraverser.php:114
    /var/www/html/Packages/Libraries/nikic/php-parser/lib/PhpParser/NodeTraverser.php:223
    /var/www/html/Packages/Libraries/nikic/php-parser/lib/PhpParser/NodeTraverser.php:91
    /var/www/html/Packages/Libraries/j6s/phparch/src/Parser/Parser.php:56
    /var/www/html/Packages/Libraries/j6s/phparch/src/Parser/Parser.php:37
    /var/www/html/Packages/Libraries/j6s/phparch/src/PhpArch.php:57
    /var/www/html/Packages/Libraries/j6s/phparch/src/PhpArch.php:31
    

    It seems to me that the DocBlockTypeAnnotations visitor tries to resolve a class named \Generator<FilterItem> instead of resolving that string.

    bug good first issue 
    opened by j6s 0
  • Check for namespaces not loaded by composer

    Check for namespaces not loaded by composer

    Since in most applications these days all autoloading is done by composer phparch could validate that only Namespaces loaded by composer are being used in the application.

    This would help keep separation between modules clear even if they are used in the same context most of the times.

    This would need some digging into the internals of the autoloader. From what I know at this point all autoloader declarations should be available through the generated autoloader class and that could be used to build validation rules in PHPArch - however the big question is: Where do we get that instance from:

    • Is there a global reference to it somewhere magically? (I doubt that)
    • Can we just new up a new instance of the autoloader without breaking anything?
    enhancement 
    opened by j6s 0
  • Add visitor to extract static method calls

    Add visitor to extract static method calls

    Static method calls are currently not extracted from class definitions.

    Example:

    class SomeClass {
        public function someMethod() 
        {
            Some\Static\Class::methodCallNotTrackedAsDependency();
        }
    }
    
    opened by j6s 0
  • Add visitor to extract dependencies from typehints

    Add visitor to extract dependencies from typehints

    Currently typehints (both, parameter and return type hints) are not tracked as dependencies. Examples:

    public function(This\Is\Not\Tracked $dependency): Also\Not\Tracked
    {
    
    }
    
    opened by j6s 0
  • Classes of PHP extensions not detected via composer `ext-*`

    Classes of PHP extensions not detected via composer `ext-*`

    I have the following in my composer.json

        "require": {
            "ext-ds": "*",
        }
    

    but I still get an error if I'm using Ds\\* classes: App must only depend on dependencies in /app/tests/../composer.json (/app/tests/../composer.lock) but XXX depends on Ds\Map.

    As workaround I had to add Ds\\ as explicitly allowed dependency:

            $composerParser = new ComposerFileParser(__DIR__ . '/../composer.json');
            $validator      = new MustOnlyDependOnComposerDependencies('App\\', $composerParser);
    
            // ext-ds is not listed as dev dependency
            $validator = new ExplicitlyAllowDependency($validator, 'App\\', 'Ds\\');
    
            $this->phpArch->validate($validator);
    
    opened by marc-mabe 0
  • Support wildcard dependency checks

    Support wildcard dependency checks

    Hi,

    This is a feature request to be able to support dependency checks via wildcards.

    Something like this:

    $phpArch->validate(new ForbiddenDependency('App\\Bus\\*\\Command', 'App\\Bus\\*\\Command'));
    $phpArch->validate(new ForbiddenDependency('App\\Bus\\*\\Query', 'App\\Bus\\*\\Command'));
    

    ##Background: I have a command/event/query bus implementation for business logic separated by business domain. I would like to forbid commands to trigger other commands es this should be done via events.

    ##Example: Wrong:

    class XCommandHandler implements CommandHandler {
        public function handle(XCommand $command); void
        {
            // do whatever needs to be done
    
            $this->commandBus->dispatch(new OtherCommand());
        }
    }
    
    class OtherCommandHandler implements CommandHandler {
        public function handle(OtherCommand $command); void
        {
            // do whatever needs to be done
        }
    }
    

    Correct:

    class XCommandHandler implements CommandHandler {
        public function handle(XCommand $command); void
        {
            // do whatever needs to be done
    
            $this->eventBus->dispatch(new XSucceededEvent());
        }
    }
    
    class OnXSucceededTriggerOther implements EventHandler {
        public function handle(XSucceededEvent $event); void
        {
            // do whatever needs to be done
    
            $this->commandBus->dispatch(new OtherCommand());
        }
    }
    
    class OtherCommandHandler implements CommandHandler {
        public function handle(OtherCommand $command); void
        {
            // do whatever needs to be done
        }
    }
    
    opened by marc-mabe 0
  • Docker support for unit tests

    Docker support for unit tests

    Added docker files to the home directory and added notes at the bottom of the main readme to make it easy for new developers to run/test phparch in a Linux container

    Note that 1 test currently fails: https://github.com/j6s/phparch/issues/22

    hacktoberfest 
    opened by swharden 3
  • Add callable type tracking

    Add callable type tracking

    At the moment, using callable type declarations in @param annotations will run into Runtime exceptions from phpdocumentor/type-parser:

    /**
     * @template TReturn
     * @param (callable(): TReturn) $generator
     * @return TReturn
     */
    

    will always yield the following RuntimeException:

    RuntimeException: A type is missing in an array expression
    
    /var/www/html/vendor/phpdocumentor/type-resolver/src/TypeResolver.php:286
    /var/www/html/vendor/phpdocumentor/type-resolver/src/TypeResolver.php:215
    /var/www/html/vendor/phpdocumentor/type-resolver/src/TypeResolver.php:215
    /var/www/html/vendor/phpdocumentor/type-resolver/src/TypeResolver.php:155
    /var/www/html/vendor/j6s/phparch/src/Parser/Visitor/DocBlockTypeAnnotations.php:45
    /var/www/html/vendor/j6s/phparch/src/Parser/Visitor/DocBlockTypeAnnotations.php:33
    /var/www/html/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:200
    /var/www/html/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:114
    /var/www/html/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:223
    /var/www/html/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:114
    /var/www/html/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:223
    /var/www/html/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:91
    /var/www/html/vendor/j6s/phparch/src/Parser/Parser.php:56
    /var/www/html/vendor/j6s/phparch/src/Parser/Parser.php:37
    /var/www/html/vendor/j6s/phparch/src/PhpArch.php:57
    /var/www/html/vendor/j6s/phparch/src/PhpArch.php:31
    /var/www/html/tests/Unit/ArchitectureTest.php:69
    

    Off the top of my head, I don't know a good way of catching this without extending phpdocumentor/type-resolver.

    opened by j6s 0
  • Support union types

    Support union types

    While reviewing #14 we realized that union types (such as Foo|Bar) are not supported yet.

    • Union types in docblock typehints should be parsed as each of the component
    • Union types in actual type declarations ( https://wiki.php.net/rfc/union_types_v2 ) should be parsed
    • Union types in generics (e.g. Baz<Foo|Bar>) should also be parsed as each component + the wrapper
    opened by j6s 2
  • Switch phpdoc libraries

    Switch phpdoc libraries

    Currently, phparch (or - more accurately, the DocBlockTypeAnnotations Visitor) uses phpdocumentor in order to parse PHPDoc comments and it's TypeResolver in order to resolve the symbols in these comments to fully qualified names.

    The problem is, that the TypeResolver version used by the current stable version of phpdocumentor does not handle arrays of multiple types (e.g. (Foo|Bar)[] or (Foo|null)[]). There is a beta version of the type resolver and phpdocumentor that handle this, but there has been very little action there. Additionally, many other libraries depend on the current version of phpdocumentor - so most environments probably already have a dependency that prevents this library from simply building on top of beta / new versions.

    All of this leads me to believe that something else should be used in order to parse docblocks. In a quick research I found the following libraries that could be helpful. Further research must be done.

    • parsing docblocks
      • https://packagist.org/packages/barryvdh/reflection-docblock
        • Seems to be a fork / related project of phpdocumentors reflections-docblock currently used but without the type parsing.
      • https://packagist.org/packages/doctrine/annotations
        • Only in this list because it is a symfony component. Used wildly, maybe it can be used for pure parsing without the annotation functionality
      • https://packagist.org/packages/gossi/docblock
    • parsing types
      • https://packagist.org/packages/serenata/docblock-type-parser - Not sure if this can resolve class imports using the use statement
    • other
      • https://packagist.org/packages/php-di/phpdoc-reader
        • Seems like this can do both

    Requirements for a type resolver:

    • Given a classname and a context it should be able to resolve a fully qualified name, resolving all imports / use statements.
    • Should support nullable types @return ?Foo
    • Should support simple array syntax @return Foo[]
    • Should support compound array syntax @return (Foo|Bar)[]
    • Should support alternate array syntax @return array<Foo>
    • Optional: Support for generic type annotations @return Collection<Foo>

    For reference: https://github.com/php-fig/fig-standards/blob/master/proposed/phpdoc.md

    opened by j6s 0
Releases(3.1.1)
  • 3.1.1(Apr 14, 2022)

    [3.1.1] - 2022-04-14

    Added

    • Broadened the requirement for symfony/finder to include version 6.x to be compatible with symfony 6.x projects.
    Source code(tar.gz)
    Source code(zip)
  • 3.1.0(Mar 28, 2022)

    [3.1.0] - 2022-02-28

    Added

    • Architecture now uses a ComposerFileParserFactory to create a file parser. CachedComposerFileParserFactory can be used for caching of results in order to prevent parsing the file multiple times.
    Source code(tar.gz)
    Source code(zip)
  • 3.0.0(Jan 31, 2021)

    [3.0.0] - 2021-01-31

    Added

    • PHP8 is now supported

    Removed

    • PHP 7.3 is no longer supported
    • The minimum compatible version of symfony/finder was lifted from 3.* to 4.* making this update incompatible with symfony 3.* projets.
    Source code(tar.gz)
    Source code(zip)
  • 2.0.1(Jan 28, 2021)

  • 2.0.0(Oct 14, 2020)

    Removed

    • Support for PHP 7.2 was dropped

    Changed

    • Dependencies were updated
      • phpunit/phpunit to 9.4+
      • thecodingmachine/safe to 1.3+
      • and more...
    • Added type hints to more methods
    Source code(tar.gz)
    Source code(zip)
  • 1.2.0(Jul 27, 2020)

  • 1.1.2(Mar 2, 2020)

  • 1.1.1(May 19, 2019)

    Fixed

    • Architectures are now not validated to only have autoloadable dependencies anymore because this validator disregards namespace imports.
    Source code(tar.gz)
    Source code(zip)
  • 1.1.0(May 19, 2019)

    Added

    • A new MustOnlyHaveAutoloadableDependencies validator has been added in order to prevent accidental dependencies to unrelated packages that just happen to be used in the same system often.
      • All components in architectures are now being checked against this new validator.
    • A new MustOnlyDependOnComposerDependencies validator has been added in order to prevent accidentally using namespaces that are not also declared in composer.json.
    • A new ExplicitlyAllowDependency validator allows explicitly allowing dependencies from one component to another.
    • Architectures now have a bunch of new helper methods
      • mustOnlyDependOnComposerDependencies adds MustOnlyDependOnComposerDependencies validator.
      • addComposerBasedComponent initializes a full component from the given composer.json file and adds a MustOnlyDependOnComposerDependencies validator
      • isAllowedToDependOn allows dependencies from one component to another one.
      • disallowInterdependence makes it easy to disallow dependence between many different components.
      • mustNotDependOnAnyOtherComponent makes it easy to declare core components that should not depend on anything else that is architecturally significant.

    Removed

    • Support for PHP 7.1 was dropped
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0(Feb 18, 2019)

    [1.0.0] - 2019-02-18

    Added

    • Allowing dependencies to Interfaces only is now possible
      • Using the AllowInterfaces Validation wrapper
      • Using the mustNotDependOn method on a component
      • Using the mustNotDirectlyDependOn method on an architecture
    • Bulk declaration of components using the Architecture->components method is now possible

    Fixed

    • Dependencies of anonymous / inner classes are now correctly tracked
    Source code(tar.gz)
    Source code(zip)
  • 0.3.1(Feb 18, 2019)

    [0.3.1] - 2018-12-12

    Fixed

    • Not loading tests into production autoloader anymore
    • Now ignoring references to non-existent classes
    • Fixed Validators not being able to be serialized correctly for error output
    Source code(tar.gz)
    Source code(zip)
  • 0.3.0(Feb 18, 2019)

    [0.3.0] - 2018-12-01

    Added

    • Now correctly identifies the following types of dependencies (though in most cases they have already been tracked through use statements):
      • DocBlock comments
      • Static method calls
      • Argument type annotations
      • Return type annotations

    Fixed

    • Namespace comparisons against higher up namespaces no longer match
    Source code(tar.gz)
    Source code(zip)
  • 0.2.0(Feb 18, 2019)

    [0.2.0] - 2018-11-30

    Added

    • New assertHasNoErrors method was added to make checking for architecture violations in PHPUnit easier.

    Changed

    • Switched from using php-dependency-analysis to manually using nikic/php-parser - phpda uses outdated dependencies that are hard to install on many up-to-date systems (such as a current laravel installation).
    Source code(tar.gz)
    Source code(zip)
  • 0.1.0(Feb 18, 2019)

Owner
Johannes Hertenstein
Johannes Hertenstein
SimpleTest is a framework for unit testing, web site testing and mock objects for PHP

SimpleTest SimpleTest is a framework for unit testing, web site testing and mock objects for PHP. Installation Downloads All downloads are stored on G

SimpleTest 147 Jun 20, 2022
The modern, simple and intuitive PHP unit testing framework.

atoum PHP version atoum version 5.3 -> 5.6 1.x -> 3.x 7.2 -> 8.x 4.x (current) A simple, modern and intuitive unit testing framework for PHP! Just lik

atoum 1.4k Nov 29, 2022
Full-stack testing PHP framework

Codeception Modern PHP Testing for everyone Codeception is a modern full-stack testing framework for PHP. Inspired by BDD, it provides an absolutely n

Codeception Testing Framework 4.6k Jan 7, 2023
AST based PHP Mutation Testing Framework

Infection - Mutation Testing framework Please read documentation here: infection.github.io Twitter: @infection_php Discord: https://discord.gg/ZUmyHTJ

Infection - Mutation Testing Framework for PHP 1.8k Jan 2, 2023
The PHP Unit Testing framework.

PHPUnit PHPUnit is a programmer-oriented testing framework for PHP. It is an instance of the xUnit architecture for unit testing frameworks. Installat

Sebastian Bergmann 18.8k Jan 4, 2023
PHP unit testing framework with built in mocks and stubs. Runs in the browser, or via the command line.

Enhance PHP A unit testing framework with mocks and stubs. Built for PHP, in PHP! Quick Start: Just add EnhanceTestFramework.php and you are ready to

Enhance PHP 67 Sep 12, 2022
Unit testing tips by examples in PHP

Unit testing tips by examples in PHP Introduction In these times, the benefits of writing unit tests are huge. I think that most of the recently start

Kamil RuczyƄski 894 Jan 4, 2023
Pest is an elegant PHP Testing Framework with a focus on simplicity

Pest is an elegant PHP Testing Framework with a focus on simplicity. It was carefully crafted to bring the joy of testing to PHP. Explore the docs: pe

PEST 5.9k Dec 27, 2022
PHP libraries that makes Selenium WebDriver + PHPUnit functional testing easy and robust

Steward: easy and robust testing with Selenium WebDriver + PHPUnit Steward is a set of libraries made to simplify writing and running robust functiona

LMC s.r.o. 219 Dec 17, 2022
Humbug - a Mutation Testing framework for PHP

Humbug is a Mutation Testing framework for PHP to measure the real effectiveness of your test suites and assist in their improvement. It eats Code Coverage for breakfast.

Humbug 1.1k Dec 28, 2022
An effort to make testing PHP code as easy and fun as its JavaScript equivalent

An effort to make testing PHP code as easy and fun as its JavaScript equivalent when using the excellent Jasmine, from which syntax and general usage is shamelessly borrowed.

Johan Stenqvist 24 Apr 22, 2022
:computer: Parallel testing for PHPUnit

ParaTest The objective of ParaTest is to support parallel testing in PHPUnit. Provided you have well-written PHPUnit tests, you can drop paratest in y

null 2k Dec 31, 2022
Few additional testing assertions for Laravel views

Laravel View Test Assertions Few additional assertions for testing Laravel views. Why Laravel has well established and documented way of testing reque

null 13 Jun 12, 2022
Real-world Project to learning about Unit Testing/TDD with Laravel for everybody

KivaNote - a Laravel TDD Sample Project Let me introduce you to KivaNote, a simple real-world application using Laravel to show you how the TDD & Unit

(Seth) Phat Tran 10 Dec 31, 2022
Package for unit testing Laravel form request classes

Package for unit testing Laravel form request classes. Why Colin DeCarlo gave a talk on Laracon online 21 about unit testing Laravel form requests cla

null 18 Dec 11, 2022
Magic Test allows you to write browser tests by simply clicking around on the application being tested, all without the slowness of constantly restarting the testing environment.

Magic Test for Laravel Magic Test allows you to write browser tests by simply clicking around on the application being tested, all without the slownes

null 400 Jan 5, 2023
A drop in fake logger for testing with the Laravel framework.

Log fake for Laravel A bunch of Laravel facades / services are able to be faked, such as the Dispatcher with Bus::fake(), to help with testing and ass

Tim MacDonald 363 Dec 19, 2022
A video course for laravel artisan to learn creating API using testing

About Laravel Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experie

Bitfumes 16 Oct 6, 2022
PHPUnit extension for database interaction testing.

This extension is no longer maintained DbUnit PHPUnit extension for database interaction testing. Installation Composer If you use Composer to manage

Sebastian Bergmann 224 Aug 20, 2022