Skip to content

Commit

Permalink
Merge branch 'feature/64' into feature/59
Browse files Browse the repository at this point in the history
  • Loading branch information
gmazzap committed Aug 25, 2023
2 parents 62544a2 + b22647b commit f2c35b3
Show file tree
Hide file tree
Showing 34 changed files with 514 additions and 855 deletions.
76 changes: 0 additions & 76 deletions .github/workflows/php-qa.yml

This file was deleted.

51 changes: 51 additions & 0 deletions .github/workflows/quality-assurance-php.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: PHP Quality Assurance

on:
push:
paths:
- '**workflows/quality-assurance-php.yml'
- '**.php'
- '**phpcs.xml.dist'
- '**phpunit.xml.dist'
- '**psalm.xml'
workflow_dispatch:
inputs:
jobs:
required: true
type: choice
default: 'Run all'
description: 'Choose jobs to run'
options:
- 'Run all'
- 'Run PHPCS only'
- 'Run Psalm only'
- 'Run lint only'
- 'Run static analysis'
- 'Run unit tests only'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
lint-php:
uses: inpsyde/reusable-workflows/.github/workflows/lint-php.yml@main
if: ${{ (github.event_name != 'workflow_dispatch') || ((github.event.inputs.jobs == 'Run all') || (github.event.inputs.jobs == 'Run lint only') || (github.event.inputs.jobs == 'Run static analysis')) }}
with:
PHP_MATRIX: '["7.4", "8.0", "8.1", "8.2"]'
LINT_ARGS: '-e php --colors --show-deprecated ./Inpsyde'

coding-standards-analysis-php:
if: ${{ (github.event_name != 'workflow_dispatch') || ((github.event.inputs.jobs == 'Run all') || (github.event.inputs.jobs == 'Run PHPCS only') || (github.event.inputs.jobs == 'Run static analysis')) }}
uses: inpsyde/reusable-workflows/.github/workflows/coding-standards-php.yml@main

static-code-analysis-php:
if: ${{ (github.event_name != 'workflow_dispatch') || ((github.event.inputs.jobs == 'Run all') || (github.event.inputs.jobs == 'Run Psalm only') || (github.event.inputs.jobs == 'Run static analysis')) }}
uses: inpsyde/reusable-workflows/.github/workflows/static-analysis-php.yml@main

tests-unit-php:
if: ${{ (github.event_name != 'workflow_dispatch') || ((github.event.inputs.jobs == 'Run all') || (github.event.inputs.jobs == 'Run unit tests only')) }}
uses: inpsyde/reusable-workflows/.github/workflows/tests-unit-php.yml@main
with:
PHP_MATRIX: '["7.4", "8.0", "8.1", "8.2"]'
PHPUNIT_ARGS: '--no-coverage'
7 changes: 0 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,4 @@ composer.phar
composer.lock
/vendor/
/phpunit.xml

# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
# composer.lock
.buildpath
.project
.settings/
.phpunit.result.cache
17 changes: 9 additions & 8 deletions Inpsyde/PhpcsHelpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,14 @@ public static function findOopContext(File $file, int $position): int
$targetLevel = (int)$tokens[$position]['level'] - 1;

foreach ($tokens[$position]['conditions'] as $condPosition => $condCode) {
assert(is_int($condPosition));
$condLevel = (int)($tokens[$condPosition]['level'] ?? -1);

if (
in_array($condCode, Tokens::$ooScopeTokens, true)
&& ($condLevel === $targetLevel)
) {
return (int)$condPosition;
return $condPosition;
}
}

