About me
What's Coding style?
PSR-2與PSR-12程式碼風格標準。
What's static code analysis?
PHPStan
Psalm
Phan
CI/CD examples
Laravel framework integration
Founded by PHP-FIG
PHP Framework Interop Group
PSR-1
PSR-2
PSR-12
More standard docs
https://github.com/php-fig/fig-standards/tree/master/accepted
Files MUST use only <?php and <?= tags.
Files MUST use only UTF-8 without BOM for PHP code.
Files SHOULD either declare symbols (classes, functions, constants, etc.) or cause side-effects (e.g. generate output, change .ini settings, etc.) but SHOULD NOT do both.
Namespaces and classes MUST follow an "autoloading" PSR: [PSR-0, PSR-4].
Class names MUST be declared in StudlyCaps.
Class constants MUST be declared in all upper case with underscore separators.
Method names MUST be declared in camelCase.
Code MUST follow a "coding style guide" PSR [PSR-1].
Code MUST use 4 spaces for indenting, not tabs.
There MUST NOT be a hard limit on line length; the soft limit MUST be 120 characters; lines SHOULD be 80 characters or less.
There MUST be one blank line after the namespace declaration, and there MUST be one blank line after the block of use declarations.
Opening braces for classes MUST go on the next line, and closing braces MUST go on the next line after the body.
Opening braces for methods MUST go on the next line, and closing braces MUST go on the next line after the body.
Visibility MUST be declared on all properties and methods; abstract and final MUST be declared before the visibility; static MUST be declared after the visibility.
Control structure keywords MUST have one space after them; method and function calls MUST NOT.
Opening braces for control structures MUST go on the same line, and closing braces MUST go on the next line after the body.
Opening parentheses for control structures MUST NOT have a space after them, and closing parentheses for control structures MUST NOT have a space before.
FILE: ...n-source-contributions/localized/src/Validation/LtValidation.php
----------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------
31 | ERROR | [x] Use single instead of double quotes for simple
| | strings.
----------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------
FILE: ...is/build/open-source-contributions/localized/tests/bootstrap.php
----------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------
15 | ERROR | [x] Use single instead of double quotes for simple
| | strings.
----------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------
FILE: ...n-source-contributions/localized/src/Validation/BrValidation.php
----------------------------------------------------------------------
FOUND 2 ERRORS AFFECTING 1 LINE
----------------------------------------------------------------------
196 | ERROR | [x] Use single instead of double quotes for simple
| | strings.
196 | ERROR | [x] Use single instead of double quotes for simple
| | strings.
----------------------------------------------------------------------
PHPCBF CAN FIX THE 2 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------
<?xml version="1.0"?>
<ruleset name="Coding Standard">
<arg name="basepath" value="."/>
<arg name="colors"/>
<arg value="sp"/>
<config name="ignore_warnings_on_exit" value="1"/>
<file>./src</file>
<file>./tests</file>
<rule ref="PSR2"></rule>
<!-- <rule ref="PSR12"></rule> -->
<rule ref="Squiz.Commenting.ClassComment">
<exclude name="Squiz.Commenting.ClassComment.TagNotAllowed"/>
<type>warning</type>
<exclude-pattern>*/tests/</exclude-pattern>
</rule>
<rule ref="Squiz.Commenting.ClassComment.Missing">
<type>warning</type>
</rule>
<rule ref="Squiz.Commenting.FunctionComment.Missing">
<type>warning</type>
<exclude-pattern>*/config/</exclude-pattern>
</rule>
<rule ref="Squiz.Commenting.FunctionComment.MissingParamTag">
<type>warning</type>
</rule>
<rule ref="Squiz.Commenting.FunctionComment.MissingParamComment">
<type>warning</type>
</rule>
<rule ref="Squiz.Commenting.FunctionComment.ParamCommentNotCapital">
<type>warning</type>
</rule>
<rule ref="Generic.Metrics.CyclomaticComplexity">
<properties>
<property name="absoluteComplexity" value="50"/>
</properties>
</rule>
<rule ref="Generic.Metrics.NestingLevel">
<properties>
<property name="nestingLevel" value="2"/>
<property name="absoluteNestingLevel" value="4"/>
</properties>
</rule>
</ruleset>
curl -OL https://cs.symfony.com/download/php-cs-fixer-v2.phar
php php-cs-fixer-v2.phar fix --dry-run --format=txt --verbose --diff --diff-format=udiff --config=.cs.php
curl -OL https://cs.symfony.com/download/php-cs-fixer-v3.phar
php php-cs-fixer-v3.phar fix --dry-run --format=txt --verbose --diff --diff-format=udiff --config=.cs.php
<?php
return PhpCsFixer\Config::create()
->setUsingCache(false)
->setRiskyAllowed(true)
//->setCacheFile(__DIR__ . '/.php_cs.cache')
->setRules([
'@PSR1' => true,
'@PSR2' => true,
'@Symfony' => true,
'psr4' => true,
'yoda_style' => false,
'array_syntax' => ['syntax' => 'short'],
'list_syntax' => ['syntax' => 'short'],
'concat_space' => ['spacing' => 'one'],
'cast_spaces' => ['space' => 'none'],
'compact_nullable_typehint' => true,
'increment_style' => ['style' => 'post'],
'declare_equal_normalize' => ['space' => 'single'],
'no_short_echo_tag' => true,
'protected_to_private' => false,
'phpdoc_align' => false,
'phpdoc_add_missing_param_annotation' => ['only_untyped' => false],
'phpdoc_order' => true, // psr-5
'phpdoc_no_empty_return' => false,
'align_multiline_comment' => true, // psr-5
'general_phpdoc_annotation_remove' => [
'annotations' => [
'author',
'package',
],
],
])
->setFinder(PhpCsFixer\Finder::create()
->in(__DIR__ . '/src')
->in(__DIR__ . '/tests')
->name('*.php')
->ignoreDotFiles(true)
->ignoreVCS(true));
It's the analysis of computer software that is performed without actually executing programs.
Dynamic code analysis is the analysis of computer software that is performed by executing programs.
Unit tests, integration tests, system tests and acceptance tests use dynamic testing.
Psalm
PHPStan
Phan→The PHP Father recommended
composer require phpstan/phpstan:0.* --dev
composer require vimeo/psalm:4.* --dev
composer require phan/phan:5.* --dev
there are no syntax errors;
all the classes, methods, functions and constants exist;
the variables exist;
the hints in PHPDoc correspond to reality;
there are no arguments or variables unused.
Most analyzers allow to configure the level of strictness of checking and imitate strict_types:
they check that String or Boolean aren’t passed to this function.
Most analyzers allow to configure the level of strictness of checking and imitate strict_types:
they check that String or Boolean aren’t passed to this function.
/**
* @var string|int|bool $yes_or_no
*/
function isYes($yes_or_no) :bool
{
if (is_numeric($yes_or_no)) {
return $yes_or_no > 0;
} else {
return strtoupper($yes_or_no) == 'YES';
}
}
Most analyzers allow to configure the level of strictness of checking and imitate strict_types:
they check that String or Boolean aren’t passed to this function.
/** @return int|bool */
function fwrite(...) {
…
}
<?php
/** @return resource|bool */
function open_file() {
$fp = fopen('./composer.json', 'r');
if($fp === false) {
return false;
}
return fwrite($fp, "some string");
}
lee@lee-VirtualBox:~/phpstan-example$ vendor/bin/phpstan analyse ./false_type.php --level=max -c phpstan.neon --no-progress --ansi
------ --------------------------------------------------------------------------------------------
Line false_type.php
------ --------------------------------------------------------------------------------------------
4 Function open_file() never returns resource so it can be removed from the return typehint.
10 Function open_file() should return bool|resource but returns int|false.
------ --------------------------------------------------------------------------------------------
<?php
/** @return int|false */
function open_file() {
$fp = fopen('./composer.json', 'r');
if($fp === false) {
return false;
}
return fwrite($fp, "some string");
}
lee@lee-VirtualBox:~/phpstan-example$ vendor/bin/phpstan analyse ./false_type.php \
--level=max -c phpstan.neon --no-progress --ansi
[OK] No errors
<?php
/** @return array */
function array_func(array $arr) {
return $arr;
}
lee@lee-VirtualBox:~/phpstan-example$ vendor/bin/phpstan analyse ./array_example.php \
--level=max -c phpstan.neon --no-progress --ansi
------ -----------------------------------------------------------------------------------------------
Line array_example.php
------ -----------------------------------------------------------------------------------------------
4 Function array_func() has parameter $arr with no value type specified in iterable type array.
💡 See: https://phpstan.org/blog/solving-phpstan-no-value-type-specified-in-iterable-type
4 Function array_func() return type has no value type specified in iterable type array.
💡 See: https://phpstan.org/blog/solving-phpstan-no-value-type-specified-in-iterable-type
------ -----------------------------------------------------------------------------------------------
[ERROR] Found 2 errors
<?php
/**
@param array<string> $arr
@return array<string>
*/
function array_func($arr) {
return $arr;
}
Developed by Ondřej Mirtes
Install it (the simplest way is via Composer)
Configure it (optional)
Run it
lee@lee-VirtualBox:~/phpstan-example$ vendor/bin/phpstan analyse ./array_example.php
1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
[OK] No errors
💡 Tip of the Day:
PHPStan is performing only the most basic checks.
You can pass a higher rule level through the --level option
(the default and current level is 0) to analyse code more thoroughly.
lee@lee-VirtualBox:~/phpstan-example$
PHPStan will try to autoload unknown classes.
If some classes are not autoloaded, it will not be able to find them and will return an error.
If using magical methods via __call, __get, or __set, it can write a plug-in for PHPStan.
In actual fact, PHPStan doesn’t only perform autoload in the case of unknown classes, but it also does so for all classes.
Using neon-format for configuration.
No support for its PHPDoc tags @phpstan-var, @phpstan-return etc.
PhpStan has a playground website https://phpstan.org.
Developed by the Etsy company. First commits by Rasmus Lerdorf.
Requiring the php-ast extension.
Plugin example is available here.
Creating a .phan/config.php file.
Playground website is available.
lee@lee-VirtualBox:~/phpstan-example$ php vendor/bin/phan array_example.php
analyze ████████████████████████████████████████████████████████████ 100.0% 29MB/29MB
lee@lee-VirtualBox:~/phpstan-example$ php vendor/bin/phan array_example.php
analyze ████████████████████████████████████████████████████████████ 100.0% 28MB/31MB
array_example.php:9 PhanSyntaxError syntax error, unexpected '}', expecting ';' (at column 1)
Developed by the Vimeo company
Annotations code
XML format file about configuration
Type aliases
array
closure
union type (for example, several classes or a class and other types)
enum
<?xml version="1.0"?>
<psalm
errorLevel="1"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
</psalm>
░░░░░░░E░░░░E░E░░░EE░░░░░░░░░░░E░░░░E░░░░░E░E░░
ERROR: ParamNameMismatch - src/Element/Element.php:131:54 - Argument 2 of Innmind\Xml\Element\Element::replaceChild has wrong name $node, expecting $child as defined by Innmind\Xml\Node::replaceChild (see https://psalm.dev/230)
public function replaceChild(int $position, Node $node): Node
ERROR: ParamNameMismatch - src/Element/SelfClosingElement.php:36:54 - Argument 2 of Innmind\Xml\Element\SelfClosingElement::replaceChild has wrong name $node, expecting $child as defined by Innmind\Xml\Node::replaceChild (see https://psalm.dev/230)
public function replaceChild(int $position, Node $node): Node
ERROR: ParamNameMismatch - src/Node/CharacterData.php:43:54 - Argument 2 of Innmind\Xml\Node\CharacterData::replaceChild has wrong name $node, expecting $child as defined by Innmind\Xml\Node::replaceChild (see https://psalm.dev/230)
public function replaceChild(int $position, Node $node): Node
ERROR: ParamNameMismatch - src/Node/Comment.php:43:54 - Argument 2 of Innmind\Xml\Node\Comment::replaceChild has wrong name $node, expecting $child as defined by Innmind\Xml\Node::replaceChild (see https://psalm.dev/230)
public function replaceChild(int $position, Node $node): Node
ERROR: ParamNameMismatch - src/Node/Document.php:86:54 - Argument 2 of Innmind\Xml\Node\Document::replaceChild has wrong name $node, expecting $child as defined by Innmind\Xml\Node::replaceChild (see https://psalm.dev/230)
public function replaceChild(int $position, Node $node): Node
Using Composer to install required development dependencies.
.......
psalm:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: ['7.4', '8.0']
name: 'Psalm'
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: mbstring, intl
- name: Get Composer Cache Directory
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache dependencies
uses: actions/cache@v2
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Dependencies
run: composer install
- name: Psalm
run: vendor/bin/psalm --shepherd
.......