Static Analysis به نوعی آنالیز سورسکد اشاره دارد که در آن هیچ کدی اجرا نمیشود بلکه ابزار مورد استفاده صرفاً به دنبال مشکلات احتمالی سینتکسی و ... میگردد. یکی از فیچرهای PHP 7 که آنالیز استاتیک سورسکد را تسهیل میکند Abstract Syntactic Tree یا به اختصار AST نام دارد که بواسطهٔ آن شمایی منسجم از سورسکد مد نظر ترسیم شده سپس به سادگی میتوان به دنبال الگوهای مشکلزا در آن گشت.
Phan یک لایبرری اپنسورس مبتنی بر اِکستنشن AST برای آنالیز استاتیک سورسکدهای پیاچپی است که توسط خالق اصلی این زبان یعنی Rasmus Lerdorf توسعه داده شده است اما در حال حاضر کانتریبوترهای دیگری نیز به این پروژه ملحق شدهاند.
راهنمای نصب Phan
به منظور استفاده از این ابزار، نیاز به برخورداری از نسخهٔ PHP 7 و همچنین اِکستنشن AST داریم. ابتدا جهت اطمینان حاصل کردن از نسخهٔ پیاچپی خود، کامند زیر را در محیط ترمینال وارد میکنیم:
$ php -v
PHP 7.1.28-1+ubuntu18.04.1+deb.sury.org+3 (cli) (built: Apr 10 2019 10:50:29) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.1.28-1+ubuntu18.04.1+deb.sury.org+3, Copyright (c) 1999-2018, by Zend Technologies
حال نوبت به نصب اِکستنشن AST میرسد که برای این منظور نیز میتوانیم کامند زیر را وارد کنیم:
$ sudo apt-get install php-ast
پس از نصب موفقیتآمیز این اِکستنشن، نیاز است تا آن را در فایل php.ini
فعال سازیم. به طور مثال، در محیط گنو/لینوکس این فایل در مسیر etc/php/7.1/apache2/
قرار دارد. داخل پوشهٔ apache2
فایلی است تحت عنوان php.ini
که پس از باز کردن آن، میباید دستور extension=ast.so
را داخلش درج نموده و آن را ذخیره ساخت.
اکنون نوبت به نصب خودِ ابزار Phan میرسد که برای نصب آن نیاز به ابزار Git داریم. جهت نصب، کامند زیر را در مسیری وارد مینماییم که میخواهیم پروژه را ایجاد کنیم:
$ git clone https://github.com/phan/phan.git
به طور مثال، در مسیر var/www/
آن را دانلود میکنیم و خواهیم دید که فولدری تحت عنوان phan
در مسیر مذکور ساخته میشود. حال با استفاده از دستور cd phan
وارد این فولدر شده سپس با استفاده از Composer، وابستگیهای این ابزار را نصب میکنیم:
$ sudo composer install
در صورتی که پروسهٔ نصب با موفقیت پایان پذیرد، حال نیاز داریم تا تنظیمات مد نظر خود را اِعمال کنیم که برای این منظور، فولدری مخفی تحت عنوان phan.
داخل این پروژه وجود دارد که داخلش فایلی است تحت عنوان config.php
که کلیهٔ تنظیمات مربوط به این لایبرری داخل آن نوشته میشود.
اکنون جهت تست، داخل روت پروژه فایلی با نامی دلخواه مثلاً static-analyzer.php
ساخته و کدهای زیر را داخل آن درج میکنیم:
<?php
$name = 'Behzad';
$lastname;
echo "Hello $name";
سپس دستور زیر را جهت اجرای این ابزار وارد میکنیم:
$ php phan static-analyzer.php
چنانچه در حین تحلیل کردن سورسکد هیچ مشکلی رخ ندهد، خروجی زیر در معرض دیدمان قرار خواهد گرفت:
A future major version of Phan will require php-ast 1.0.1+ for AST version 70. php-ast 0.1.6 is installed.
(Set PHAN_SUPPRESS_AST_UPGRADE_NOTICE=1 to suppress this message)
bm.php:3 PhanNoopVariable Unused variable
خط اول و دوم مربوط به هشداری است که در ارتباط با نسخهٔ نصبشدهٔ AST اعلام شده است که میتوان آن را نادیده گرفت اما خط سوم از خروجی فوق مرتبط با مشکلی است که در سورسکدمان وجود دارد و آن هم اینکه در کدمان در خط سوم متغیری تعریف شده اما هرگز از آن استفاده نکردهایم.
همچنین جهت آشنایی بیشتر با نحوهٔ کاربرد این ابزار، میتواند کامند php phan --help
را وارد نمود به طوری که با خروجی زیر مواجه خواهیم شد:
Usage: phan [options] [files...]
-f, --file-list <filename>
A file containing a list of PHP files to be analyzed
-l, --directory <directory>
A directory that should be parsed for class and
method information. After excluding the directories
defined in --exclude-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.
You may include multiple `--directory DIR` options.
--exclude-file <file>
A file that should not be parsed or analyzed (or read
at all). This is useful for excluding hopelessly
unanalyzable files.
-3, --exclude-directory-list <dir_list>
A comma-separated list of directories that defines files
that will be excluded from static analysis, but whose
class and method information should be included.
(can be repeated, ignored if --include-analysis-directory-list is used)
Generally, you'll want to include the directories for
third-party code (such as "vendor/") in this list.
-I, --include-analysis-file-list <file_list>
A comma-separated list of files that will be included in
static analysis. All others won't be analyzed.
(can be repeated)
This is primarily intended for performing standalone
incremental analysis.
-d, --project-root-directory </path/to/project>
Hunt for a directory named `.phan` in the provided directory
and read configuration file `.phan/config.php` from that path.
-r, --file-list-only
A file containing a list of PHP files to be analyzed to the
exclusion of any other directories or files passed in. This
is unlikely to be useful.
-k, --config-file
A path to a config file to load (instead of the default of
`.phan/config.php`).
-m <mode>, --output-mode
Output mode from 'text', 'json', 'csv', 'codeclimate', 'checkstyle', or 'pylint'
-o, --output <filename>
Output filename
--init
[--init-level=3]
[--init-analyze-dir=path/to/src]
[--init-analyze-file=path/to/file.php]
[--init-no-composer]
Generates a `.phan/config.php` in the current directory
based on the project's composer.json.
The logic used to generate the config file is currently very simple.
Some third party classes (e.g. in vendor/)
will need to be manually added to 'directory_list' or excluded,
and you may end up with a large number of issues to be manually suppressed.
See https://github.com/phan/phan/wiki/Tutorial-for-Analyzing-a-Large-Sloppy-Code-Base
[--init-level] affects the generated settings in `.phan/config.php`
(e.g. null_casts_as_array).
`--init-level` can be set to 1 (strictest) to 5 (least strict)
[--init-analyze-dir] can be used as a relative path alongside directories
that Phan infers from composer.json's "autoload" settings
[--init-analyze-file] can be used as a relative path alongside files
that Phan infers from composer.json's "bin" settings
[--init-no-composer] can be used to tell Phan that the project
is not a composer project.
Phan will not check for composer.json or vendor/,
and will not include those paths in the generated config.
[--init-overwrite] will allow 'phan --init' to overwrite .phan/config.php.
-C, --color
Add colors to the outputted issues. Tested in Unix.
This is recommended for only the default --output-mode ('text')
-p, --progress-bar
Show progress bar
-q, --quick
Quick mode - doesn't recurse into all function calls
-b, --backward-compatibility-checks
Check for potential PHP 5 -> PHP 7 BC issues
--target-php-version {7.0,7.1,7.2,7.3,7.4,native}
The PHP version that the codebase will be checked for compatibility against.
For best results, the PHP binary used to run Phan should have the same PHP version.
(Phan relies on Reflection for some param counts
and checks for undefined classes/methods/functions)
-i, --ignore-undeclared
Ignore undeclared functions and classes
-y, --minimum-severity <level in {0,5,10}>
Minimum severity level (low=0, normal=5, critical=10) to report.
Defaults to 0.
-c, --parent-constructor-required
Comma-separated list of classes that require
parent::__construct() to be called
-x, --dead-code-detection
Emit issues for classes, methods, functions, constants and
properties that are probably never referenced and can
be removed. This implies `--unused-variable-detection`.
--unused-variable-detection
Emit issues for variables, parameters and closure use variables
that are probably never referenced.
This has a few known false positives, e.g. for loops or branches.
-j, --processes <int>
The number of parallel processes to run during the analysis
phase. Defaults to 1.
-z, --signature-compatibility
Analyze signatures for methods that are overrides to ensure
compatibility with what they're overriding.
--disable-cache
Don't cache any ASTs from the polyfill/fallback.
ASTs from the native parser (php-ast) don't need to be cached.
This is useful if Phan will be run only once and php-ast is unavailable (e.g. in Travis)
--disable-plugins
Don't run any plugins. Slightly faster.
-P, --plugin <pluginName|path/to/Plugin.php>
Add a plugin to run. This flag can be repeated.
(Either pass the name of the plugin or a relative/absolute path to the plugin)
--strict-method-checking
Warn if any type in a method invocation's object is definitely not an object,
or any type in an invoked expression is not a callable.
(Enables the config option `strict_method_checking`)
--strict-param-checking
Warn if any type in an argument's union type cannot be cast to
the parameter's expected union type.
(Enables the config option `strict_param_checking`)
--strict-property-checking
Warn if any type in a property assignment's union type
cannot be cast to a type in the property's declared union type.
(Enables the config option `strict_property_checking`)
--strict-return-checking
Warn if any type in a returned value's union type
cannot be cast to the declared return type.
(Enables the config option `strict_return_checking`)
-S, --strict-type-checking
Equivalent to
`--strict-method-checking --strict-param-checking --strict-property-checking --strict-return-checking`.
--use-fallback-parser
If a file to be analyzed is syntactically invalid
(i.e. "php --syntax-check path/to/file" would emit a syntax error),
then retry, using a different, slower error tolerant parser to parse it.
(And phan will then analyze what could be parsed).
This flag is experimental and may result in unexpected exceptions or errors.
This flag does not affect excluded files and directories.
--allow-polyfill-parser
If the `php-ast` extension isn't available or is an outdated version,
then use a slower parser (based on tolerant-php-parser) instead.
Note that https://github.com/Microsoft/tolerant-php-parser
has some known bugs which may result in false positive parse errors.
--force-polyfill-parser
Use a slower parser (based on tolerant-php-parser) instead of the native parser,
even if the native parser is available.
Useful mainly for debugging.
-s, --daemonize-socket </path/to/file.sock>
Unix socket for Phan to listen for requests on, in daemon mode.
--daemonize-tcp-host
TCP hostname for Phan to listen for JSON requests on, in daemon mode.
(e.g. 'default', which is an alias for host 127.0.0.1, or `0.0.0.0` for
usage with Docker). `phan_client` can be used to communicate with the Phan Daemon.
--daemonize-tcp-port <default|1024-65535>
TCP port for Phan to listen for JSON requests on, in daemon mode.
(e.g. 'default', which is an alias for port 4846.)
`phan_client` can be used to communicate with the Phan Daemon.
-v, --version
Print Phan's version number
-h, --help
This help information
--extended-help
This help information, plus less commonly used flags
(E.g. for daemon mode)
همانطور که ملاحظه میشود، آپشنهای کاربردی در این ابزار امکانی را در اختیار توسعهدهندگان میگذارند تا بتوانند بسته به نیاز خود نحوهٔ آنالیز سورسکد را مدیریت کنند.