Skip to content

Commit

Permalink
feat: add IteratorItemTypeSniff
Browse files Browse the repository at this point in the history
  • Loading branch information
gskema committed May 23, 2024
1 parent a062ea8 commit e4543fa
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 1 deletion.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ All notable changes to `phpcs-type-sniff` will be documented in this file.

Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles.

## Unreleased
## 81.6.0 - 2024-05-23
### Changed
- Min `phpcs` version to 3.10
### Added
- `IteratorItemTypeSniff` - enabled by default

## 81.5.2 - 2024-04-05
### Changed
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ String `true/false` values are automatically converted to booleans.
<property name="FqcnMethodSniff.enabled" value="false" />
<property name="FqcnPropSniff.enabled" value="false" />
<property name="FqcnDescriptionSniff.enabled" value="false" />
<property name="IteratorItemTypeSniff.enabled" value="false" />

<!-- Change violation report type for all sniffs. Default is warning. -->
<property name="reportType" value="error" />
Expand All @@ -340,6 +341,7 @@ String `true/false` values are automatically converted to booleans.
<property name="FqcnMethodSniff.reportType" value="error" />
<property name="FqcnPropSniff.reportType" value="warning" />
<property name="FqcnDescriptionSniff.reportType" value="warning" />
<property name="IteratorItemTypeSniff.reportType" value="warning" />

<!-- Tags that should be removed from method PHPDoc -->
<property name="FqcnMethodSniff.invalidTags" type="array">
Expand Down
74 changes: 74 additions & 0 deletions src/Sniffs/CodeElement/IteratorItemTypeSniff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

namespace Gskema\TypeSniff\Sniffs\CodeElement;

use Error;
use Gskema\TypeSniff\Core\CodeElement\Element\AbstractFqcnElement;
use Gskema\TypeSniff\Core\CodeElement\Element\ClassElement;
use Gskema\TypeSniff\Core\CodeElement\Element\CodeElementInterface;
use Gskema\TypeSniff\Core\SniffHelper;
use IteratorAggregate;
use PHP_CodeSniffer\Files\File;
use ReflectionClass;
use ReflectionException;

class IteratorItemTypeSniff implements CodeElementSniffInterface
{
protected const CODE = 'IteratorItemTypeSniff';

protected string $reportType = 'warning';
protected bool $addViolationId = true;

/**
* @inheritDoc
*/
public function configure(array $config): void
{
$this->reportType = (string)($config['reportType'] ?? 'warning');
$this->addViolationId = (bool)($config['addViolationId'] ?? true);
}

/**
* @inheritDoc
*/
public function register(): array
{
return [
ClassElement::class,
];
}

/**
* @inheritDoc
* @param AbstractFqcnElement $element
*/
public function process(File $file, CodeElementInterface $element, CodeElementInterface $parentElement): void
{
try {
$ref = new ReflectionClass($element->getFqcn());
} catch (Error | ReflectionException) {
return; // give up...
}

if (!in_array(IteratorAggregate::class, $ref->getInterfaceNames())) {
return;
}

if ($element->getDocBlock()->getTagsByName('template-implements')) {
return;
} elseif ($element->getDocBlock()->getTagsByName('template-extends')) {
return;
} else {
$originId = $this->addViolationId ? $element->getFqcn() : null;
SniffHelper::addViolation(
$file,
'Classes which implement IteratorAggregate must have "@template-implements IteratorAggregate<?>"'
. ' doc tag with a specified item type or template type',
$element->getLine(),
static::CODE,
$this->reportType,
$originId
);
}
}
}
2 changes: 2 additions & 0 deletions src/Sniffs/CompositeCodeElementSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Gskema\TypeSniff\Core\CodeElement\Element\CodeElementInterface;
use Gskema\TypeSniff\Core\CodeElement\Element\FileElement;
use Gskema\TypeSniff\Sniffs\CodeElement\FqcnDescriptionSniff;
use Gskema\TypeSniff\Sniffs\CodeElement\IteratorItemTypeSniff;
use PHP_CodeSniffer\Files\File;
use Gskema\TypeSniff\Core\CodeElement\CodeElementDetector;
use Gskema\TypeSniff\Sniffs\CodeElement\CodeElementSniffInterface;
Expand Down Expand Up @@ -42,6 +43,7 @@ protected function configure(array $config): void
$config['sniffs'][] = FqcnPropSniff::class;
$config['sniffs'][] = FqcnConstSniff::class;
$config['sniffs'][] = FqcnDescriptionSniff::class;
$config['sniffs'][] = IteratorItemTypeSniff::class;

// CodeElementSniff(s) are saved by their short name, meaning you can't have 2 instances of same sniff.
$rawSniffs = [];
Expand Down
12 changes: 12 additions & 0 deletions tests/Sniffs/CompositeCodeElementSniffTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,18 @@ public static function dataProcess(): array
],
];

// #25
$dataSets[] = [
[
'addViolationId' => false,
'useReflection' => false,
],
__DIR__ . '/fixtures/TestIterator0.php',
[
'009 Classes which implement IteratorAggregate must have "@template-implements IteratorAggregate<?>" doc tag with a specified item type or template type'
],
];

return $dataSets;
}

Expand Down
15 changes: 15 additions & 0 deletions tests/Sniffs/fixtures/TestIterator0.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Gskema\TypeSniff\Sniffs\fixtures;

use ArrayIterator;
use IteratorAggregate;
use Traversable;

class TestIterator0 implements IteratorAggregate
{
public function getIterator(): Traversable
{
return new ArrayIterator([]);
}
}

0 comments on commit e4543fa

Please sign in to comment.