Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions src/ValueObject/PatternCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

namespace Rector\Behastan\ValueObject;

use InvalidArgumentException;
use Rector\Behastan\ValueObject\Pattern\AbstractPattern;
use Rector\Behastan\ValueObject\Pattern\ExactPattern;
use Rector\Behastan\ValueObject\Pattern\RegexPattern;

final readonly class PatternCollection
{
Expand Down Expand Up @@ -38,6 +41,16 @@ public function all(): array
return $this->patterns;
}

/**
* @return string[]
*/
public function exactPatternStrings(): array
{
$exactPatterns = $this->byType(ExactPattern::class);

return array_map(fn (ExactPattern $exactPattern): string => $exactPattern->pattern, $exactPatterns);
}

/**
* @template TPattern as AbstractPattern
*
Expand All @@ -48,4 +61,42 @@ public function byType(string $type): array
{
return array_filter($this->patterns, fn (AbstractPattern $pattern): bool => $pattern instanceof $type);
}

public function regexPatternString(): string
{
$regexPatterns = $this->byType(RegexPattern::class);

$regexPatternStrings = array_map(
fn (RegexPattern $regexPattern): string => $regexPattern->pattern,
$regexPatterns
);

return $this->combineRegexes($regexPatternStrings, '#');
}

/**
* @param string[] $regexes Like ['/foo/i', '~bar\d+~', '#baz#u']
*/
private function combineRegexes(array $regexes, string $delimiter = '#'): string
{
$parts = [];

foreach ($regexes as $regex) {
// Very common case: regex is given like "/pattern/flags"
// Parse: delimiter + pattern + delimiter + flags
if (! preg_match('~^(.)(.*)\\1([a-zA-Z]*)$~s', $regex, $m)) {
throw new InvalidArgumentException('Invalid regex: ' . $regex);
}

$pattern = $m[2];
$flags = $m[3];

// If you truly have mixed flags per-regex, you can't naively merge them.
// Best practice: normalize flags beforehand (same for all).
// We'll ignore per-regex flags here and let the caller decide final flags.
$parts[] = '(?:' . $pattern . ')';
}

return $delimiter . '(?:' . implode('|', $parts) . ')' . $delimiter;
}
}
37 changes: 37 additions & 0 deletions tests/ValueObject/PatternCollectionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace Rector\Behastan\Tests\ValueObject;

use PHPUnit\Framework\TestCase;
use Rector\Behastan\ValueObject\Pattern\ExactPattern;
use Rector\Behastan\ValueObject\Pattern\NamedPattern;
use Rector\Behastan\ValueObject\Pattern\RegexPattern;
use Rector\Behastan\ValueObject\PatternCollection;

final class PatternCollectionTest extends TestCase
{
public function testExactPatterns(): void
{
$patternCollection = new PatternCollection([
new ExactPattern('pattern1', 'file1.php', 10, 'SomeClass', 'someMethod'),
new ExactPattern('pattern2', 'file2.php', 20, 'AnotherClass', 'anotherMethod'),
new NamedPattern('this is :me', 'file1.php', 10, 'SomeClass', 'someMethod'),
]);

$this->assertSame(['pattern1', 'pattern2'], $patternCollection->exactPatternStrings());
}

public function testRegexPatterns(): void
{
$patternCollection = new PatternCollection([
new ExactPattern('pattern1', 'file1.php', 10, 'SomeClass', 'someMethod'),
new RegexPattern('#this is it#', 'file1.php', 10, 'SomeClass', 'someMethod'),
new RegexPattern('#here is more#', 'file1.php', 10, 'SomeClass', 'someMethod'),
]);

$this->assertSame('#(?:(?:this is it)|(?:here is more))#', $patternCollection->regexPatternString());

}
}