Giter Site home page Giter Site logo

phan / phan Goto Github PK

View Code? Open in Web Editor NEW
5.5K 5.5K 359.0 42.51 MB

Phan is a static analyzer for PHP. Phan prefers to avoid false-positives and attempts to prove incorrectness rather than correctness.

Home Page: https://github.com/phan/phan/wiki

License: Other

PHP 99.46% Shell 0.32% Makefile 0.01% Dockerfile 0.03% Hack 0.01% Vim Script 0.18%
analysis analyzer phan php static-analysis static-code-analysis

phan's Introduction

Phan is a static analyzer for PHP that prefers to minimize false-positives. Phan attempts to prove incorrectness rather than correctness.

Phan looks for common issues and will verify type compatibility on various operations when type information is available or can be deduced. Phan has a good (but not comprehensive) understanding of flow control and can track values in a few use cases (e.g. arrays, integers, and strings).

Build Status Build Status (Windows) Gitter Latest Stable Version License

Getting Started

The easiest way to use Phan is via Composer.

composer require phan/phan

With Phan installed, you'll want to create a .phan/config.php file in your project to tell Phan how to analyze your source code. Once configured, you can run it via ./vendor/bin/phan.

Phan 5 depends on PHP 7.2+ with the php-ast extension (1.1.1+ is preferred) and supports analyzing PHP version 7.0-8.2 syntax. Installation instructions for php-ast can be found here. (Phan can be used without php-ast by using the CLI option --allow-polyfill-parser, but there are slight differences in the parsing of doc comments)

  • Alternative Installation Methods
    See Getting Started for alternative methods of using Phan and details on how to configure Phan for your project.
  • Incrementally Strengthening Analysis
    Take a look at Incrementally Strengthening Analysis for some tips on how to slowly ramp up the strictness of the analysis as your code becomes better equipped to be analyzed.
  • Installing Dependencies
    Take a look at Installing Phan Dependencies for help getting Phan's dependencies installed on your system.

The Wiki has more information about using Phan.

Features

Phan is able to perform the following kinds of analysis:

  • Check that all methods, functions, classes, traits, interfaces, constants, properties and variables are defined and accessible.
  • Check for type safety and arity issues on method/function/closure calls.
  • Check for PHP8/PHP7/PHP5 backward compatibility.
  • Check for features that weren't supported in older PHP 7.x minor releases (E.g. object, void, iterable, ?T, [$x] = ...;, negative string offsets, multiple exception catches, etc.)
  • Check for sanity with array accesses.
  • Check for type safety on binary operations.
  • Check for valid and type safe return values on methods, functions, and closures.
  • Check for No-Ops on arrays, closures, constants, properties, variables, unary operators, and binary operators.
  • Check for unused/dead/unreachable code. (Pass in --dead-code-detection)
  • Check for unused variables and parameters. (Pass in --unused-variable-detection)
  • Check for redundant or impossible conditions and pointless casts. (Pass in --redundant-condition-detection)
  • Check for unused use statements. These and a few other issue types can be automatically fixed with --automatic-fix.
  • Check for classes, functions and methods being redefined.
  • Check for sanity with class inheritance (e.g. checks method signature compatibility). Phan also checks for final classes/methods being overridden, that abstract methods are implemented, and that the implemented interface is really an interface (and so on).
  • Supports namespaces, traits and variadics.
  • Supports Union Types.
  • Supports Generic Types (i.e. @template).
  • Supports generic arrays such as int[], UserObject[], array<int,UserObject>, etc..
  • Supports array shapes such as array{key:string,otherKey:?stdClass}, etc. (internally and in PHPDoc tags) This also supports indicating that fields of an array shape are optional via array{requiredKey:string,optionalKey?:string} (useful for @param)
  • Supports phpdoc type annotations.
  • Supports inheriting phpdoc type annotations.
  • Supports checking that phpdoc type annotations are a narrowed form (E.g. subclasses/subtypes) of the real type signatures
  • Supports inferring types from assert() statements and conditionals in if elements/loops.
  • Supports @deprecated annotation for deprecating classes, methods and functions
  • Supports @internal annotation for elements (such as a constant, function, class, class constant, property or method) as internal to the package in which it's defined.
  • Supports @suppress <ISSUE_TYPE> annotations for suppressing issues.
  • Supports magic @property annotations (@property <union_type> <variable_name>)
  • Supports magic @method annotations (@method <union_type> <method_name>(<union_type> <param1_name>))
  • Supports class_alias annotations (experimental, off by default)
  • Supports indicating the class to which a closure will be bound, via @phan-closure-scope (example)
  • Supports analysis of closures and return types passed to array_map, array_filter, and other internal array functions.
  • Offers extensive configuration for weakening the analysis to make it useful on large sloppy code bases
  • Can be run on many cores. (requires pcntl)
  • Output is emitted in text, checkstyle, json, pylint, csv, or codeclimate formats.
  • Can run user plugins on source for checks specific to your code. Phan includes various plugins you may wish to enable for your project.

See Phan Issue Types for descriptions and examples of all issues that can be detected by Phan. Take a look at the \Phan\Issue to see the definition of each error type.

Take a look at the Tutorial for Analyzing a Large Sloppy Code Base to get a sense of what the process of doing ongoing analysis might look like for you.

Phan can be used from various editors and IDEs for its error checking, "go to definition" support, etc. via the Language Server Protocol. Editors and tools can also request analysis of individual files in a project using the simpler Daemon Mode.

See the tests directory for some examples of the various checks.

Phan is imperfect and shouldn't be used to prove that your PHP-based rocket guidance system is free of defects.

Features provided by plugins

Additional analysis features have been provided by plugins.

Example: Phan's plugins for self-analysis.

Usage

After installing Phan, Phan needs to be configured with details on where to find code to analyze and how to analyze it. The easiest way to tell Phan where to find source code is to create a .phan/config.php file. A simple .phan/config.php file might look something like the following.

<?php

/**
 * This configuration will be read and overlaid on top of the
 * default configuration. Command line arguments will be applied
 * after this file is read.
 */
