A project to add Psalm support for Drupal for security testing, focused only on taint analysis.

Overview

Test Status

psalm-plugin-drupal

A Drupal integration for Psalm focused on security scanning (SAST) taint analysis.

Features

  • Stubs for sinks, sources, and sanitizers
  • Loading of .module and .theme files
  • Autoloading of modules without an installed site
  • Support for \Drupal::service()
  • Custom script for dumping the Drupal container to XML
  • Support for detecting tainted render arrays
  • Novel support for Controllers and Form methods.

Installing and running on your Drupal site

This plugin is meant to be used on your Drupal site, for the scanning of custom modules. Note that if you follow this guide and run it on a contrib module, and you find a valid result, you should report your findings to the Drupal Security Team.

To install the plugin:

  1. Run composer require mortenson/psalm-plugin-drupal:dev-master
  2. Change directories to the root of your Drupal installation (ex: cd web, cd docroot).
  3. Create a psalm.xml file in the root of your Drupal installation like:
<?xml version="1.0"?>
<psalm
  errorLevel="6"
  resolveFromConfigFile="true"
  runTaintAnalysis="true"
  autoloader="../vendor/mortenson/psalm-plugin-drupal/scripts/autoload.php"
>
    <fileExtensions>
        <extension name=".php" />
        <extension name=".module" />
        <extension name=".theme" />
        <extension name=".inc" />
    </fileExtensions>
    <projectFiles>
        <directory name="modules/custom"/>
    </projectFiles>
    <plugins>
        <pluginClass class="Psalm\SymfonyPsalmPlugin\Plugin">
            <containerXml>DrupalContainerDump.xml</containerXml>
        </pluginClass>
        <pluginClass class="mortenson\PsalmPluginDrupal\Plugin">
            <containerXml>DrupalContainerDump.xml</containerXml>
            <extensions>
              <!-- List your modules explicitly here, as the scan may happen without a database -->
              <module name="my_custom_module" />
              <module name="my_module_dependency" />
            </extensions>
        </pluginClass>
    </plugins>
</psalm>
  1. Run php ../vendor/mortenson/psalm-plugin-drupal/scripts/dump_script.php && ../vendor/bin/psalm .

Note that the path to vendor may change based on your Drupal installation.

Generating an entrypoint for seemingly unused class methods

Drupal's code paths aren't always clear, especially in Drupal 8. Because of this, things like Controller methods (aka route callbacks) will not be analyzed when running Psalm.

To have Psalm analyze these paths, you'll need to generate an entrypoint file that executes the methods you want to test.

A script has been included for you to generate this entrypoint for you. To use it, do the following:

  1. Run php ../vendor/mortenson/psalm-plugin-drupal/scripts/generate_entrypoint.php <comma separated paths to your custom modules>
  2. Add <file name="psalm_drupal_entrypoint.module"></file> to your psalm.xml file, under the <projectFiles> node.
  3. Run Psalm.

Currently, only routing.yml files are parsed to generate the entrypoint, focusing on Controller and Form methods.

Contributing

Running and writing tests

Tests use Codeception via weirdan/codeception-psalm-module.

You can run tests with composer run test.

To write tests, edit tests/acceptance/PsalmPluginDrupal.feature and add a new Scenario.

To run a single failing test, add the @failing tag above the Scenario: line, then run composer run test-failing.

Checking code style

Code style should be checked before committing code.

To do this, run composer run cs-check, or composer run cs-fix to automatically fix issues with phpcbf.

