Giter VIP home page Giter VIP logo

silverstripe-phpstan's Introduction

PHPStan for Silverstripe

A module allowing PHPStan to work with Silverstripe.

๐ŸŽญ Tests codecov Dependabot phpstan composer Packagist Version

Features:

  • Support for DataObject::get(), ie. it understands you have a DataList of iterable SiteTree records.
  • Support for DataObject db, has_one, has_many and many_many magic properties and methods, ie. it knows SiteTree::Title is a string, that SiteTree::ParentID is an integer and that SiteTree::Parent() is a SiteTree record.
  • Support for singleton('SiteTree') and Injector::inst()->get('SiteTree'), ie. it knows these will return "SiteTree". If you override these with the injector, it'll also know what class you're actually using.
  • Support for config properties

This PHPStan module is able to reason about extensions installed specific to your project as it bootstraps the SilverStripe config system. So if you've added an extension to your Page object that adds an additional db field, PHPStan will be able to reason about it.

Composer Install

SilverStripe 5.X

composer require --dev syntro/silverstripe-phpstan ^5

For Silverstripe 4, see the corresponding branch.

Requirements

  • SilverStripe 5.0+

Documentation

Known Limitations / Gotchas

  • PHPStan checks if properties are actually initialized, read and written. This however does not apply to Silverstripes configuration properties. In order to tell PHPStan which of your properties are configuration values, you have to use the @config docblock.
  • The type of the owner property can't be reasoned about for extensions. You must use getOwner(). Related Issues: #1043 and #1044

Credits

This is a fork of the original symbiote/silverstripe-phpstan maintained by Symbiote & Contributors. We have created this, as the original repository seems to be no longer actively maintained.

silverstripe-phpstan's People

Contributors

dependabot[bot] avatar erikfrerejean avatar grantlucas avatar michalkleiner avatar mleutenegger avatar satrun77 avatar silbinarywolf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

silverstripe-phpstan's Issues

Error when running PHPStan: Call to undefined method PhpParser\Node\Expr\MethodCall::toString()

Silverstripe 4.13, PHPStan 1.10.38, silverstripe-phpstan 4.x-dev #fcb734c

This method in an Extension to SiteTree makes PHPStan error:

    /**
     * Helper, to reload the current page e.g. in SearchForm to load additional fields that might not be loaded.
     *
     * @return SiteTree
     */
    public function getReload()
    {
        return $this->getOwner()::get()->byID($this->getOwner()->ID);
    }

The error is:

Internal error: Internal error: Call to undefined method PhpParser\Node\Expr\MethodCall::toString() in file /path/to/app/src/myproject/Extensions/SiteTree.php

 Post the following stack trace to https://github.com/phpstan/phpstan/issues/new?template=Bug_report.yaml:
 #0 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(3494): Syntro\SilverstripePHPStan\Type\DataObjectGetStaticReturnTypeExtension->getTypeFromStaticMethodCall()
 #1 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1422): PHPStan\Analyser\MutatingScope->methodCallReturnType()
 #2 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1428): PHPStan\Analyser\MutatingScope->PHPStan\Analyser\{closure}()
 #3 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(566): PHPStan\Analyser\MutatingScope->resolveType()
 #4 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NullsafeOperatorHelper.php(12): PHPStan\Analyser\MutatingScope->getType()
 #5 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Rules/Methods/MethodCallCheck.php(51): PHPStan\Analyser\NullsafeOperatorHelper::getNullsafeShortcircuitedExprRespectingScope()
 #6 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Rules/Methods/CallMethodsRule.php(42): PHPStan\Rules\Methods\MethodCallCheck->check()
 #7 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(107): PHPStan\Rules\Methods\CallMethodsRule->processNode()
 #8 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Node/ClassStatementsGatherer.php(108): PHPStan\Analyser\FileAnalyser->PHPStan\Analyser\{closure}()
 #9 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(543): PHPStan\Node\ClassStatementsGatherer->__invoke()
 #10 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(2622): PHPStan\Analyser\NodeScopeResolver::PHPStan\Analyser\{closure}()
 #11 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(1574): PHPStan\Analyser\NodeScopeResolver->callNodeCallbackWithExpression()
 #12 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(581): PHPStan\Analyser\NodeScopeResolver->processExprNode()
 #13 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(388): PHPStan\Analyser\NodeScopeResolver->processStmtNode()
 #14 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(561): PHPStan\Analyser\NodeScopeResolver->processStmtNodes()
 #15 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(388): PHPStan\Analyser\NodeScopeResolver->processStmtNode()
 #16 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(644): PHPStan\Analyser\NodeScopeResolver->processStmtNodes()
 #17 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(388): PHPStan\Analyser\NodeScopeResolver->processStmtNode()
 #18 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(616): PHPStan\Analyser\NodeScopeResolver->processStmtNodes()
 #19 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(357): PHPStan\Analyser\NodeScopeResolver->processStmtNode()
 #20 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(166): PHPStan\Analyser\NodeScopeResolver->processNodes()
 #21 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(132): PHPStan\Analyser\FileAnalyser->analyseFile()
 #22 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97): PHPStan\Command\WorkerCommand->PHPStan\Command\{closure}()
 #23 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/clue/ndjson-react/src/Decoder.php(117): _PHPStan_adbc35a1c\Evenement\EventEmitter->emit()
 #24 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97): _PHPStan_adbc35a1c\Clue\React\NDJson\Decoder->handleData()
 #25 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/Util.php(62): _PHPStan_adbc35a1c\Evenement\EventEmitter->emit()
 #26 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97):
 _PHPStan_adbc35a1c\React\Stream\Util::_PHPStan_adbc35a1c\React\Stream\{closure}()
 #27 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/DuplexResourceStream.php(154): _PHPStan_adbc35a1c\Evenement\EventEmitter->emit()
 #28 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(201): _PHPStan_adbc35a1c\React\Stream\DuplexResourceStream->handleData()
 #29 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(173): _PHPStan_adbc35a1c\React\EventLoop\StreamSelectLoop->waitForStreamActivity()
 #30 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(98): _PHPStan_adbc35a1c\React\EventLoop\StreamSelectLoop->run()
 #31 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Command/Command.php(259): PHPStan\Command\WorkerCommand->execute()
 #32 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(870): _PHPStan_adbc35a1c\Symfony\Component\Console\Command\Command->run()
 #33 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(261): _PHPStan_adbc35a1c\Symfony\Component\Console\Application->doRunCommand()
 #34 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(157): _PHPStan_adbc35a1c\Symfony\Component\Console\Application->doRun()
 #35 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(124): _PHPStan_adbc35a1c\Symfony\Component\Console\Application->run()
 #36 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(125): _PHPStan_adbc35a1c\{closure}()
 #37 /path/to/vendor/phpstan/phpstan/phpstan(7): require('phar:///ho>  Internal error: Internal error: Call to undefined method PhpParser\Node\Expr\MethodCall::toString() in file /path/to/app/src/myproject/Extensions/SiteTree.php

 Post the following stack trace to https://github.com/phpstan/phpstan/issues/new?template=Bug_report.yaml:
 #0 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(3494): Syntro\SilverstripePHPStan\Type\DataObjectGetStaticReturnTypeExtension->getTypeFromStaticMethodCall()
 #1 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1422): PHPStan\Analyser\MutatingScope->methodCallReturnType()
 #2 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1428): PHPStan\Analyser\MutatingScope->PHPStan\Analyser\{closure}()
 #3 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(566): PHPStan\Analyser\MutatingScope->resolveType()
 #4 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NullsafeOperatorHelper.php(12): PHPStan\Analyser\MutatingScope->getType()
 #5 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Rules/Methods/MethodCallCheck.php(51): PHPStan\Analyser\NullsafeOperatorHelper::getNullsafeShortcircuitedExprRespectingScope()
 #6 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Rules/Methods/CallMethodsRule.php(42): PHPStan\Rules\Methods\MethodCallCheck->check()
 #7 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(107): PHPStan\Rules\Methods\CallMethodsRule->processNode()
 #8 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Node/ClassStatementsGatherer.php(108): PHPStan\Analyser\FileAnalyser->PHPStan\Analyser\{closure}()
 #9 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(543): PHPStan\Node\ClassStatementsGatherer->__invoke()
 #10 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(2622): PHPStan\Analyser\NodeScopeResolver::PHPStan\Analyser\{closure}()
 #11 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(1574): PHPStan\Analyser\NodeScopeResolver->callNodeCallbackWithExpression()
 #12 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(581): PHPStan\Analyser\NodeScopeResolver->processExprNode()
 #13 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(388): PHPStan\Analyser\NodeScopeResolver->processStmtNode()
 #14 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(561): PHPStan\Analyser\NodeScopeResolver->processStmtNodes()
 #15 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(388): PHPStan\Analyser\NodeScopeResolver->processStmtNode()
 #16 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(644): PHPStan\Analyser\NodeScopeResolver->processStmtNodes()
 #17 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(388): PHPStan\Analyser\NodeScopeResolver->processStmtNode()
 #18 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(616): PHPStan\Analyser\NodeScopeResolver->processStmtNodes()
 #19 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(357): PHPStan\Analyser\NodeScopeResolver->processStmtNode()
 #20 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(166): PHPStan\Analyser\NodeScopeResolver->processNodes()
 #21 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(132): PHPStan\Analyser\FileAnalyser->analyseFile()
 #22 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97): PHPStan\Command\WorkerCommand->PHPStan\Command\{closure}()
 #23 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/clue/ndjson-react/src/Decoder.php(117): _PHPStan_adbc35a1c\Evenement\EventEmitter->emit()
 #24 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97): _PHPStan_adbc35a1c\Clue\React\NDJson\Decoder->handleData()
 #25 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/Util.php(62): _PHPStan_adbc35a1c\Evenement\EventEmitter->emit()
 #26 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97):
 _PHPStan_adbc35a1c\React\Stream\Util::_PHPStan_adbc35a1c\React\Stream\{closure}()
 #27 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/DuplexResourceStream.php(154): _PHPStan_adbc35a1c\Evenement\EventEmitter->emit()
 #28 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(201): _PHPStan_adbc35a1c\React\Stream\DuplexResourceStream->handleData()
 #29 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(173): _PHPStan_adbc35a1c\React\EventLoop\StreamSelectLoop->waitForStreamActivity()
 #30 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(98): _PHPStan_adbc35a1c\React\EventLoop\StreamSelectLoop->run()
 #31 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Command/Command.php(259): PHPStan\Command\WorkerCommand->execute()
 #32 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(870): _PHPStan_adbc35a1c\Symfony\Component\Console\Command\Command->run()
 #33 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(261): _PHPStan_adbc35a1c\Symfony\Component\Console\Application->doRunCommand()
 #34 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(157): _PHPStan_adbc35a1c\Symfony\Component\Console\Application->doRun()
 #35 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(124): _PHPStan_adbc35a1c\Symfony\Component\Console\Application->run()
 #36 phar:///path/to/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(125): _PHPStan_adbc35a1c\{closure}()
 #37 /path/to/vendor/phpstan/phpstan/phpstan(7): require('phar:///path/to...')
 #38 /path/to/vendor/bin/phpstan(115): include('/path/to...')
 #39 {main}
 Child process error (exit code 1):me/we...')
 #38 /path/to/vendor/bin/phpstan(115): include('/path/to...')
 #39 {main}
 Child process error (exit code 1):

