Passed
Push — master ( 521834...9d2094 )
by Joachim
05:03
created

ReportWriter::write()   B

Complexity

Conditions 7
Paths 11

Size

Total Lines 55
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 30
c 2
b 0
f 0
dl 0
loc 55
rs 8.5066
cc 7
nc 11
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Setono\SyliusStockMovementPlugin\Writer;
6
7
use InvalidArgumentException;
8
use League\Flysystem\FilesystemInterface;
9
use RuntimeException;
10
use Safe\Exceptions\FilesystemException;
11
use Safe\Exceptions\OutcontrolException;
12
use Safe\Exceptions\StringsException;
13
use function Safe\fopen;
14
use function Safe\fwrite;
15
use function Safe\ob_end_clean;
16
use function Safe\sprintf;
17
use Setono\SyliusStockMovementPlugin\Model\ReportInterface;
18
use Setono\SyliusStockMovementPlugin\Resolver\ReportPathResolverInterface;
19
use Throwable;
20
use Twig\Environment;
21
use Twig\Error\LoaderError;
22
use Twig\Error\RuntimeError;
23
use Twig\Error\SyntaxError;
24
use function Safe\fclose;
25
26
class ReportWriter implements ReportWriterInterface
27
{
28
    /** @var Environment */
29
    private $twig;
30
31
    /** @var FilesystemInterface */
32
    private $filesystem;
33
34
    /** @var ReportPathResolverInterface */
35
    private $reportPathResolver;
36
37
    public function __construct(Environment $twig, FilesystemInterface $filesystem, ReportPathResolverInterface $reportPathResolver)
38
    {
39
        $this->filesystem = $filesystem;
40
        $this->twig = $twig;
41
        $this->reportPathResolver = $reportPathResolver;
42
    }
43
44
    /**
45
     * @throws FilesystemException
46
     * @throws OutcontrolException
47
     * @throws StringsException
48
     * @throws Throwable
49
     * @throws LoaderError
50
     * @throws RuntimeError
51
     * @throws SyntaxError
52
     */
53
    public function write(ReportInterface $report): string
54
    {
55
        $key = $this->reportPathResolver->resolve($report);
56
        // todo should we provide some kind of 'overwrite' parameter?
57
        if ($this->filesystem->has($key)) {
58
            return $key;
59
        }
60
61
        $reportConfiguration = $report->getReportConfiguration();
62
        if (null === $reportConfiguration) {
63
            throw new RuntimeException(sprintf('No report configuration associated with report %s', $report->getId()));
64
        }
65
66
        $template = $this->twig->load($reportConfiguration->getTemplate());
67
68
        $definedBlocks = $template->getBlockNames();
69
        foreach (['extension', 'body'] as $block) {
70
            if (!in_array($block, $definedBlocks, true)) {
71
                throw new InvalidArgumentException(sprintf(
72
                    'The block "%s" is not present in the defined blocks ["%s"] of your template %s',
73
                    $block,
74
                    implode('", "', $definedBlocks),
75
                    $reportConfiguration->getTemplate()
76
                )); // todo better exception
77
            }
78
        }
79
80
        $path = $this->generateTempPath();
81
82
        $fp = fopen($path, 'w+b'); // needs to be w+ since we use the same stream later to read from
83
84
        ob_start(static function ($buffer) use ($fp) {
85
            fwrite($fp, $buffer);
86
        }, 1024);
87
88
        $template->displayBlock('body', [
89
            'stockMovements' => $report->getStockMovements(),
90
        ]);
91
92
        ob_end_clean();
93
94
        $res = $this->filesystem->writeStream($key, $fp);
95
96
        try {
97
            // tries to close the file pointer although it may already have been closed by flysystem
98
            fclose($fp);
99
        } catch (FilesystemException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
100
101
        }
102
103
        if(false === $res) {
104
            throw new RuntimeException(sprintf('An error occurred when trying to write the report %s', $key));
105
        }
106
107
        return $key;
108
    }
109
110
    private function generateTempPath(): string
111
    {
112
        do {
113
            $path = sys_get_temp_dir() . '/' . uniqid('stock-movement-report-', true);
114
        } while (file_exists($path));
115
116
        return $path;
117
    }
118
}
119