return [

    // Supported values: `'5.6'`, `'7.0'`, `'7.1'`, `'7.2'`, `'7.3'`, `'7.4'`,
    // `'8.0'`, `'8.1'`, `'8.2'`, `'8.3'`, `null`.
    // If this is set to `null`,
    // then Phan assumes the PHP version which is closest to the minor version
    // of the php executable used to execute Phan.
    "target_php_version" => null,

    // A list of directories that should be parsed for class and
    // method information. After excluding the directories
    // defined in exclude_analysis_directory_list, the remaining
    // files will be statically analyzed for errors.
    //
    // Thus, both first-party and third-party code being used by
    // your application should be included in this list.
    'directory_list' => [
        'src',
        'vendor/symfony/console',
    ],

    // A directory list that defines files that will be excluded
    // from static analysis, but whose class and method
    // information should be included.
    //
    // Generally, you'll want to include the directories for
    // third-party code (such as "vendor/") in this list.
    //
    // n.b.: If you'd like to parse but not analyze 3rd
    //       party code, directories containing that code
    //       should be added to the `directory_list` as
    //       to `exclude_analysis_directory_list`.
    "exclude_analysis_directory_list" => [
        'vendor/'
    ],

    // A list of plugin files to execute.
    // Plugins which are bundled with Phan can be added here by providing their name
    // (e.g. 'AlwaysReturnPlugin')
    //
    // Documentation about available bundled plugins can be found
    // at https://github.com/phan/phan/tree/v5/.phan/plugins
    //
    // Alternately, you can pass in the full path to a PHP file
    // with the plugin's implementation.
    // (e.g. 'vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php')
    'plugins' => [
        // checks if a function, closure or method unconditionally returns.
        // can also be written as 'vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php'
        'AlwaysReturnPlugin',
        'DollarDollarPlugin',
        'DuplicateArrayKeyPlugin',
        'DuplicateExpressionPlugin',
        'PregRegexCheckerPlugin',
        'PrintfCheckerPlugin',
        'SleepCheckerPlugin',
        // Checks for syntactically unreachable statements in
        // the global scope or function bodies.
        'UnreachableCodePlugin',
        'UseReturnValuePlugin',
        'EmptyStatementListPlugin',
        'LoopVariableReusePlugin',
    ],
];

Take a look at Creating a Config File and Incrementally Strengthening Analysis for more details.

Running phan --help will show usage information and command-line options.

Annotating Your Source Code

Phan reads and understands most PHPDoc type annotations including Union Types (like int|MyClass|string|null) and generic array types (like int[] or string[]|MyClass[] or array<int,MyClass>).

Take a look at Annotating Your Source Code and About Union Types for some help getting started with defining types in your code.

Phan supports (int|string)[] style annotations, and represents them internally as int[]|string[] (Both annotations are treated like array which may have integers and/or strings). When you have arrays of mixed types, just use array.

The following code shows off the various annotations that are supported.

/**
 * @return void
 */
function f() {}

/** @deprecated */
class C {
    /** @var int */
    const C = 42;

    /** @var string[]|null */
    public $p = null;

    /**
     * @param int|null $p
     * @return string[]|null
     */
    public static function f($p) {
        if (is_null($p)) {
            return null;
        }

        return array_map(
            /** @param int $i */
            function($i) {
                return "thing $i";
            },
            range(0, $p)
        );
    }
}

Just like in PHP, any type can be nulled in the function declaration which also means a null is allowed to be passed in for that parameter.

Phan checks the type of every single element of arrays (Including keys and values). In practical terms, this means that [$int1=>$int2,$int3=>$int4,$int5=>$str6] is seen as array<int,int|string>, which Phan represents as array<int,int>|array<int,string>. [$strKey => new MyClass(), $strKey2 => $unknown] will be represented as array<string,MyClass>|array<string,mixed>.

  • Literals such as [12,'myString'] will be represented internally as array shapes such as array{0:12,1:'myString'}

Generating a file list

This static analyzer does not track includes or try to figure out autoloader magic. It treats all the files you throw at it as one big application. For code encapsulated in classes this works well. For code running in the global scope it gets a bit tricky because order matters. If you have an index.php including a file that sets a bunch of global variables and you then try to access those after the include(...) in index.php the static analyzer won't know anything about these.

In practical terms this simply means that you should put your entry points and any files setting things in the global scope at the top of your file list. If you have a config.php that sets global variables that everything else needs, then you should put that first in the list followed by your various entry points, then all your library files containing your classes.

Development

Take a look at Developer's Guide to Phan for help getting started hacking on Phan.

When you find an issue, please take the time to create a tiny reproducing code snippet that illustrates the bug. And once you have done that, fix it. Then turn your code snippet into a test and add it to tests then ./test and send a PR with your fix and test. Alternatively, you can open an Issue with details.

To run Phan's unit tests, just run ./test.

To run all of Phan's unit tests and integration tests, run ./tests/run_all_tests.sh

Code of Conduct

We are committed to fostering a welcoming community. Any participant and contributor is required to adhere to our Code of Conduct.

Online Demo

This requires an up to date version of Firefox/Chrome and at least 4 GB of free RAM. (this is a 15 MB download)

Run Phan entirely in your browser.

Preview of analyzing PHP

phan's People

Contributors

ablyler avatar adambaratz avatar akrabat avatar arichard4 avatar arjenschol avatar arm128 avatar artemgordinskiy avatar black-silence avatar bugreportuser avatar cweagans avatar daimona avatar dannys712 avatar dasl- avatar dereckson avatar ebernhardson avatar ericnorris avatar localheinz avatar maksimovic avatar morria avatar muglug avatar reedy avatar rlerdorf avatar romtin avatar scaytrase avatar seldaek avatar tpunt avatar trainmaster avatar tysonandre avatar tysonandre-tmg avatar zonuexe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

phan's Issues

Autocomplete functionality

There's literally no FOSS project that handles PHP autocomplete well (e.g. lexically scoped, contextually relevant suggestions, type inference, etc).

As a first pass at this, I think it would be reasonable to have a command line utility that gets run once with the file context, and then that would pull autocomplete suggestions from sqlite.

Longer term, running Phan as a daemon that watches for file changes and automatically reindexes as they change would be awesome. This would also allow for using an in-memory sqlite database, which could be a pretty nice thing for some projects. I thought about doing this for github.com/cweagans/theforce, but never got around to it.

Phan/Analyze/BreadthFirstVisitor.php:851 Undefined offset: 0

When I run phan on one of my codebases I get a lot of these in the output:

/usr/local/Cellar/phan/0.1/src/Phan/Analyze/BreadthFirstVisitor.php:851 Undefined offset: 0
#0  Phan\Log::errorHandler() called at [/usr/local/Cellar/phan/0.1/src/Phan/Analyze/BreadthFirstVisitor.php:851]
#1  Phan\Analyze\BreadthFirstVisitor->visitDim() called at [/usr/local/Cellar/phan/0.1/src/Phan/Language/AST/Element.php:72]
#2  Phan\Language\AST\Element->acceptKindVisitor() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:399]
#3  Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#4  Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#5  Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#6  Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#7  Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#8  Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#9  Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#10 Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#11 Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#12 Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#13 Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#14 Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Analyze/Analyzable.php:89]
#15 Phan\Language\Element\Method->analyze() called at [/usr/local/Cellar/phan/0.1/src/Phan/Analyze/BreadthFirstVisitor.php:1114]
#16 Phan\Analyze\BreadthFirstVisitor->analyzeCallToMethod() called at [/usr/local/Cellar/phan/0.1/src/Phan/Analyze/BreadthFirstVisitor.php:577]
#17 Phan\Analyze\BreadthFirstVisitor->visitCall() called at [/usr/local/Cellar/phan/0.1/src/Phan/Language/AST/Element.php:44]
#18 Phan\Language\AST\Element->acceptKindVisitor() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:399]
#19 Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#20 Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#21 Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#22 Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#23 Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#24 Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#25 Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#26 Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#27 Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Analyze/Analyzable.php:89]
#28 Phan\Language\Element\Method->analyze() called at [/usr/local/Cellar/phan/0.1/src/Phan/Analyze/BreadthFirstVisitor.php:1114]
#29 Phan\Analyze\BreadthFirstVisitor->analyzeCallToMethod() called at [/usr/local/Cellar/phan/0.1/src/Phan/Analyze/BreadthFirstVisitor.php:577]
#30 Phan\Analyze\BreadthFirstVisitor->visitCall() called at [/usr/local/Cellar/phan/0.1/src/Phan/Language/AST/Element.php:44]
#31 Phan\Language\AST\Element->acceptKindVisitor() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:399]
#32 Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#33 Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389]
#34 Phan\Phan->analyzeNodeInContext() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:330]
#35 Phan\Phan->analyzeFile() called at [/usr/local/Cellar/phan/0.1/src/Phan/Phan.php:111]
#36 Phan\Phan->analyzeFileList() called at [/usr/local/Cellar/phan/0.1/phan:34]

Invalid ParamError for ArrayObject

I'm pretty sure ArrayObject takes up to 3 arguments.

$test3 = new ArrayObject($config, ArrayObject::ARRAY_AS_PROPS, 'ArrayIterator');
// ParamError call with 3 arg(s) to \arrayobject::__construct() which only takes 1 arg(s)

$test2 = new ArrayObject($config, ArrayObject::ARRAY_AS_PROPS);
// ParamError call with 2 arg(s) to \arrayobject::__construct() which only takes 1 arg(s)

$test1 = new ArrayObject($config);
// No error

Using phar package on windows results to errors for CamelCased file names

i.e. Running on symfony-based project (Analyzing is OK as I pass only single kernel file):

> C:\Work\Tools\PHP7\php.exe C:/work/tools/phan/build/phan.phar app/AppKernel.php

Warning: filemtime(): stat failed for app/AppKernel.php in phar://C:/Work/Tools/phan/build/phan.phar/src/Phan/CodeBase/File.php on line 154

Call Stack:
    0.0132    1030456   1. {main}() C:\Work\Tools\phan\build\phan.phar:0
    0.0134    1028080   2. include('phar://C:/Work/Tools/phan/build/phan.phar/phan.php') C:\Work\Tools\phan\build\phan.phar:9
    1.3994   22145152   3. Phan\Phan->analyzeFileList() phar://C:/Work/Tools/phan/build/phan.phar/phan.php:36
    1.4065   22809184   4. Phan\CodeBase->setParseUpToDateForFile() phar://C:/Work/Tools/phan/build/phan.phar/src/Phan/Phan.php:78
    1.4065   22809184   5. Phan\CodeBase\File->setParseUpToDate() phar://C:/Work/Tools/phan/build/phan.phar/src/Phan/CodeBase/FileMap.php:146
    1.4065   22809184   6. filemtime() phar://C:/Work/Tools/phan/build/phan.phar/src/Phan/CodeBase/File.php:154

app/AppKernel.php:6 UndefError Trying to inherit from unknown class \Symfony\Component\HttpKernel\kernel
app/AppKernel.php:40 UndefError parameter of undeclared type \Symfony\Component\Config\Loader\loaderinterface
app/AppKernel.php:11 UndefError reference to undeclared class \symfony\bundle\frameworkbundle\frameworkbundle
app/AppKernel.php:12 UndefError reference to undeclared class \symfony\bundle\securitybundle\securitybundle
app/AppKernel.php:13 UndefError reference to undeclared class \symfony\bundle\twigbundle\twigbundle
app/AppKernel.php:14 UndefError reference to undeclared class \symfony\bundle\monologbundle\monologbundle
app/AppKernel.php:15 UndefError reference to undeclared class \symfony\bundle\swiftmailerbundle\swiftmailerbundle
app/AppKernel.php:16 UndefError reference to undeclared class \symfony\bundle\asseticbundle\asseticbundle
app/AppKernel.php:17 UndefError reference to undeclared class \doctrine\bundle\doctrinebundle\doctrinebundle
app/AppKernel.php:18 UndefError reference to undeclared class \doctrine\bundle\fixturesbundle\doctrinefixturesbundle
app/AppKernel.php:19 UndefError reference to undeclared class \doctrine\bundle\migrationsbundle\doctrinemigrationsbundle
app/AppKernel.php:20 UndefError reference to undeclared class \sensio\bundle\frameworkextrabundle\sensioframeworkextrabundle
app/AppKernel.php:30 UndefError call to undeclared method \appkernel->getEnvironment()
app/AppKernel.php:31 UndefError reference to undeclared class \symfony\bundle\debugbundle\debugbundle
app/AppKernel.php:32 UndefError reference to undeclared class \symfony\bundle\webprofilerbundle\webprofilerbundle
app/AppKernel.php:33 UndefError reference to undeclared class \sensio\bundle\distributionbundle\sensiodistributionbundle
app/AppKernel.php:34 UndefError reference to undeclared class \sensio\bundle\generatorbundle\sensiogeneratorbundle
app/AppKernel.php:42 UndefError call to undeclared method \appkernel->getEnvironment()
PHP Warning:  filemtime(): stat failed for app/AppKernel.php in phar://C:/Work/Tools/phan/build/phan.phar/src/Phan/CodeBase/File.php on line 154
PHP Stack trace:
PHP   1. {main}() C:\Work\Tools\phan\build\phan.phar:0
PHP   2. include() C:\Work\Tools\phan\build\phan.phar:9
PHP   3. Phan\Phan->analyzeFileList() phar://C:/Work/Tools/phan/build/phan.phar/phan.php:36
PHP   4. Phan\CodeBase->setParseUpToDateForFile() phar://C:/Work/Tools/phan/build/phan.phar/src/Phan/Phan.php:78
PHP   5. Phan\CodeBase\File->setParseUpToDate() phar://C:/Work/Tools/phan/build/phan.phar/src/Phan/CodeBase/FileMap.php:146
PHP   6. filemtime() phar://C:/Work/Tools/phan/build/phan.phar/src/Phan/CodeBase/File.php:154