when I comment out that method, phpstan runs without an internal error.

Missing composer.lock file and phpstan-src

Hey, I'm trying to set up my local environment. I'm facing a couple of issues and would like some advice in how to proceed.

  1. Missing composer.lock file results in mismatching package dependency versions
  2. phpstan/phpstan-src is required for LSP help and type checking too.

I assume you have phpstan-src globally available somehow. I'm not too familiar with how composer and the LSPs resolve libraries outside this project.

Handle magic `Page` and `PageController` classes

Various modules and core classes assume the existance of the Page and PageController classes, when developing a new module that has silverstripe/cms as dependency it is nearly impossible to run phpstan on it due to the way classes are bootstrapped and this implicit dependency on these classes. The analysis breaks with errors like

Error thrown in /app/vendor/silverstripe/cms/code/Model/RedirectorPage.php on line 24 while loading bootstrap file /app/vendor/syntro/silverstripe-phpstan/bootstrap.php: Class "Page" not found

From the outside I can't really find a decent way around this, using stubs breaks as this isn't an analysis failure but rather the bootstrap that breaks. Shipping an additional bootstrap file which defines these classes doesn't consistently work and probably shouldn't be considered a real solution because that would mean that every module needs to define those classes somehow and strip them before publishing to prevent issues in websites.

A nice addition to this module would be to handle these cases, so when these classes do not exist (which most likely means that phpstan is ran in a module rather then a fully functional website) define the symbols. As a quick test I've hacked the bootstrap file with a

if (!class_exists('Page')) {
    require __DIR__ . '/Page.php';
    require __DIR__ . '/PageController.php';
}

which causes the analysis to properly run in this module.

Error: SilverStripe\Versioned\Versioned.versioned was not found

When adding the Versioned extension in code like

    private static $extensions = [
        Versioned::class . '.versioned'
    ];

I get the following error:

Class SilverStripe\Versioned\Versioned.versioned was not found while trying to analyse it - discovering symbols is probably not configured properly.

This can be fixed by defining an alias in bootstrap.php like:

class_alias(Versioned::class, Versioned::class . '.versioned');

Maybe those injector alias classes can be handled more elegantly.

redirect is not a early terminating method, but httpError is

In the phpstan.neon the redirect method is defined as a earlyTerminatingMethodCalls, but this method does not exit it returns a HTTPResponse.

(Yes the method has the side-effect, that is set the response object of the controller - but it does not early exits ...)

httpError which throws a HTTPResponse_Exception should be marked as earlyTerminating instead.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.