phpcon poland - static analysis of php code – how the heck did i write so many bugs?

62
Static Analysis of PHP Code How the Heck did I write so many Bugs? PHPCon Poland, September 2016 By Rouven Weßling ( ) Ecosystem Developer / Developer Evangelist, Contentful @RouvenWessling photo credit: by Warsaw Kamil Porembiński (license)

Upload: rouven-wessling

Post on 08-Jan-2017

220 views

Category:

Software


3 download

TRANSCRIPT

Static Analysis of PHPCode

How the Heck did I write so many Bugs?PHPCon Poland, September 2016

By Rouven Weßling ( )Ecosystem Developer / Developer Evangelist, Contentful

@RouvenWesslingphoto credit: by Warsaw Kamil Porembiński (license)

A content management developer platform with an APIat its core.

What is StaticAnalysis?

Analysing software withoutexecuting it.

Dynamic AnalysisxdebugxhprofPHP AnalyzerPHP Vulnerability HunterAssertions

Why use StaticAnalysis?

Spend less time on unit tests...

...and code review

class ClientTest extends \PHPUnit_Framework_TestCase{ public function testGetSynchronizationManager() { $client = new Client('b4c0n73n7fu1', 'cfexampleapi');

$this->assertInstanceOf(Manager::class, $client->getSynchronizationManager()); }}

Easy to integrate in ContinuousIntegration

Find issues that can not be foundthrough unit tests

PHP 7Abstract Syntax TreeScalar TypesStrict Types

PHP is dynamic �ReflectionVariable variablesReferencing classes/functions/properties by string

The more static your code is, theeasier it's to reason about.

Some tools

phpmd phan phpcs phpunit phploc phpcpd phpsa php7ccCoupling Detector Mondrian PHP

Assumption PhpCodeAnalyzer PHPCodeFixer php7mar PHPSemantic Versioning Checker PHP Inspection PHPlint PHP Depend PhpMetrics PHPCheckstyle PHP

Reaper PHP vuln hunter RIPS Parse SonarQube SideChannel

Analyzer TaintPHP Deptrac PhpDependencyAnalysis PHPsemver checker

phpmd phan phpcs phpunit phploc phpcpd phpsa php7ccCoupling Detector Mondrian PHP

Assumption PhpCodeAnalyzer PHPCodeFixer php7mar PHPSemantic Versioning Checker PHP Inspection PHPlint PHP Depend PhpMetrics PHPCheckstyle PHP

Reaper PHP vuln hunter RIPS Parse SonarQube SideChannel

Analyzer TaintPHP Deptrac PhpDependencyAnalysis PHPsemver checker

phpmd phan phpcs phpunit phploc phpcpd phpsa php7ccCoupling Detector Mondrian PHP

Assumption PhpCodeAnalyzer PHPCodeFixer php7mar PHPSemantic Versioning Checker PHP Inspection PHPlint PHP Depend PhpMetrics PHPCheckstyle PHP

Reaper PHP vuln hunter RIPS Parse SonarQube SideChannel

Analyzer TaintPHP Deptrac PhpDependencyAnalysis PHPsemver checker

phpmd phan phpcs phpunit phploc phpcpd phpsa php7ccCoupling Detector Mondrian PHP

Assumption PhpCodeAnalyzer PHPCodeFixer php7mar PHPSemantic Versioning Checker PHP Inspection PHPlint PHP Depend PhpMetrics PHPCheckstyle PHP

Reaper PHP vuln hunter RIPS Parse SonarQube SideChannel

Analyzer TaintPHP Deptrac PhpDependencyAnalysis PHPsemver checker

phpmd phan phpcs phpunit phploc phpcpd phpsa php7ccCoupling Detector Mondrian PHP

Assumption PhpCodeAnalyzer PHPCodeFixer php7mar PHPSemantic Versioning Checker PHP Inspection PHPlint PHP Depend PhpMetrics PHPCheckstyle PHP