Running on lowercase filenames i.e app/sample.php works OK, but app/SamPle.php fails.

Phan doesn't play nice with XDebug enabled

When scanning symfony-based project (all files, but vendor exluded with -3 option) I'm getting recursion limit exception.

On 300 levels I got:

   96.5956  768976816 294. Phan\Language\AST::backwardCompatibilityCheck() C:\Work\Tools\phan\src\Phan\Language\AST.php:306
   96.5956  768976816 295. Phan\Language\AST::backwardCompatibilityCheck() C:\Work\Tools\phan\src\Phan\Language\AST.php:306
   96.5956  768976816 296. Phan\Language\AST::backwardCompatibilityCheck() C:\Work\Tools\phan\src\Phan\Language\AST.php:306
   96.5956  768976816 297. Phan\Language\AST::backwardCompatibilityCheck() C:\Work\Tools\phan\src\Phan\Language\AST.php:306
   96.5956  768976816 298. Phan\Language\AST::backwardCompatibilityCheck() C:\Work\Tools\phan\src\Phan\Language\AST.php:306
   96.5956  768976816 299. Phan\Language\AST::backwardCompatibilityCheck() C:\Work\Tools\phan\src\Phan\Language\AST.php:306
   96.5956  768976816 300. Phan\Language\AST::backwardCompatibilityCheck() C:\Work\Tools\phan\src\Phan\Language\AST.php:306

on 1000 levels deep I got:

PHP  79. Phan\Phan->analyzeNodeInContext() C:\Work\Tools\phan\src\Phan\Phan.php:395
PHP  80. Phan\Phan->analyzeNodeInContext() C:\Work\Tools\phan\src\Phan\Phan.php:395
PHP  81. Phan\Phan->analyzeNodeInContext() C:\Work\Tools\phan\src\Phan\Phan.php:395
PHP  82. Phan\Phan->analyzeNodeInContext() C:\Work\Tools\phan\src\Phan\Phan.php:395
PHP  83. Phan\Phan->analyzeNodeInContext() C:\Work\Tools\phan\src\Phan\Phan.php:395
PHP  84. Phan\Language\AST\Element->acceptKindVisitor() C:\Work\Tools\phan\src\Phan\Phan.php:405
PHP  85. Phan\Analyze\BreadthFirstVisitor->visitMethodCall() C:\Work\Tools\phan\src\Phan\Language\AST\Element.php:113
PHP  86. Phan\Language\AST::classMethodFromNodeInContext() C:\Work\Tools\phan\src\Phan\Analyze\BreadthFirstVisitor.php:814
PHP  87. Phan\Language\AST::classFromNodeInContext() C:\Work\Tools\phan\src\Phan\Language\AST.php:455
PHP  88. Phan\Language\AST::classNameFromNode() C:\Work\Tools\phan\src\Phan\Language\AST.php:392
PHP  89. Phan\Language\AST\Element->acceptKindVisitor() C:\Work\Tools\phan\src\Phan\Language\AST.php:103
PHP  90. Phan\Analyze\ClassNameVisitor->visitMethodCall() C:\Work\Tools\phan\src\Phan\Language\AST\Element.php:113
PHP  91. Phan\Language\AST\Element->acceptKindVisitor() C:\Work\Tools\phan\src\Phan\Analyze\ClassNameVisitor.php:179
PHP  92. Phan\Analyze\ClassName\MethodCallVisitor->visitMethodCall() C:\Work\Tools\phan\src\Phan\Language\AST\Element.php:113

