Phan é um analisador estático para PHP que prefere minimizar falsos positivos. Phan tenta provar a incorreção em vez da correção.
Phan procura problemas comuns e verificará a compatibilidade de tipo em várias operações quando as informações de tipo estiverem disponíveis ou puderem ser deduzidas. Phan tem um bom conhecimento (mas não abrangente) de controle de fluxo e pode rastrear valores em alguns casos de uso (por exemplo, arrays, números inteiros e strings).
A maneira mais fácil de usar o Phan é através do Composer.
composer require phan/phan
Com o Phan instalado, você desejará criar um arquivo .phan/config.php
em seu projeto para dizer ao Phan como analisar seu código-fonte. Uma vez configurado, você pode executá-lo via ./vendor/bin/phan
.
Phan 5 depende do PHP 7.2+ com a extensão php-ast (1.1.1+ é o preferido) e suporta a análise da sintaxe do PHP versão 7.0-8.2. As instruções de instalação do php-ast podem ser encontradas aqui. (Phan pode ser usado sem php-ast usando a opção CLI --allow-polyfill-parser
, mas há pequenas diferenças na análise dos comentários do documento)
O Wiki tem mais informações sobre como usar o Phan.
Phan é capaz de realizar os seguintes tipos de análise:
object
, void
, iterable
, ?T
, [$x] = ...;
, deslocamentos de string negativos, capturas de múltiplas exceções, etc.)--dead-code-detection
)--unused-variable-detection
)--redundant-condition-detection
)use
não utilizadas. Esses e alguns outros tipos de problemas podem ser corrigidos automaticamente com --automatic-fix
.@template
).int[]
, UserObject[]
, array<int,UserObject>
, etc.array{key:string,otherKey:?stdClass}
, etc. (internamente e em tags PHPDoc) Isso também suporta a indicação de que os campos de um formato de array são opcionais por meio de array{requiredKey:string,optionalKey?:string}
(útil para @param
)@deprecated
para classes, métodos e funções obsoletas@internal
para elementos (como constante, função, classe, constante de classe, propriedade ou método) como internos ao pacote no qual está definido.@suppress <ISSUE_TYPE>
para suprimir problemas.@property <union_type> <variable_name>
)@method <union_type> <method_name>(<union_type> <param1_name>)
)class_alias
(experimental, desativado por padrão)@phan-closure-scope
(exemplo)array_map
, array_filter
e outras funções internas do array.pcntl
)Consulte Tipos de problemas do Phan para obter descrições e exemplos de todos os problemas que podem ser detectados pelo Phan. Dê uma olhada em PhanIssue para ver a definição de cada tipo de erro.
Dê uma olhada no Tutorial para analisar uma grande base de código desleixada para ter uma ideia de como pode ser o processo de análise contínua para você.
Phan pode ser usado em vários editores e IDEs para verificação de erros, suporte para "ir para definição", etc. por meio do protocolo de servidor de linguagem. Editores e ferramentas também podem solicitar a análise de arquivos individuais em um projeto usando o Modo Daemon mais simples.
Veja o diretório de testes para alguns exemplos das diversas verificações.
Phan é imperfeito e não deve ser usado para provar que seu sistema de orientação de foguetes baseado em PHP está livre de defeitos.
Recursos de análise adicionais foram fornecidos por plug-ins.
{ throw new Exception("Message"); return $value; }
)*printf()
em relação aos argumentos fornecidos (bem como verificando erros comuns)preg_*()
são válidas@suppress
que não são mais necessárias.Exemplo: plugins de Phan para autoanálise.
Depois de instalar o Phan, o Phan precisa ser configurado com detalhes sobre onde encontrar o código para analisar e como analisá-lo. A maneira mais fácil de dizer a Phan onde encontrar o código-fonte é criar um arquivo .phan/config.php
. Um arquivo .phan/config.php
simples pode ser parecido com o seguinte.
<?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 3 rd
// 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 ' ,
],
];
Dê uma olhada em Criando um arquivo de configuração e fortalecendo a análise incrementalmente para obter mais detalhes.
A execução de phan --help
mostrará informações de uso e opções de linha de comando.
Phan lê e entende a maioria das anotações de tipo PHPDoc, incluindo tipos de união (como int|MyClass|string|null
) e tipos de array genéricos (como int[]
ou string[]|MyClass[]
ou array<int,MyClass>
).
Dê uma olhada em Anotando seu código-fonte e Sobre tipos de união para obter ajuda para começar a definir tipos em seu código.
Phan suporta anotações no estilo (int|string)[]
e as representa internamente como int[]|string[]
(ambas as anotações são tratadas como um array que pode ter números inteiros e/ou strings). Quando você tiver arrays de tipos mistos, basta usar array
.
O código a seguir mostra as diversas anotações suportadas.
/ * *
* @ 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 )
);
}
}
Assim como no PHP, qualquer tipo pode ser anulado na declaração da função, o que também significa que um valor nulo pode ser passado para esse parâmetro.
Phan verifica o tipo de cada elemento dos arrays (incluindo chaves e valores). Em termos práticos, isso significa que [$int1=>$int2,$int3=>$int4,$int5=>$str6]
é visto como array<int,int|string>
, que Phan representa como array<int,int>|array<int,string>
. [$strKey => new MyClass(), $strKey2 => $unknown]
será representado como array<string,MyClass>|array<string,mixed>
.
[12,'myString']
serão representados internamente como formas de array, como array{0:12,1:'myString'}
Este analisador estático não rastreia inclusões nem tenta descobrir a mágica do autoloader. Ele trata todos os arquivos que você lança como um grande aplicativo. Para código encapsulado em classes isso funciona bem. Para código executado no escopo global, fica um pouco complicado porque a ordem é importante. Se você tiver um index.php
incluindo um arquivo que define um monte de variáveis globais e tentar acessá-las após include(...)
em index.php
o analisador estático não saberá nada sobre elas.
Em termos práticos, isso significa simplesmente que você deve colocar seus pontos de entrada e quaisquer arquivos que configurem coisas no escopo global no topo da sua lista de arquivos. Se você possui um config.php
que define variáveis globais que todo o resto precisa, então você deve colocá-lo primeiro na lista, seguido por seus vários pontos de entrada e, em seguida, todos os arquivos de sua biblioteca contendo suas classes.
Dê uma olhada no Guia do desenvolvedor para Phan para obter ajuda para começar a hackear Phan.
Ao encontrar um problema, reserve um tempo para criar um pequeno trecho de código de reprodução que ilustre o bug. E depois de fazer isso, conserte. Em seguida, transforme seu trecho de código em um teste e adicione-o aos testes, em seguida, ./test
e envie um PR com sua correção e teste. Alternativamente, você pode abrir um problema com detalhes.
Para executar os testes de unidade de Phan, basta executar ./test
.
Para executar todos os testes de unidade e testes de integração de Phan, execute ./tests/run_all_tests.sh
Estamos empenhados em promover uma comunidade acolhedora. Qualquer participante e colaborador é obrigado a aderir ao nosso Código de Conduta.
Isso requer uma versão atualizada do Firefox/Chrome e pelo menos 4 GB de RAM livre. (este é um download de 15 MB)
Execute o Phan inteiramente no seu navegador.