Comments
  • vimeo/psalm:dev-master requires composer/semver:^2 or ^3

    vimeo/psalm:dev-master requires composer/semver:^2 or ^3

    • vimeo/psalm (as of vimeo/psalm@5ecab7b9e70f9c9894b66afcc2005ab8430da682) requires composer/semver ^2 || ^3
    • drupal/core-recommended:8.9.13 requires composer/semver:1.5.1 (here)
    • vimeo/psalm:4.6.4 may be the last version that works with Drupal 8.9?
    • nope, 4.6.4 lacks RemoveTaintInterface, trying mortenson/psalm-plugin-drupal:dev-master#4aabb411a8e90cf1fb465a687c093e549e1212aa and vimeo/psalm:dev-master#2edf61399a4f85a3c9dbd76d5fe204aa7231ed55 ...

    Opening an issue to discuss / share solution.

    opened by xurizaemon 3
  • Issue #9: Use the latest version of psalm/plugin-symfony compatible with Symfony 6 and Psalm 4.

    Issue #9: Use the latest version of psalm/plugin-symfony compatible with Symfony 6 and Psalm 4.

    I tried to execute PHPUnit tests locally, but for that I had to change PHPUnit version because PHPUnit 7 is not compatible with PHP 8.1, so I tried with PHPUnit 8.

    After that I got some tests in error (the error was the same for all tests):

    Fail  Failed to parse output: Fatal error: Declaration of Drupal\Core\DrupalKernel::handle(Symfony\Component\HttpFoundation\Request $request, $type = self::MASTER_REQUEST, $catch = true) must be compatible with Symfony\Component\HttpKernel\HttpKernelInterface::handle(Symfony\Component\HttpFoundation\Request $request, int $type = self::MAIN_REQUEST, bool $catch = true): Symfony\Component\HttpFoundation\Response in /project/contrib/psalm-plugin-drupal/tests/_tmp/drupal/core/lib/Drupal/Core/DrupalKernel.php on line 691
    

    So I tried to use Drupal 10.0.0 in pretest.php, less tests in error, new error:

    1) Psalm Plugin Drupal: ContainerHandler works
     Test  tests/acceptance/PsalmPluginDrupal.feature:ContainerHandler works
     Step  I see these errors 
       | Type       | Message              |
       | TaintedSql | Detected tainted SQL |
     Fail  Failed to parse output: Error: Call to undefined method Symfony\Component\DependencyInjection\Definition::getClassName() in /project/contrib/psalm-plugin-drupal/ContainerHandler.php on line 61 #0 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/EventDispatcher.php(319): mortenson\PsalmPluginDrupal\ContainerHandler::afterMethodCallAnalysis(Object(PhpParser\Node\Expr\StaticCall), 'Drupal::service', 'Drupal::service', 'Drupal::service', Object(Psalm\Context), Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(Psalm\Codebase), Array, Object(Psalm\Type\Union))
    #1 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php(409): Psalm\Internal\EventDispatcher->dispatchAfterMethodCallAnalysis(Object(Psalm\Plugin\EventHandler\Event\AfterMethodCallAnalysisEvent))
    #2 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php(877): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\ExistingAtomicStaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(PhpParser\Node\Identifier), Array, Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Object(Psalm\Internal\MethodIdentifier), 'Drupal::service', Object(Psalm\Storage\ClassLikeStorage), false)
    #3 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php(202): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\AtomicStaticCallAnalyzer::handleNamedCall(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(PhpParser\Node\Identifier), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Array, 'Drupal', false, true)
    #4 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php(215): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\AtomicStaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), false, false, false, true)
    #5 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(190): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context))
    #6 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(78): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context), false, NULL, false)
    #7 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php(60): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context))
    #8 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(186): Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context))
    #9 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(78): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context), false, NULL, true)
    #10 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(572): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\MethodCall), Object(Psalm\Context), false, NULL, true)
    #11 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(207): Psalm\Internal\Analyzer\StatementsAnalyzer::analyzeStatement(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\Expression), Object(Psalm\Context), NULL)
    #12 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(205): Psalm\Internal\Analyzer\StatementsAnalyzer->analyze(Array, Object(Psalm\Context), NULL, true)
    #13 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(362): Psalm\Internal\Analyzer\FileAnalyzer->analyze()
    #14 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(619): Psalm\Internal\Codebase\Analyzer->Psalm\Internal\Codebase\{closure}(0, '/project/contri...')
    #15 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(291): Psalm\Internal\Codebase\Analyzer->doAnalysis(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 7)
    #16 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(691): Psalm\Internal\Codebase\Analyzer->analyzeFiles(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 7, false, true)
    #17 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalm.php(373): Psalm\Internal\Analyzer\ProjectAnalyzer->check('/project/contri...', false)
    #18 /project/contrib/psalm-plugin-drupal/vendor/vimeo/psalm/psalm(7): Psalm\Internal\Cli\Psalm::run(Array)
    #19 /project/contrib/psalm-plugin-drupal/vendor/bin/psalm(120): include('/project/contri...')
    #20 {main}
    Error: Call to undefined method Symfony\Component\DependencyInjection\Definition::getClassName() in mortenson\PsalmPluginDrupal\ContainerHandler::afterMethodCallAnalysis() (line 61 of /project/contrib/psalm-plugin-drupal/ContainerHandler.php).
    Error:Syntax error
    
    Scenario Steps:
    
     14. $I->seeTheseErrors("| Type       | Message              |\n| TaintedSql | Detected tainted SQL |")
     13. $I->runPsalmIn("tests/_tmp/drupal") at tests/_support/AcceptanceTester.php:38
     12. $I->runShellCommand("cd tests/_tmp/drupal && php ../../../scripts/generate_entrypoint.php ../../../tests/_run") at tests/_support/AcceptanceTester.php:36
     11. $I->haveTheFollowingCode("\\Drupal::service('database')->query($_GET['input']);")
     10. $I->haveTheFollowingConfig("<?xml version="1.0"?>\n<psalm\n  errorLevel="6"\n  resolveFromConfigFile="true"\n  runTaintAnalysis="true"\n  autoloader="../../../scripts/autoload.php"\n>\n    <fileExtensi...")
     9. $I->haveTheFollowingCodePreamble("<?php\n")
    
    

    Finally got it!

    But I think it will require a new major version...

    opened by FlorentTorregrosa 1
  • Drupal10 / Symfony 6 compatibility

    Drupal10 / Symfony 6 compatibility

    - Root composer.json requires mortenson/psalm-plugin-drupal dev-master -> satisfiable by mortenson/psalm-plugin-drupal[dev-master].
        - mortenson/psalm-plugin-drupal dev-master requires psalm/plugin-symfony ^2.2 -> satisfiable by psalm/plugin-symfony[v2.2.0, ..., v2.x-dev].
        - psalm/plugin-symfony[v2.2.0, ..., v2.x-dev] require symfony/framework-bundle ^3.0 || ^4.0 || ^5.0 -> satisfiable by symfony/framework-bundle[v3.0.0-BETA1, ..., 3.4.x-dev, v4.0.0-BETA1, ..., 4.4.x-dev, v5.0.0-BETA1, ..., 5.4.x-dev].
    

    Need to use a new major version of "psalm/plugin-symfony" https://github.com/psalm/psalm-plugin-symfony/tags to be able to use with Drupal 10.

    opened by FlorentTorregrosa 1
  • PHP Warning in RenderArrayTainter

    PHP Warning in RenderArrayTainter

    Depending on the code base I execute Psalm. I have a huge number of similar warnings like the following.

    </pre><em class="placeholder">Warning</em>: Uninitialized string offset 0 in <em class="placeholder">mortenson\PsalmPluginDrupal\RenderArrayTainter::removeTaints()</em> (line <em class="placeholder">52</em> of <em class="placeholder">/project/vendor/mortenson/psalm-plugin-drupal/RenderArrayTainter.php</em>). <pre class="backtrace">mortenson\PsalmPluginDrupal\RenderArrayTainter::removeTaints(Object) (Line: 616)
    Psalm\Internal\EventDispatcher-&gt;dispatchRemoveTaints(Object) (Line: 409)
    Psalm\Internal\Analyzer\Statements\Expression\ArrayAnalyzer::analyzeArrayItem(Object, Object, Object, Object, Object) (Line: 88)
    Psalm\Internal\Analyzer\Statements\Expression\ArrayAnalyzer::analyze(Object, Object, Object) (Line: 287)
    Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object, Object, Object, , NULL, ) (Line: 78)
    Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object, Object, Object) (Line: 240)
    Psalm\Internal\Analyzer\Statements\Expression\AssignmentAnalyzer::analyze(Object, Object, Object, NULL, Object, NULL) (Line: 167)
    Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object, Object, Object, , NULL, 1) (Line: 78)
    Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object, Object, Object, , NULL, 1) (Line: 572)
    Psalm\Internal\Analyzer\StatementsAnalyzer::analyzeStatement(Object, Object, Object, NULL) (Line: 207)
    Psalm\Internal\Analyzer\StatementsAnalyzer-&gt;analyze(Array, Object) (Line: 387)
    Psalm\Internal\Analyzer\Statements\Block\LoopAnalyzer::analyze(Object, Array, Array, Array, Object, Object, , 1) (Line: 340)
    Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer::analyze(Object, Object, Object) (Line: 530)
    Psalm\Internal\Analyzer\StatementsAnalyzer::analyzeStatement(Object, Object, Object, Object) (Line: 207)
    Psalm\Internal\Analyzer\StatementsAnalyzer-&gt;analyze(Array, Object, Object, 1) (Line: 476)
    Psalm\Internal\Analyzer\FunctionLikeAnalyzer-&gt;analyze(Object, Object, Object) (Line: 1798)
    Psalm\Internal\Analyzer\ClassAnalyzer-&gt;analyzeClassMethod(Object, Object, Object, Object, Object) (Line: 425)
    Psalm\Internal\Analyzer\ClassAnalyzer-&gt;analyze(Object, Object) (Line: 229)
    Psalm\Internal\Analyzer\FileAnalyzer-&gt;analyze() (Line: 362)
    Psalm\Internal\Codebase\Analyzer-&gt;Psalm\Internal\Codebase\{closure}(17, &#039;/project/app/modules/custom/&#039;) (Line: 211)
    Psalm\Internal\Fork\Pool-&gt;__construct(Object, Array, Object, Object, Object, Object) (Line: 428)
    Psalm\Internal\Codebase\Analyzer-&gt;doAnalysis(Object, 7) (Line: 291)
    Psalm\Internal\Codebase\Analyzer-&gt;analyzeFiles(Object, 7, , 1) (Line: 691)
    Psalm\Internal\Analyzer\ProjectAnalyzer-&gt;check(&#039;/project/app/&#039;, ) (Line: 373)
    Psalm\Internal\Cli\Psalm::run(Array) (Line: 7)
    include(&#039;/project/vendor/vimeo/psalm/psalm&#039;) (Line: 120)
    </pre>
    

    My Psalm config: https://gitlab.com/florenttorregrosa-drupal/docker-drupal-project/-/blob/9.x/scripts/quality/psalm/psalm.xml

    I will create a PR.

    opened by FlorentTorregrosa 1
  • InvalidArgumentException: Could not get class storage for drupal\core\entity\entitytypebundleinfo

    InvalidArgumentException: Could not get class storage for drupal\core\entity\entitytypebundleinfo

    Just trying this out with the default config (with the generate bits added for a few modules). And I seem to get stuck at this when running threads=1. With more threads it hits other things (same error classification but different class name).

    I'm unsure what causes it as I'm honestly fairly new to psalm (been using phpstan a while). Any ideas what might cause it? Guessing something missing from the container? Tracking it with debug-by-line and threads=1 shows below:

    Analyzing /www/modules/custom/CUSTOM_MODULE/src/Form/CUSTOMEntityDeleteForm.php
    /www/modules/custom/CUSTOM_MODULE/src/Form/CUSTOMEntityDeleteForm.php:12
    /www/core/lib/Drupal/Core/Entity/ContentEntityForm.php:57
    /www/core/lib/Drupal/Core/Entity/ContentEntityForm.php:58
    /www/core/lib/Drupal/Core/Entity/ContentEntityForm.php:59
    /www/core/lib/Drupal/Core/Entity/EntityForm.php:81
    /www/core/lib/Drupal/Core/Entity/EntityForm.php:82
    /www/core/lib/Drupal/Core/Entity/EntityForm.php:83
    /www/core/lib/Drupal/Core/Entity/EntityForm.php:88
    /www/core/lib/Drupal/Core/Entity/ContentEntityForm.php:61
    /www/core/lib/Drupal/Core/Entity/ContentEntityForm.php:62
    

    The ContentEntityForm:62 is the $this->entityTypeBundleInfo below

      public function __construct(EntityRepositoryInterface $entity_repository, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL) {
        if ($entity_repository instanceof EntityManagerInterface) {
          @trigger_error('Passing the entity.manager service to ContentEntityForm::__construct() is deprecated in Drupal 8.6.0 and will be removed before Drupal 9.0.0. Pass the entity.repository service instead. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED);
          $this->entityManager = $entity_repository;
        }
        $this->entityRepository = $entity_repository;
        $this->entityTypeBundleInfo = $entity_type_bundle_info ?: \Drupal::service('entity_type.bundle.info');
        $this->time = $time ?: \Drupal::service('datetime.time');
      }
    
    InvalidArgumentException: Could not get class storage for drupal\core\entity\entitytypebundleinfo
    
    opened by driskell 7
Owner
Samuel Mortenson
Samuel Mortenson
phpcs-security-audit is a set of PHP_CodeSniffer rules that finds vulnerabilities and weaknesses related to security in PHP code

phpcs-security-audit v3 About phpcs-security-audit is a set of PHP_CodeSniffer rules that finds vulnerabilities and weaknesses related to security in

Floe design + technologies 655 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
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
Static code analysis to find violations in a dependency graph

PhpDependencyAnalysis PhpDependencyAnalysis is an extendable static code analysis for object-oriented PHP-Projects to generate dependency graphs from

Marco Muths 546 Dec 7, 2022
Performs advanced static analysis on PHP code

PHP Analyzer Please report bugs or feature requests via our website support system ? in bottom right or by emailing [email protected]. Contri

Continuous Inspection 443 Sep 23, 2022
The Exakat Engine : smart static analysis for PHP

Exakat The Exakat Engine is an automated code reviewing engine for PHP. Installation Installation with the phar Phar is the recommended installation p

Exakat 370 Dec 28, 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
A set of tools for lexical and syntactical analysis written in pure PHP.

Welcome to Dissect! master - this branch always contains the last stable version. develop - the unstable development branch. Dissect is a set of tools

Jakub Lédl 221 Nov 29, 2022
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
Deptrac is a static code analysis tool for PHP that helps you communicate, visualize and enforce architectural decisions in your projects

Deptrac is a static code analysis tool for PHP that helps you communicate, visualize and enforce architectural decisions in your projects. You can freely define your architectural layers over classes and which rules should apply to them.

QOSSMIC GmbH 2.2k Dec 30, 2022
Static Analysis Results Baseliner

Static Analysis Results Baseliner (SARB) Why SARB Requirements Installing Using SARB Examples Further reading Why SARB? If you've tried to introduce a

Dave Liddament 151 Jan 3, 2023
Infection Static Analysis Plugin

Static analysis on top of mutation testing - prevents escaped mutants from being invalid according to static analysis

Roave, LLC 108 Jan 2, 2023
Parse: A Static Security Scanner

Parse: A PHP Security Scanner PLEASE NOTE: This tool is still in a very early stage. The work continues... The Parse scanner is a static scanning tool

psec.io 342 Jan 2, 2023
PHP Architecture Tester - Easy to use architectural testing tool for PHP :heavy_check_mark:

Easy to use architecture testing tool for PHP Introduction ?? PHP Architecture Tester is a static analysis tool to verify architectural requirements.

Carlos A Sastre 765 Dec 30, 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
A generic content parser based on the devto markdown + frontmatter format, with liquid tag support

Parsed A generic content parser based on the devto post format, with front matter and liquid tag support. Parsed uses league/commonmark as base markdo

null 18 Dec 28, 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
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
Perform static analysis of Drupal PHP code with phpstan-drupal.

Perform static analysis of Drupal PHP code with PHPStan and PHPStan-Drupal on Drupal using PHP 8. For example: docker run --rm \ -v $(pwd)/example01

Dcycle 0 Dec 10, 2021