And recusing in

  360.4971 1576100672 972. Phan\Analyze\ClassName\MethodCallVisitor->visitMethodCall() C:\Work\Tools\phan\src\Phan\Language\AST\Element.php:113
  360.4971 1576100672 973. Phan\Language\UnionType::fromNode() C:\Work\Tools\phan\src\Phan\Analyze\ClassName\MethodCallVisitor.php:247
  360.4971 1576100808 974. Phan\Language\AST\Element->acceptKindVisitor() C:\Work\Tools\phan\src\Phan\Language\UnionType.php:156
  360.4971 1576100808 975. Phan\Analyze\UnionTypeVisitor->visitMethodCall() C:\Work\Tools\phan\src\Phan\Language\AST\Element.php:113
  360.4971 1576100808 976. Phan\Language\AST::classNameFromNode() C:\Work\Tools\phan\src\Phan\Analyze\UnionTypeVisitor.php:824
  360.4971 1576100944 977. Phan\Language\AST\Element->acceptKindVisitor() C:\Work\Tools\phan\src\Phan\Language\AST.php:103
  360.4971 1576100944 978. Phan\Analyze\ClassNameVisitor->visitMethodCall() C:\Work\Tools\phan\src\Phan\Language\AST\Element.php:113
  360.4971 1576101080 979. Phan\Language\AST\Element->acceptKindVisitor() C:\Work\Tools\phan\src\Phan\Analyze\ClassNameVisitor.php:179
  360.4971 1576101080 980. Phan\Analyze\ClassName\MethodCallVisitor->visitMethodCall() C:\Work\Tools\phan\src\Phan\Language\AST\Element.php:113
  360.4971 1576101080 981. Phan\Language\UnionType::fromNode() C:\Work\Tools\phan\src\Phan\Analyze\ClassName\MethodCallVisitor.php:247
  360.4971 1576101216 982. Phan\Language\AST\Element->acceptKindVisitor() C:\Work\Tools\phan\src\Phan\Language\UnionType.php:156
  360.4971 1576101216 983. Phan\Analyze\UnionTypeVisitor->visitMethodCall() C:\Work\Tools\phan\src\Phan\Language\AST\Element.php:113
  360.4971 1576101216 984. Phan\Language\AST::classNameFromNode() C:\Work\Tools\phan\src\Phan\Analyze\UnionTypeVisitor.php:824
  360.4971 1576101352 985. Phan\Language\AST\Element->acceptKindVisitor() C:\Work\Tools\phan\src\Phan\Language\AST.php:103
  360.4971 1576101352 986. Phan\Analyze\ClassNameVisitor->visitMethodCall() C:\Work\Tools\phan\src\Phan\Language\AST\Element.php:113
  360.4971 1576101488 987. Phan\Language\AST\Element->acceptKindVisitor() C:\Work\Tools\phan\src\Phan\Analyze\ClassNameVisitor.php:179
  360.4971 1576101488 988. Phan\Analyze\ClassName\MethodCallVisitor->visitMethodCall() C:\Work\Tools\phan\src\Phan\Language\AST\Element.php:113
  360.4971 1576101488 989. Phan\Language\UnionType::fromNode() C:\Work\Tools\phan\src\Phan\Analyze\ClassName\MethodCallVisitor.php:247
  360.4971 1576101624 990. Phan\Language\AST\Element->acceptKindVisitor() C:\Work\Tools\phan\src\Phan\Language\UnionType.php:156
  360.4971 1576101624 991. Phan\Analyze\UnionTypeVisitor->visitMethodCall() C:\Work\Tools\phan\src\Phan\Language\AST\Element.php:113
  360.4971 1576101624 992. Phan\Language\AST::classNameFromNode() C:\Work\Tools\phan\src\Phan\Analyze\UnionTypeVisitor.php:824
  360.4971 1576101760 993. Phan\Language\AST\Element->acceptKindVisitor() C:\Work\Tools\phan\src\Phan\Language\AST.php:103
  360.4971 1576101760 994. Phan\Analyze\ClassNameVisitor->visitMethodCall() C:\Work\Tools\phan\src\Phan\Language\AST\Element.php:113
  360.4972 1576101896 995. Phan\Language\AST\Element->acceptKindVisitor() C:\Work\Tools\phan\src\Phan\Analyze\ClassNameVisitor.php:179
  360.4972 1576101896 996. Phan\Analyze\ClassName\MethodCallVisitor->visitVar() C:\Work\Tools\phan\src\Phan\Language\AST\Element.php:163
  360.4972 1576101896 997. Phan\Language\UnionType->nonGenericArrayTypes() C:\Work\Tools\phan\src\Phan\Analyze\ClassName\MethodCallVisitor.php:122
  360.4972 1576102408 998. array_filter() C:\Work\Tools\phan\src\Phan\Language\UnionType.php:532
  360.4972 1576102464 999. Phan\Language\UnionType->Phan\Language\{closure}() C:\Work\Tools\phan\src\Phan\Language\UnionType.php:532
  360.4972 1576102464 1000. Phan\Language\Type->isGenericArray() C:\Work\Tools\phan\src\Phan\Language\UnionType.php:531

Replace custom CLI stuff with symfony/console or league/climate

This reduces the amount of custom argument parsing code that has to be maintained, and allows for customizable output formats, which is useful for integration with other tools. This would be helpful to do before autocomplete, but not strictly necessary, I guess.

Add to union type for dim variables born as parameters

The following emits an error saying that $r['a'] is a string because we inferred that $r is string[] based on the $r['b'] = 's' assignment. For assignments with dims to variables that are born as parameters we should be adding to their union type instead of setting their types outright.

<?php
function f(array $r) {
    $r['b'] = 's';
    foreach ($r['a'] as $i) {
        print $i;
    }
}
f(['a' => [1]]);

Running phan on src/Monolog/Formatter/LogstashFormatter.php from Monolog is a real-world example.

Plan ahead for and adjust terminology for inclusion of "real" generics

I apologize in advance if this issue is too non-specific - I'm trying to open a discussion, more than pointing out one specific issue, and I don't know that there's a better place to do that, so here goes...

It seems there's a bit of terminology mix-up - for example here - it doesn't return the type "as a non-generic type", it returns the element type, which conceptually is only applicable to generic arrays, not to generics at large.

I know that PHP doesn't support generics, but the type-system does support generic type-relationships, and they can be described using generic notation - it's just that nobody is doing that, yet.

I am hoping to eventually see support for generics in php-doc, which would enable static analysis for generic type-declarations in php-doc tags.

I'd like to eventually fork this project and try to add generics, if there is interest?

And if so, it would be great if you would think ahead to generics becoming "a thing", which, apparently, you're already doing to some extent? The class name GenericArrayType (as opposed to e.g. GenericType) seems to suggest you may have given this some thought already?

