Skip to content

Commit

Permalink
feat: added PHP factories
Browse files Browse the repository at this point in the history
  • Loading branch information
petrknap committed Nov 17, 2024
1 parent 8d96dd7 commit 4f0ac6d
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 9 deletions.
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Its primary role is to **facilitate filtering operations within a pipeline**,
allowing for easy chaining and execution of executable filters.

```php
namespace PetrKnap\ExternalFilter;
use PetrKnap\ExternalFilter\Filter;

# echo "H4sIAAAAAAAAA0tJLEkEAGPz860EAAAA" | base64 --decode | gzip --decompress
echo Filter::new('base64', ['--decode'])
Expand All @@ -16,7 +16,7 @@ echo Filter::new('base64', ['--decode'])
If you want to process external data, redirect output or get errors, you can use input, output or error streams.

```php
namespace PetrKnap\ExternalFilter;
use PetrKnap\ExternalFilter\Filter;

$errorStream = fopen('php://memory', 'w+');

Expand All @@ -30,6 +30,15 @@ echo stream_get_contents($errorStream);
fclose($errorStream);
```

If you want to call PHP code in the pipeline, you can via factory.

```php
use PetrKnap\ExternalFilter\Filter;

echo Filter::new(phpSnippet: 'echo stream_get_contents(STDIN);')
->filter(b'data');
```

---

Run `composer require petrknap/external-filter` to install it.
Expand Down
48 changes: 43 additions & 5 deletions src/Filter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace PetrKnap\ExternalFilter;

use BadMethodCallException;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

Expand All @@ -25,12 +26,36 @@ public function __construct(
}

/**
* @param non-empty-string $command
* @note if you are not creating command filter use named arguments
*
* @param non-empty-string|null $command
* @param array<non-empty-string>|null $options
* @param non-empty-string|null $phpFile path to PHP file which consumes {@see STDIN}

Check notice on line 33 in src/Filter.php

View workflow job for this annotation

GitHub Actions / run

* [Line length] Line exceeds 80 characters; contains 89 characters
* @param non-empty-string|null $phpSnippet PHP snippet which consumes {@see STDIN}
*/
public static function new(string $command, array|null $options = null): PipelinableFilter
{
return new Filter($command, $options);
public static function new(
string|null $command = null,
array|null $options = null,
# PHP
string|null $phpFile = null,
string|null $phpSnippet = null,
): PipelinableFilter {
$arguments = func_get_args();
if ($command !== null && self::isNullArray($arguments, 0, 1)) {
return new Filter($command, $options);
}

if ($phpFile !== null && self::isNullArray($arguments, 2)) {
return new Filter('php', ['-f', $phpFile]);
}

if ($phpSnippet !== null && self::isNullArray($arguments, 3)) {
/** @var non-empty-string $phpSnippet */
$phpSnippet = (string) preg_replace('/^<\?php\s+/i', '', $phpSnippet);
return new Filter('php', ['-r', $phpSnippet]);
}

throw new BadMethodCallException(__METHOD__ . ' requires valid combination of arguments');
}

/**
Expand Down Expand Up @@ -120,12 +145,25 @@ private static function transformPipeline(array $pipeline): array
...($filter->options ?? []),
]))->getCommandLine();
} else {
throw new \BadMethodCallException('$pipeline contains unsupported filter');
throw new BadMethodCallException('$pipeline contains unsupported filter');
}
}
if ($shellCommandLine !== null) {
$transformedPipeline[] = Process::fromShellCommandline($shellCommandLine);
}
return $transformedPipeline;
}

/**
* @param array<int, mixed> $values

Check notice on line 158 in src/Filter.php

View workflow job for this annotation

GitHub Actions / run

* [Disallow mixed type hint] Usage of "mixed" type hint is disallowed.
*/
private static function isNullArray(array $values, int ...$exceptIndices): bool
{
foreach ($values as $index => $value) {
if ($value !== null && !in_array($index, $exceptIndices, strict: true)) {
return false;
}
}
return true;
}
}
22 changes: 20 additions & 2 deletions tests/FilterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,33 @@

namespace PetrKnap\ExternalFilter;

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

final class FilterTest extends TestCase
{
public function testFactoryWorks()
private const INPUT = b'input';

#[DataProvider('dataFactoryWorks')]
public function testFactoryWorks(array $arguments): void
{
$filter = Filter::new(...$arguments);

self::assertInstanceOf(
Filter::class,
Filter::new(command: 'php'),
$filter,
);

self::assertSame(self::INPUT, $filter->filter(self::INPUT));
}

public static function dataFactoryWorks(): array
{
return [
'command' => [['command' => 'cat']],
'PHP file' => [['phpFile' => __DIR__ . '/Some/filter.php']],
'PHP snippet' => [['phpSnippet' => 'fputs(STDOUT, fgets(STDIN));']],
'PHP snippet (prefixed)' => [['phpSnippet' => '<?php fputs(STDOUT, fgets(STDIN));']],
];
}
}
1 change: 1 addition & 0 deletions tests/ReadmeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public static function getExpectedOutputsOfPhpExamples(): iterable
return [
'pipeline' => 'data',
'error-stream' => 'error',
'filter-factory' => 'data',
];
}
}
6 changes: 6 additions & 0 deletions tests/Some/filter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env php
<?php

declare(strict_types=1);

echo stream_get_contents(STDIN);

0 comments on commit 4f0ac6d

Please sign in to comment.