ReportWriter   A
last analyzed

Complexity

Total Complexity 12

Size/Duplication

Total Lines 104
Duplicated Lines 0 %

Importance

Changes 6
Bugs 1 Features 0
Metric Value
wmc 12
eloc 40
c 6
b 1
f 0
dl 0
loc 104
rs 10

5 Methods

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