Although there is no intermediary GenericType between GenericArrayType and GenericType - which, on one hand, might be taking it a bit far at this point (unless you're already planning to support generics anyhow) and, on the other hand, might help conceptually planning ahead and segregating things that are general to generic types (such as the list of type-arguments) from things that are specific to generic arrays (such as the element type).

In terms of the generic array type, you also might want to think about the fact that T[] is actually synonymous with array<T>, and that the generic array type actually has two forms, e.g. array<I,T> which specifies both the index type and the element type, and that array<T> actually implies array<int|string,T> - of course, you don't strictly need the index type at this point, but it might help to already model it that way, for example, it a foreach ($a as $i => $v) statement, where you could already infer the type of $i based on the index-type, if it was already modeled that; and this would lead up nicely to generics later on.

On a related noted, I hope that you don't decide to reference Hack for generics, because, while they're doing a lot of things right, they've got certain things, like inference, completely backwards - if you were to base static type-checking on that, generics would be a lot less generally useful than it could have been. If I end up working on this feature, I will be referencing languages like C#, Dart, Typescript for generics, which is more or less consistent across those languages, extremely powerful, and probably more like what most people would expect from generics than what Hack delivers...

Strange error when catching core exceptions

I am seeing a bunch of errors like src/Composer/Installer.php:387 UndefError call to method on undeclared class RuntimeException but if you look at the sources it is really odd. First of all it uses \RuntimeException which is obviously declared and then it also errors out on the line of the catch where no method is called and the error has no method name. Many oddities, but I could not find a minimal repro case unfortunately :/

Option to filter which files are analyzed

I am not sure if I am misusing it but right now in an attempt to avoid undefined classes and so on in my lib I list my dependencies as well, so I run phan like this:

find src/ vendor/composer/ vendor/justinrainbow/ vendor/seld/ vendor/symfony/ -name '*.php' | grep -i -v /tests/ > files.lst
phan -f files.lst > phan.log
rm files.lst

As I include dpeendencies, they get analyzed as well but I'd like to only analyze my files (or at least only get output for mine) while still giving phan awareness of my dependencies.

Something like phan -f files.lst --only-dir src/ that would restrict which files matter to me.

Exception on analyzing project

I've disabled xdebug (#58) and got the following exception

> C:\Work\Tools\PHP7\php.exe C:/work/tools/phan/phan -f files.txt -3 vendor/

Fatal error: Uncaught TypeError: Argument 2 passed to Phan\Language\Element\Clazz::hasPropertyWithName() must be of the type string, object given, called in C:\Work\Tools\phan\src\Phan\Analyze\ClassName\ValidationVisitor.php on line 171 and defined in C:\Work\Tools\phan\src\Phan\Language\Element\Clazz.php:304
Stack trace:
#0 C:\Work\Tools\phan\src\Phan\Analyze\ClassName\ValidationVisitor.php(171): Phan\Language\Element\Clazz->hasPropertyWithName(Object(Phan\CodeBase), Object(ast\Node))
#1 C:\Work\Tools\phan\src\Phan\Language\AST\Element.php(141): Phan\Analyze\ClassName\ValidationVisitor->visitStaticProp(Object(ast\Node))
#2 C:\Work\Tools\phan\src\Phan\Language\AST.php(116): Phan\Language\AST\Element->acceptKindVisitor(Object(Phan\Analyze\ClassName\ValidationVisitor))
#3 C:\Work\Tools\phan\src\Phan\Analyze\UnionTypeVisitor.php(613): Phan\Language\AST::classNameFromNode(Object(Phan\Language\Context), Object(Phan\CodeBase), Object(ast\Node))
#4 C:\Work\Tools\phan\src\Phan\Language\AST\Element.php(141): Phan\Analyze\UnionType in C:\Work\Tools\phan\src\Phan\Language\Element\Clazz.php on line 304

Invalid VarError with usage of references

I got a minimal repro case for this:

<?php

class Test {
    protected $capture;

    function foo($command, &$bar = null) {
        $this->capture = count(func_get_args()) > 1;
        if ($this->capture && !is_callable($bar)) {
            $bar = 'bar';
        }
    }

    function foo2(&$bar) {}
}

(new Test)->foo('cmd', $undef);
var_dump($undef);

(new Test)->foo2($undef2);
var_dump($undef2);

Phan outputs:

test.php:17 VarError Variable $undef is not defined
test.php:20 VarError Variable $undef2 is not defined

However, php executes this just fine and outputs 'bar' & NULL. Passing the variables by reference defines them even if they are not assigned by the callee. get_defined_vars() also confirms that.

Provide an example of usage on real project

Given a single library, say... Symfony Console component, how phan should be used to perform code analysis correctly?

Analysing a single or a plain list of files is quiet easy to understand but when a huge project with autoloader managed by things like compose - it is hard to manually prepare file list with correct analys order

Tag a release in GitHub

I'd like to create a package for your code in homebrew-php, but need versions to be able to do so. Would you consider starting to create releases in GitHub?

Class property type check is missing

<?php
class Test {
    /** @var Client */
    public $client;

    function fn(Client $client) {
        $this->client = $client;
        $this->client->test();
    }
}

Old phan produced:

test.php:8 UndefError call to method on undeclared class client

New phan is silent

Distinguish null and void return types

Phan does not currently distinguish between null and void return types.

/** @return null */
function f() { return; }

/** @return void */
function g() { return null; }

should emit two errors.

Too many errors emitted for a constant on an undeclared class.

Running phan on

<?php
$x = UndefinedClass::CONSTANT;

results in two errors for the single issue;

%s:2 UndefError call to undeclared class \undefinedclass
%s:2 UndefError Can't access constant CONSTANT from undeclared class UndefinedClass

Only the constant issue should be emitted.

Invalid VarError with $http_response_header

I gave it a shot on the composer code base and a few things came up that don't seem like valid errors:

VarError Variable $http_response_header is not defined => It is a horrible interface, but calling file_get_contents has the side-effect of populating $http_response_header in the calling scope. I have no idea how you can work that in, but it sure would be nice. Alternatively I suppose we could declare it before calling file_get_contents but I don't really like adding dummy code for tooling to work.

Uncaught TypeError

This short snippet crashes phan:

<?php
function do_something($var, callable $callback = NULL) {
    if ($callback) {
        $callback(3);
    }
}

do_something(3, function($callvar) {
        var_dump($callvar);
});
?>

output:

PHP Fatal error:  Uncaught TypeError: Argument 1 passed to Phan\CodeBase::hasMethodWithFunctionFQSEN() must be an instance of Phan\Language\FQSEN\FullyQualifiedFunctionName, instance of Phan\Language\FQSEN\FullyQualifiedClassName given, called in /usr/local/Cellar/phan/0.1/src/Phan/CodeBase/MethodMap.php on line 68 and defined in /usr/local/Cellar/phan/0.1/src/Phan/CodeBase/MethodMap.php:91
Stack trace:
#0 /usr/local/Cellar/phan/0.1/src/Phan/CodeBase/MethodMap.php(68): Phan\CodeBase->hasMethodWithFunctionFQSEN(Object(Phan\Language\FQSEN\FullyQualifiedClassName))
#1 /usr/local/Cellar/phan/0.1/src/Phan/Analyze/BreadthFirstVisitor.php(613): Phan\CodeBase->hasMethod(Object(Phan\Language\FQSEN\FullyQualifiedClassName))
#2 /usr/local/Cellar/phan/0.1/src/Phan/Language/AST/Element.php(44): Phan\Analyze\BreadthFirstVisitor->visitCall(Object(ast\Node))
#3 /usr/local/Cellar/phan/0.1/src/Phan/Phan.php(399): Phan\Language\AST\Element->acceptKindVisitor(Object(Phan\Analyze\BreadthFirstVisitor))
#4 /usr/local/Cellar/phan/0.1/src/Phan/Phan.ph in /usr/local/Cellar/phan/0.1/src/Phan/CodeBase/MethodMap.php on line 91

Fatal error: Uncaught TypeError: Argument 1 passed to Phan\CodeBase::hasMethodWithFunctionFQSEN() must be an instance of Phan\Language\FQSEN\FullyQualifiedFunctionName, instance of Phan\Language\FQSEN\FullyQualifiedClassName given, called in /usr/local/Cellar/phan/0.1/src/Phan/CodeBase/MethodMap.php on line 68 and defined in /usr/local/Cellar/phan/0.1/src/Phan/CodeBase/MethodMap.php on line 91

TypeError: Argument 1 passed to Phan\CodeBase::hasMethodWithFunctionFQSEN() must be an instance of Phan\Language\FQSEN\FullyQualifiedFunctionName, instance of Phan\Language\FQSEN\FullyQualifiedClassName given, called in /usr/local/Cellar/phan/0.1/src/Phan/CodeBase/MethodMap.php on line 68 in /usr/local/Cellar/phan/0.1/src/Phan/CodeBase/MethodMap.php on line 91

Call Stack:
    0.0002     359336   1. {main}() /usr/local/Cellar/phan/0.1/phan:0
    1.1293   18516832   2. Phan\Phan->analyzeFileList() /usr/local/Cellar/phan/0.1/phan:34
    1.3509   19090448   3. Phan\Phan->analyzeFile() /usr/local/Cellar/phan/0.1/src/Phan/Phan.php:111
    1.3511   19104144   4. Phan\Phan->analyzeNodeInContext() /usr/local/Cellar/phan/0.1/src/Phan/Phan.php:330
    1.3516   19164416   5. Phan\Phan->analyzeNodeInContext() /usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389
    1.3526   19279952   6. Phan\Phan->analyzeNodeInContext() /usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389
    1.3526   19279952   7. Phan\Phan->analyzeNodeInContext() /usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389
    1.3527   19279952   8. Phan\Phan->analyzeNodeInContext() /usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389
    1.3527   19279952   9. Phan\Phan->analyzeNodeInContext() /usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389
    1.3527   19279952  10. Phan\Phan->analyzeNodeInContext() /usr/local/Cellar/phan/0.1/src/Phan/Phan.php:389
    1.3528   19280104  11. Phan\Language\AST\Element->acceptKindVisitor() /usr/local/Cellar/phan/0.1/src/Phan/Phan.php:399
    1.3528   19280104  12. Phan\Analyze\BreadthFirstVisitor->visitCall() /usr/local/Cellar/phan/0.1/src/Phan/Language/AST/Element.php:44
    1.3529   19280312  13. Phan\CodeBase->hasMethod() /usr/local/Cellar/phan/0.1/src/Phan/Analyze/BreadthFirstVisitor.php:613
    1.3529   19280312  14. Phan\CodeBase->hasMethodWithFunctionFQSEN() /usr/local/Cellar/phan/0.1/src/Phan/CodeBase/MethodMap.php:68

Class-names displaying in lower-case?

Example:

$ /C/workspace/test/etsy-phan/phan *.php Facets/*.php
Field.php:89 UndefError reference to undeclared class \Kodus\Forms\Validators\isrequired
InputRenderer.php:558 TypeError arg#2(attr) is float|int but \Kodus\Forms\inputrenderer::tag() takes array defined at InputRenderer.php:241

These class-names are actually IsRequired and InputRenderer, how come the case is being folded to lower-case in these error-messages?

(Neither filenames, nor declarations, nor usages refer to these in lower-case.)

Add refactoring support

This is a bit more involved, but since Phan builds up a database of information about a given codebase, eventually, it should be possible to add refactoring support similar to PhpStorm. Could be really cool to eventually reach feature parity with PhpStorm and other large Java IDEs.

Problems with -3 option

Given a large project. I cannot reproduce with surrogate set, so check the https://github.com/symfony/symfony-standard

do

cd symfony-standard
find -type f -name *.php > files.txt
php path-to-phan/phan -3 vendor -f files.txt -o phan.txt

In my case

C:\Work\Projects\symfony-standard>php C:\Work\Tools\phan\phan -3 vendor/ -f files.txt -o phan.txt

Output consist only of vendor analysis

files.txt
phan.txt

TypeError arg# on \string when passing objects implementing __toString() cast function

<?php

class Stringular
{
    public function __toString()
    {
        return 'test string';
    }
}

function test(\string $test)
{
    echo $test;
}

test(new Stringular());
C:\Work\Tools\PHP7\php.exe C:\Work\Tools\phan\sample.php
test string
> C:\Work\Tools\PHP7\php.exe phan sample.php
sample.php:16 TypeError arg#1(test) is \stringular but \test() takes \string defined at sample.php:11

Nullable typehinted parameters raise error if not the last mandatory parameter

Allowing a type-hinted to be null uses the same syntax as making a parameter optional, which confuses phan into reporting an error if followed by non-optional parameters:

/**
 * @param DateTime|null $d
 * @param int $i
 */
function foo(DateTime $d = null, int $i){}
//ParamError required arg follows optional

In the case of a type-hinted parameter as above, this error should probably be suppressed.

Types on overloaded global functions

The code

<?php
namespace A;

function strlen(string $a) : string {
    return "Hello, World";
}

function f(string $s) : string { return $s; }

print f(strlen("string"));

emits

test.php:10 TypeError arg#1(s) is int but \A\f() takes string defined at test.php:8

but shouldn't given that PHP will choose functions in the local namespace before the global function.

Make sqlite database creation more robust

We should catch the exception and print the error message to help the user debug. I just got this error for example which isn't very helpful:

./phan -d . -r -s foo src/**/*.php

Fatal error: Uncaught Exception: Unable to open database: unable to open database file in /home/dmiller/development/phan/src/Phan/Database.php:17
Stack trace:
#0 /home/dmiller/development/phan/src/Phan/Database.php(17): SQLite3->__construct('foo', 6)
#1 /home/dmiller/development/phan/src/Phan/Database.php(42): Phan\Database->__construct()
#2 /home/dmiller/development/phan/src/Phan/CodeBase/MethodMap.php(120): Phan\Database::get()
#3 /home/dmiller/development/phan/src/Phan/CodeBase/MethodMap.php(82): Phan\CodeBase->hasMethodWithScopeAndName('\\exception', '__clone')
#4 /home/dmiller/development/phan/src/Phan/CodeBase/MethodMap.php(66): Phan\CodeBase->hasMethodWithMethodFQSEN(Object(Phan\Language\FQSEN\FullyQualifiedMethodName))
#5 /home/dmiller/development/phan/src/Phan/Language/Element/Clazz.php(453): Phan\CodeBase->hasMethod(Object(Phan\Language\FQSEN\FullyQualifiedMethodName))
#6 /home/dmiller/development/phan/src/Phan/Language/Element/Clazz.php(224): Phan\Language\Element\Clazz->addMethod(Object(Phan\CodeBase), Object( in /home/dmiller/development/phan/src/Phan/Database.php on line 17

Invalid UndefError after an instanceof check

Here is a repro case:

<?php

interface A {
}

interface B {
    function interesting();
}

function foo(A $a) {
    if ($a instanceof B) {
        $a->interesting();
    }
}

Phan outputs test.php:12 UndefError call to undeclared method \a->interesting() which is not true as within the if block we know that $a is of type \a + \b.

Exception when assigning inside an array with value extracted from xpath

Using phan from git hash 4521bad

Example trimmed down from: https://github.com/wikimedia/mediawiki/blob/master/includes/parser/Preprocessor_DOM.php

This seems to be the minimum viable bug, but perhaps i've missed something. chaining calls instead of assigning $value makes the bug go away. Assigning to a variable instead of an array key also makes it go away.

sample code:

<?php
$xpath = new DOMXPath();
$value = $xpath->query('foo');
$bar['baz'] = $value->item(0);

Exception thrown:

Fatal error: Uncaught TypeError: Argument 1 passed to phan\mkgenerics() must be of the type string, null given, called in /home/ebernhardson/git/phan/includes/pass2.php on line 647 and defined in /home/ebernhardson/git/phan/includes/pass2.php:1274
Stack trace:
#0 /home/ebernhardson/git/phan/includes/pass2.php(647): phan\mkgenerics(NULL)
#1 /home/ebernhardson/git/phan/includes/pass2.php(199): phan\var_assign('bug.php', '', Object(ast\Node), 'global', NULL, Array)
#2 /home/ebernhardson/git/phan/includes/pass2.php(183): phan\pass2('bug.php', '', Object(ast\Node), 'global', Object(ast\Node), NULL, NULL, NULL)
#3 /home/ebernhardson/git/phan/phan(66): phan\pass2('bug.php', '', Object(ast\Node), 'global')
#4 {main}

Progress bar output should go on stderr

If you do something like ./phan -p -f files.lst > phan.out the progress bar output goes into phan.out which is definitely not what is intended :)