Reaper PHP vuln hunter RIPS Parse SonarQube SideChannel

Analyzer TaintPHP Deptrac PhpDependencyAnalysis PHPsemver checker

phpmd phan phpcs phpunit phploc phpcpd phpsa php7ccCoupling Detector Mondrian PHP

Assumption PhpCodeAnalyzer PHPCodeFixer php7mar PHPSemantic Versioning Checker PHP Inspection PHPlint PHP Depend PhpMetrics PHPCheckstyle PHPReaper PHP vuln hunter Parse SonarQube Side

ChannelAnalyzer TaintPHP Deptrac PhpDependencyAnalysis PHP

semver checker

phpmd phan phpcs phpunit phploc phpcpd phpsa php7ccCoupling Detector Mondrian PHP

Assumption PhpCodeAnalyzer PHPCodeFixer php7mar PHPSemantic Versioning Checker PHP Inspection PHPlint PHP Depend PhpMetrics PHPCheckstyle PHPReaper PHP vuln hunter Parse SonarQube Side

ChannelAnalyzer TaintPHP Deptrac PhpDependencyAnalysis PHP

semver checker

phpmd phan phpcs phpunit phploc phpcpd phpsa PHPCoupling Detector Mondrian PHP

Assumption PhpCodeAnalyzer PHPCodeFixer php7mar PHPSemantic Versioning Checker PHP Inspection PHPlint PHP Depend PhpMetrics PHPCheckstyle PHPReaper PHP vuln hunter Parse SonarQube Side

ChannelAnalyzer TaintPHP Deptrac PhpDependencyAnalysis PHP

semver checker

PHP lintphp -l

Compiles PHP script with the actual PHP compilerIt's already installed on your computerCan be used to test compatibility with multiple PHPversions

<?phpnamespace Contentful\Log

use Psr\Http\Message\RequestInterface;use Psr\Http\Message\ResponseInterface;

class NullLogger implements LoggerInterface{ public function getTimer() { return new NullTimer; }

public function log($api, RequestInterface $request, StandardTimer $timer, ResponseInterface $response = null, \Exception $exception = null) { }}

PHP 7.0.3 | 10 parallel jobs.................................X...... 40/40 (100 %)

Checked 40 files in 0.5 secondsSyntax error found in 1 file

------------------------------------------------------------Parse error: src/Log/NullLogger.php:9 7| namespace Contentful\Log 8| > 9| use Psr\Http\Message\RequestInterface; 10| use Psr\Http\Message\ResponseInterface; 11|Unexpected 'use' (T_USE), expecting '{'

Make your life easierUse PHP-Parallel-Lint

phploc

Gather stats about your projectsGet an idea of the complexitySee long term trends

phploc 3.0.1 by Sebastian Bergmann.

Directories 6Files 40

Size Lines of Code (LOC) 5683 Comment Lines of Code (CLOC) 2562 (45.08%) Non-Comment Lines of Code (NCLOC) 3121 (54.92%) Logical Lines of Code (LLOC) 951 (16.73%) Classes 868 (91.27%) Average Class Length 21 Minimum Class Length 0 Maximum Class Length 190 Average Method Length 2 Minimum Method Length 0 Maximum Method Length 25 Functions 0 (0.00%) Average Function Length 0 Not in classes or functions 83 (8.73%)

Cyclomatic Complexity Average Complexity per LLOC 0.28 Average Complexity per Class 7.65 Minimum Class Complexity 1.00 Maximum Class Complexity 62.00 Average Complexity per Method 1.82 Minimum Method Complexity 1.00 Maximum Method Complexity 11.00

If you're getting serious about Cyclomatic Complexity,use phpmd.

Dependencies Global Accesses 0 Global Constants 0 (0.00%) Global Variables 0 (0.00%) Super-Global Variables 0 (0.00%) Attribute Accesses 536 Non-Static 535 (99.81%) Static 1 (0.19%) Method Calls 319 Non-Static 308 (96.55%) Static 11 (3.45%)

