From c0ec819d32847e05d1cc7377a22f836a59a8ba02 Mon Sep 17 00:00:00 2001 From: Nicolas Joubert Date: Tue, 8 Jul 2025 18:16:03 +0200 Subject: [PATCH] #172 Add spl_file_object_flags and json_flags options on JsonStream*Tasks. Update docs. --- .../tasks/json_stream_reader_task.md | 10 +++++++- .../tasks/json_stream_writer_task.md | 12 +++++++--- src/Filesystem/JsonStreamFile.php | 24 +++++++++++++++---- .../File/JsonStream/JsonStreamReaderTask.php | 22 +++++++++++++++-- .../File/JsonStream/JsonStreamWriterTask.php | 15 ++++++++++-- 5 files changed, 70 insertions(+), 13 deletions(-) diff --git a/docs/reference/tasks/json_stream_reader_task.md b/docs/reference/tasks/json_stream_reader_task.md index e48da640..77fc2329 100644 --- a/docs/reference/tasks/json_stream_reader_task.md +++ b/docs/reference/tasks/json_stream_reader_task.md @@ -23,7 +23,11 @@ Underlying method are [SplFileObject::fgets](https://www.php.net/manual/fr/splfi Options ------- -none +| Code | Type | Required | Default | Description | +|-------------------------|-----------------|:--------:|---------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `spl_file_object_flags` | `array`, `null` | | `\SplFileObject::DROP_NEW_LINE \SplFileObject::READ_AHEAD \SplFileObject::SKIP_EMPTY` | Flags to pass to `SplFileObject` constructor, can be empty.
See [PHP documentation](https://www.php.net/manual/en/splfileobject.construct.php) for more information on available flags. | +| `json_flags` | `array`, `null` | | `\JSON_THROW_ON_ERROR` | Flags to pass to `json_encode` function, can be empty.
See [PHP documentation](https://www.php.net/manual/en/function.json-encode.php) for more information on available flags. | + Example ------- @@ -38,6 +42,10 @@ entry: file_path: '%kernel.project_dir%/var/data/json_stream_reader.json' read: service: '@CleverAge\ProcessBundle\Task\File\JsonStream\JsonStreamReaderTask' + options: + spl_file_object_flags: [] + json_flags: + - !php/const JSON_ERROR_NONE ``` diff --git a/docs/reference/tasks/json_stream_writer_task.md b/docs/reference/tasks/json_stream_writer_task.md index 79fd2e39..9b4a2302 100644 --- a/docs/reference/tasks/json_stream_writer_task.md +++ b/docs/reference/tasks/json_stream_writer_task.md @@ -23,9 +23,11 @@ Possible outputs Options ------- -| Code | Type | Required | Default | Description | -|-------------|----------|:--------:|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `file_path` | `string` | **X** | | Path of the file to write to (relative to symfony root or absolute).
It can also take placeholders (`{date}`, `{date_time}`, `{timestamp}` `{unique_token}`) to insert data into the filename | +| Code | Type | Required | Default | Description | +|-------------------------|-----------------|:--------:|---------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `file_path` | `string` | **X** | | Path of the file to write to (relative to symfony root or absolute).
It can also take placeholders (`{date}`, `{date_time}`, `{timestamp}` `{unique_token}`) to insert data into the filename | +| `spl_file_object_flags` | `array`, `null` | | `\SplFileObject::DROP_NEW_LINE \SplFileObject::READ_AHEAD \SplFileObject::SKIP_EMPTY` | Flags to pass to `SplFileObject` constructor, can be empty.
See [PHP documentation](https://www.php.net/manual/en/splfileobject.construct.php) for more information on available flags. | +| `json_flags` | `array`, `null` | | `\JSON_THROW_ON_ERROR` | Flags to pass to `json_encode` function, can be empty.
See [PHP documentation](https://www.php.net/manual/en/function.json-encode.php) for more information on available flags. | Example ---------------- @@ -50,4 +52,8 @@ write: service: '@CleverAge\ProcessBundle\Task\File\JsonStream\JsonStreamWriterTask' options: file_path: '%kernel.project_dir%/var/data/json_stream_writer_{date_time}.csv' + spl_file_object_flags: [] + json_flags: + - !php/const JSON_PRETTY_PRINT + - !php/const JSON_UNESCAPED_SLASHES ``` diff --git a/src/Filesystem/JsonStreamFile.php b/src/Filesystem/JsonStreamFile.php index 6d4873b3..fc76dc62 100644 --- a/src/Filesystem/JsonStreamFile.php +++ b/src/Filesystem/JsonStreamFile.php @@ -20,16 +20,30 @@ class JsonStreamFile implements FileStreamInterface, WritableFileInterface { protected \SplFileObject $file; + private readonly int $jsonFlags; + protected ?int $lineCount = null; protected int $lineNumber = 1; - public function __construct(string $filename, string $mode = 'rb') - { + public function __construct( + string $filename, + string $mode = 'rb', + ?array $splFileObjectFlags = null, + ?array $jsonFlags = null, + ) { $this->file = new \SplFileObject($filename, $mode); // Useful to skip empty trailing lines (doesn't work well on PHP 8, see readLine() code) - $this->file->setFlags(\SplFileObject::DROP_NEW_LINE | \SplFileObject::READ_AHEAD | \SplFileObject::SKIP_EMPTY); + $this->file->setFlags(null !== $splFileObjectFlags + ? array_sum($splFileObjectFlags) + : \SplFileObject::DROP_NEW_LINE | \SplFileObject::READ_AHEAD | \SplFileObject::SKIP_EMPTY + ); + + $this->jsonFlags = null !== $jsonFlags + ? array_sum($jsonFlags) + : \JSON_THROW_ON_ERROR + ; } /** @@ -78,12 +92,12 @@ public function readLine(?int $length = null): ?array } ++$this->lineNumber; - return json_decode($rawLine, true, 512, \JSON_THROW_ON_ERROR); + return json_decode($rawLine, true, 512, $this->jsonFlags); } public function writeLine(array $fields): int { - $this->file->fwrite(json_encode($fields, \JSON_THROW_ON_ERROR).\PHP_EOL); + $this->file->fwrite(json_encode($fields, $this->jsonFlags).\PHP_EOL); ++$this->lineNumber; return $this->lineNumber; diff --git a/src/Task/File/JsonStream/JsonStreamReaderTask.php b/src/Task/File/JsonStream/JsonStreamReaderTask.php index 8db6fc26..e1924b0f 100644 --- a/src/Task/File/JsonStream/JsonStreamReaderTask.php +++ b/src/Task/File/JsonStream/JsonStreamReaderTask.php @@ -14,17 +14,25 @@ namespace CleverAge\ProcessBundle\Task\File\JsonStream; use CleverAge\ProcessBundle\Filesystem\JsonStreamFile; +use CleverAge\ProcessBundle\Model\AbstractConfigurableTask; use CleverAge\ProcessBundle\Model\IterableTaskInterface; use CleverAge\ProcessBundle\Model\ProcessState; +use Symfony\Component\OptionsResolver\OptionsResolver; -class JsonStreamReaderTask implements IterableTaskInterface +class JsonStreamReaderTask extends AbstractConfigurableTask implements IterableTaskInterface { protected ?JsonStreamFile $file = null; public function execute(ProcessState $state): void { if (!$this->file instanceof JsonStreamFile) { - $this->file = new JsonStreamFile($this->getFilePath($state), 'rb'); + $options = $this->getOptions($state); + $this->file = new JsonStreamFile( + $this->getFilePath($state), + 'rb', + $options['spl_file_object_flags'], + $options['json_flags'], + ); } $line = $this->file->readLine(); @@ -49,4 +57,14 @@ protected function getFilePath(ProcessState $state): string { return $state->getInput(); } + + protected function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'spl_file_object_flags' => null, + 'json_flags' => null, + ]); + $resolver->setAllowedTypes('spl_file_object_flags', ['array', 'null']); + $resolver->setAllowedTypes('json_flags', ['array', 'null']); + } } diff --git a/src/Task/File/JsonStream/JsonStreamWriterTask.php b/src/Task/File/JsonStream/JsonStreamWriterTask.php index 494cc0ed..c57446b5 100644 --- a/src/Task/File/JsonStream/JsonStreamWriterTask.php +++ b/src/Task/File/JsonStream/JsonStreamWriterTask.php @@ -26,9 +26,14 @@ class JsonStreamWriterTask extends AbstractConfigurableTask implements BlockingT public function execute(ProcessState $state): void { - $options = $this->getOptions($state); if (!$this->file instanceof JsonStreamFile) { - $this->file = new JsonStreamFile($options['file_path'], 'wb'); + $options = $this->getOptions($state); + $this->file = new JsonStreamFile( + $options['file_path'], + 'wb', + $options['spl_file_object_flags'], + $options['json_flags'], + ); } $input = $state->getInput(); @@ -60,5 +65,11 @@ protected function configureOptions(OptionsResolver $resolver): void ] ) ); + $resolver->setDefaults([ + 'spl_file_object_flags' => null, + 'json_flags' => null, + ]); + $resolver->setAllowedTypes('spl_file_object_flags', ['array', 'null']); + $resolver->setAllowedTypes('json_flags', ['array', 'null']); } }