Having it on stderr would make more sense and make phan easier to use.

Add flag to dump variable types.

Output should look like

Monolog\Formatter\LogstashFormatter::__construct
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
 Variables:
    applicationName: string (param: 1)
    systemName: string|null (param: 2)
    extraPrefix: string|null (param: 3)
    contextPrefix: string (param: 4)
    version: integer|int (param: 5)
    this: Monolog\Formatter\LogstashFormatter

Monolog\Formatter\LogstashFormatter::format
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
 Variables:
    record: array|mixed (param: 1)
    this: Monolog\Formatter\LogstashFormatter
    message: array|string[]|mixed

Monolog\Formatter\LogstashFormatter::formatV0
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
 Variables:
    record: array|mixed|string[] (param: 1)
    this: Monolog\Formatter\LogstashFormatter
    message: array|string[]|mixed
    val: 
    key: 

Monolog\Formatter\LogstashFormatter::formatV1
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
 Variables:
    record: array|mixed|string[] (param: 1)
    this: Monolog\Formatter\LogstashFormatter
    message: array|string[]|mixed
    val: 
    key: 

global
¯¯¯¯¯¯
 Variables:
    _GET: array(tainted)
    _POST: array(tainted)
    _COOKIE: array(tainted)
    _REQUEST: array(tainted)
    _SERVER: array(tainted)
    _FILES: array(tainted)
    _SESSION: array
    _ENV: array
    GLOBALS: array
    argc: int
    argv: array