Expand Down Expand Up @@ -387,7 +388,7 @@ public static function functionDocBlockTags(
$normalizedTags = [];
static $rand;
$rand or $rand = bin2hex(random_bytes(3));
foreach ($tags as list($tagName, $tagContent)) {
foreach ($tags as [$tagName, $tagContent]) {
empty($normalizedTags[$tagName]) and $normalizedTags[$tagName] = [];
if (!$normalizeContent) {
$normalizedTags[$tagName][] = $tagContent;
Expand Down Expand Up @@ -433,7 +434,7 @@ public static function functionDocBlockParamTypes(File $file, int $functionPosit

$types = [];
foreach ($params as $param) {
preg_match('~^([^$]+)\s*(\$(?:[^\s]+))~', trim($param), $matches);
preg_match('~^([^$]+)\s*(\$\S+)~', trim($param), $matches);
if (empty($matches[1]) || empty($matches[2])) {
continue;
}
Expand Down Expand Up @@ -461,7 +462,7 @@ public static function isHookFunction(File $file, int $position): bool
*/
public static function functionBody(File $file, int $position): string
{
list($start, $end) = static::functionBoundaries($file, $position);
[$start, $end] = static::functionBoundaries($file, $position);
if ($start < 0 || $end < 0) {
return '';
}
Expand All @@ -470,7 +471,7 @@ public static function functionBody(File $file, int $position): string
$tokens = $file->getTokens();
$body = '';
for ($i = $start + 1; $i < $end; $i++) {
$body .= (string)$tokens[$i]['content'];
$body .= (string)($tokens[$i]['content'] ?? '');
}

return $body;
Expand Down Expand Up @@ -531,7 +532,7 @@ public static function returnsCountInfo(File $file, int $position): array
{
$returnCount = ['nonEmpty' => 0, 'void' => 0, 'null' => 0, 'total' => 0];

list($start, $end) = self::functionBoundaries($file, $position);
[$start, $end] = self::functionBoundaries($file, $position);
if ($start < 0 || $end <= 0) {
return $returnCount;
}
Expand All @@ -550,8 +551,8 @@ public static function returnsCountInfo(File $file, int $position): array

$pos = $start + 1;
while ($pos < $end) {
list(, $innerFunctionEnd) = self::functionBoundaries($file, $pos);
list(, $innerClassEnd) = self::classBoundaries($file, $pos);
[, $innerFunctionEnd] = self::functionBoundaries($file, $pos);
[, $innerClassEnd] = self::classBoundaries($file, $pos);
if ($innerFunctionEnd > 0 || $innerClassEnd > 0) {
$pos = ($innerFunctionEnd > 0) ? $innerFunctionEnd + 1 : $innerClassEnd + 1;
continue;
Expand Down
75 changes: 43 additions & 32 deletions Inpsyde/Sniffs/CodeQuality/ArgumentTypeDeclarationSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,87 +19,98 @@

class ArgumentTypeDeclarationSniff implements Sniff
{
const TYPE_CODES = [
public const TYPE_CODES = [
T_STRING,
T_ARRAY_HINT,
T_CALLABLE,
T_SELF,
];

const METHODS_WHITELIST = [
public const METHODS_WHITELIST = [
'unserialize',
'seek',
];

/**
* @return array<int|string>
*
* phpcs:disable Inpsyde.CodeQuality.ReturnTypeDeclaration
* @return list<int|string>
*/
public function register()
public function register(): array
{
// phpcs:enable Inpsyde.CodeQuality.ReturnTypeDeclaration

return [T_FUNCTION, T_CLOSURE, T_FN];
}

/**
* @param File $file
* @param int $position
* @param File $phpcsFile
* @param int $stackPtr
* @return void
*
* phpcs:disable Inpsyde.CodeQuality
* phpcs:disable Inpsyde.CodeQuality.ArgumentTypeDeclaration
* phpcs:disable Generic.Metrics.CyclomaticComplexity
*/
public function process(File $file, $position)
public function process(File $phpcsFile, $stackPtr): void
{
// phpcs:enable Inpsyde.CodeQuality
// phpcs:enable Inpsyde.CodeQuality.ArgumentTypeDeclaration
// phpcs:enable Generic.Metrics.CyclomaticComplexity

if (
PhpcsHelpers::functionIsArrayAccess($file, $position)
|| PhpcsHelpers::isHookClosure($file, $position)
|| PhpcsHelpers::isHookFunction($file, $position)
|| PhpcsHelpers::isUntypedPsrMethod($file, $position)
|| (
PhpcsHelpers::functionIsMethod($file, $position)
&& in_array($file->getDeclarationName($position), self::METHODS_WHITELIST, true)
)
) {
if ($this->shouldIgnore($phpcsFile, $stackPtr)) {
return;
}

/** @var array<int, array<string, mixed>> $tokens */
$tokens = $file->getTokens();
$paramsStart = (int)($tokens[$position]['parenthesis_opener'] ?? 0);
$paramsEnd = (int)($tokens[$position]['parenthesis_closer'] ?? 0);
$tokens = $phpcsFile->getTokens();
$paramsStart = (int)($tokens[$stackPtr]['parenthesis_opener'] ?? 0);
$paramsEnd = (int)($tokens[$stackPtr]['parenthesis_closer'] ?? 0);

if (!$paramsStart || !$paramsEnd || $paramsStart >= ($paramsEnd - 1)) {
return;
}

$docBlockTypes = PhpcsHelpers::functionDocBlockParamTypes($file, $position);
$variables = PhpcsHelpers::filterTokensByType($paramsStart, $paramsEnd, $file, T_VARIABLE);
$docBlockTypes = PhpcsHelpers::functionDocBlockParamTypes($phpcsFile, $stackPtr);
$variables = PhpcsHelpers::filterTokensByType($paramsStart, $paramsEnd, $phpcsFile, T_VARIABLE);

foreach ($variables as $varPosition => $varToken) {
// Not triggering error for variable explicitly declared as mixed (or mixed|null)
if ($this->isMixed((string)($varToken['content'] ?? ''), $docBlockTypes)) {
continue;
}

$typePosition = $file->findPrevious(
$typePosition = $phpcsFile->findPrevious(
[T_WHITESPACE, T_ELLIPSIS, T_BITWISE_AND],
$varPosition - 1,
$paramsStart + 1,
true
);

$type = $tokens[$typePosition] ?? null;
/** @psalm-suppress MixedArgument */
if ($type && !in_array($type['code'], self::TYPE_CODES, true)) {
$file->addWarning('Argument type is missing', $position, 'NoArgumentType');
if ($type && !in_array($type['code'] ?? '', self::TYPE_CODES, true)) {
$phpcsFile->addWarning('Argument type is missing', $stackPtr, 'NoArgumentType');
}
}
}

/**
* @param File $phpcsFile
* @param int $stackPtr
* @return bool
*
* phpcs:disable Inpsyde.CodeQuality.ArgumentTypeDeclaration
*/
private function shouldIgnore(File $phpcsFile, $stackPtr): bool
{
// phpcs:enable Inpsyde.CodeQuality.ArgumentTypeDeclaration

$name = $phpcsFile->getDeclarationName($stackPtr);

return PhpcsHelpers::functionIsArrayAccess($phpcsFile, $stackPtr)
|| PhpcsHelpers::isHookClosure($phpcsFile, $stackPtr)
|| PhpcsHelpers::isHookFunction($phpcsFile, $stackPtr)
|| PhpcsHelpers::isUntypedPsrMethod($phpcsFile, $stackPtr)
|| (
PhpcsHelpers::functionIsMethod($phpcsFile, $stackPtr)
&& in_array($name, self::METHODS_WHITELIST, true)
);
}

/**
* @param string $paramName
* @param array<string, array<string>> $docBlockTypes
Expand Down
42 changes: 0 additions & 42 deletions Inpsyde/Sniffs/CodeQuality/ConstantVisibilitySniff.php

This file was deleted.

Loading

0 comments on commit f2c35b3

Please sign in to comment.