Structure Namespaces 7 Interfaces 3 Traits 0 Classes 37 Abstract Classes 3 (8.11%) Concrete Classes 34 (91.89%) Methods 272 Scope Non-Static Methods 272 (100.00%) Static Methods 0 (0.00%) Visibility Public Methods 221 (81.25%) Non-Public Methods 51 (18.75%) Functions 6 Named Functions 0 (0.00%) Anonymous Functions 6 (100.00%) Constants 2 Global Constants 0 (0.00%) Class Constants 2 (100.00%)

Deptrac

Software has layersThere should be rules about those layersRules are easily broken

1. Define the layers of your architecture2. Define what layers another layer can access3. Profit!!!

layers: - name: Controller collectors: - type: className regex: .*Controller.* - name: Entity collectors: - type: className regex: AstaRwth\\VorkursticketBundle\\Entity\\.*

ruleset: Controller: - Service - Entity - Form Service: - Repository Command: - Entity Entity: - Validator

How it worksParses all files in your codeStores which classes access which others classesChecks the graph for rule violations

deptrac is alpha, not production ready.please help us and report feedback / bugs.

Start to create an AstMap for 24 Files.........................AstMap created.start emitting dependencies "InheritanceDependencyEmitter"start emitting dependencies "BasicDependencyEmitter"end emitting dependenciesstart flatten dependenciesend flatten dependenciescollecting violations.formatting dependencies.[...]\Services\PdfOrder::5 must not depend on [...]\Entity\Vorkursticket (Service on Entity)[...]\Services\PdfOrder::23 must not depend on [...]\Entity\Vorkursticket (Service on Entity)

Found 2 Violations

phan

Type safety for PHPChecks docblocksSignature mismatchesUnused code

How it worksMakes 2 passes over the codebase1. Build a list of all classes, functions, methods, etc.2. Go trough each function and follow the type ofeach variable

/** * @param Locale|string|null $locale * * @return string */public function getDescription($locale = null){ $localeCode = $this->getLocaleFromInput($locale);

// This checks happens after the call to getLocaleFromInput to make sure // the Exception for invalid locales is still thrown. if ($this->description === null) { return null; }

return $this->description->$localeCode;}

src/Delivery/Asset.php:74 PhanTypeMismatchReturn Returning type nullbut getDescription() is declared to return string

class ContentType{ /** * The fields, keyed by ID. * * @var object */ private $fields = [];}

src/Delivery/ContentType.php:34 PhanTypeMismatchProperty Assigning array toproperty but \contentful\delivery\contenttype::fields is object

public function __call($name, $arguments){ // Lots of code here

if ($result instanceof Link) { return $client->resolveLink($result); }

return array_map(function ($value) use ($client) { if ($value instanceof Link) { return $client->resolveLink($value); }

return $value; }, $result);}

src/Delivery/DynamicEntry.php:126PhanTypeMismatchArgumentInternal Argument 2 (input1) is\contentful\delivery\link but \array_map() takes array

Not a bug

Not a bug

Don't trust blindly

Bad news?Requires php-astNot easy to deal with library codeNoisy - not easily integrated in CI.

The future

phan is using brute force for type checkingRoughly as good as the compiler for a staticallytyped languageWorks, but a Control Flow Graph could give evendeeper insight

int foo(int length) { int x = 0; for (int i = 0; i < length; i++) x += 1

return length/x;}

Bottom lineThere are dozens of tools - pick what's necessary foryouMake them part of your Continuous Integration setupNever trust. Make sure you understand where theerror is coming from.

Slides available on Slideshare:http://www.slideshare.net/rwessling

Please leave feedback on joind.in( ) or tweet me

( )https://joind.in/talk/8f19b

@RouvenWessling