Do we really need to store the own example of core stubs?

As far as I can see - you have hardcoded PHP stubs for all (or most, haven't check all) core and ext php functions.

In the future this means that you should manually support all these stubs and track their changes, which should be hell.

I suggest you to re-use existing stubs for popular IDEs - i.e jetbrains phpstorm stubs. I think you can parse them easily and cache with the same format that you already have, but this would be not hardcoded class.

Language analyzer as a service

Have you thought ahead as far as allowing for the static code analysis engine to run as (part of) a service?

That is, I'd like to eventually run the language analyzer in the background, continuously integrating changed files into the working model and providing updated analysis - e.g. allowing it to run as a service with (say) a REST API to which any modern IDE or text-editor could connect and continuously obtain information about the source-code.

Typescript has the right approach to this, with it's incremental compiler/analyzer which runs as a service, and has enabled quick, accurate integration with a range of IDEs and editors.

It would be really awesome if we could have consistent static analysis at design-time on our local systems, as on our continuous integration server, so I think it's worth thinking ahead to this?

Add note in README about phpize dependency

In Debian (and descendants), phpize is available by running sudo apt-get install php5-dev or sudo apt-get isntall php7.0-dev

This tool is not bundled with the default PHP distribution in most cases. It would be good to add a note on how to install it.

Relative namespace bug

Two files:

ns1.php

<?php
namespace A\B\C;
class Util {
    public static function fn($arg) { }
}

ns2.php

<?php
include './ns1.php';
namespace A\B;

C\Util::fn(1);

That call to C\Util::fn() should resolve to \A\B\C\Util::fn() but phan loses track of it and produces:

ns2.php:5 UndefError call to undeclared class \A\B\c\util

Invalid VarError with usage of references

The fix done for #31 didn't fix my initial issue (but it fix the repro case I built). I can't find a minimal repro case that triggers it, but it's similar.. just a call to something that populates a variable by reference.

The best I got is:

git clone https://github.com/composer/composer
cd composer
phan -i src/Composer/Util/ProcessExecutor.php src/Composer/Util/GitLab.php

It prints src/Composer/Util/GitLab.php:61 VarError Variable $output is not defined at https://github.com/composer/composer/blob/master/src/Composer/Util/GitLab.php#L61 but https://github.com/composer/composer/blob/master/src/Composer/Util/ProcessExecutor.php#L44 sets it.

The problem might be that in the GitLab class, $this->process has no type def in a doc block. By looking at the constructor though the type should be possible to infer I'd say, but I am not sure if that's supported yet.

Don't namespace 'self'

<?php
namespace A\B;
class Test {
    /** @return self */
    function fn() {
        return $this;
    }
}

Produces:

ns.php:6 TypeError return \A\B\test but fn() is declared to return \A\B\self

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.