OptimizePdfAction::cleanup()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 6
rs 10
1
<?php
2
3
namespace Mostafaznv\PdfOptimizer\Actions;
4
5
use Mostafaznv\PdfOptimizer\DTOs\OptimizeResult;
6
use Mostafaznv\PdfOptimizer\Laravel\Concerns\Disk;
7
use Mostafaznv\PdfOptimizer\Laravel\Concerns\File;
8
use Mostafaznv\PdfOptimizer\PdfOptimizerLogger;
9
use Psr\Log\LoggerInterface;
10
use Symfony\Component\Process\Process;
11
12
13
class OptimizePdfAction
14
{
15
    private readonly ?File $file;
16
    private readonly ?Disk $outputDisk;
17
18
    private int             $timeout = 60;
19
    private LoggerInterface $logger;
20
21
22
    public function __construct(?File $file = null, ?Disk $outputDisk = null)
23
    {
24
        $this->file = $file;
0 ignored issues
show
Bug introduced by
The property file is declared read-only in Mostafaznv\PdfOptimizer\Actions\OptimizePdfAction.
Loading history...
25
        $this->outputDisk = $outputDisk;
0 ignored issues
show
Bug introduced by
The property outputDisk is declared read-only in Mostafaznv\PdfOptimizer\Actions\OptimizePdfAction.
Loading history...
26
27
        $this->logger(new PdfOptimizerLogger);
28
    }
29
30
    public static function init(?File $file = null, ?Disk $outputDisk = null): self
31
    {
32
        return new self($file, $outputDisk);
33
    }
34
35
36
    public function setTimeout(int $timeout): self
37
    {
38
        $this->timeout = $timeout;
39
40
        return $this;
41
    }
42
43
    public function execute(array $command, string $input, string $output): OptimizeResult
44
    {
45
        $this->logger->info('Start optimizing');
46
47
        $originalOutput = $output;
48
        [$input, $output] = $this->io($input, $output);
49
50
        $command = $this->command($command, $input, $output);
51
52
        $process = new Process($command);
53
        $process->setTimeout($this->timeout);
54
        $process->run();
55
56
        $status = $process->isSuccessful();
57
58
        if ($status) {
59
            $this->store($output, $originalOutput);
60
        }
61
62
        $this->cleanup();
63
        $this->logResult($process);
64
65
        return OptimizeResult::make(
66
            status: $status,
67
            message: !$status ? "error: {$process->getErrorOutput()}" : ''
68
        );
69
    }
70
71
    public function logger(?LoggerInterface $logger): self
72
    {
73
        if ($logger) {
74
            $this->logger = $logger;
75
        }
76
77
        return $this;
78
    }
79
80
    private function io(string $input, string $output): array
81
    {
82
        if ($this->file) {
83
            $input = $this->file->getLocalPath();
84
        }
85
86
        if ($this->outputDisk?->getAdapter()) {
87
            $output = ltrim($output, '/');
88
            $dirname = pathinfo($output, PATHINFO_DIRNAME);
89
90
            $disk = $this->outputDisk->isLocalDisk()
0 ignored issues
show
Bug introduced by
The method isLocalDisk() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

90
            $disk = $this->outputDisk->/** @scrutinizer ignore-call */ isLocalDisk()

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
91
                ? $this->outputDisk->getAdapter()
92
                : $this->outputDisk->getTemporaryDisk();
93
94
95
            if ($dirname != '.' and !$disk->directoryExists($dirname)) {
0 ignored issues
show
Bug introduced by
It seems like $dirname can also be of type array; however, parameter $path of Illuminate\Filesystem\Fi...pter::directoryExists() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

95
            if ($dirname != '.' and !$disk->directoryExists(/** @scrutinizer ignore-type */ $dirname)) {
Loading history...
96
                $disk->makeDirectory($dirname);
0 ignored issues
show
Bug introduced by
It seems like $dirname can also be of type array; however, parameter $path of Illuminate\Filesystem\Fi...dapter::makeDirectory() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

96
                $disk->makeDirectory(/** @scrutinizer ignore-type */ $dirname);
Loading history...
97
            }
98
99
            $output = $disk->path($output);
100
        }
101
102
        $this->logger->info("Input: $input", [
103
            'input' => $input,
104
        ]);
105
106
        $this->logger->info("Output: $output", [
107
            'output' => $output,
108
        ]);
109
110
        return [$input, $output];
111
    }
112
113
    private function command(array $command, string $input, string $output): array
114
    {
115
        if ($output) {
116
            $exists = false;
117
118
            foreach ($command as $value) {
119
                if (str_starts_with($value, '-sOutputFile')) {
120
                    $exists = true;
121
                }
122
            }
123
124
            if (!$exists) {
125
                $command[] = "-sOutputFile=$output";
126
            }
127
        }
128
129
        if ($input) {
130
            $exists = false;
131
132
            foreach ($command as $value) {
133
                if (str_ends_with($value, '.pdf') and !str_starts_with($value, '-sOutputFile')) {
134
                    $exists = true;
135
                }
136
            }
137
138
            if (!$exists) {
139
                $command[] = $input;
140
            }
141
        }
142
143
        $this->logger->info('Command: ' . implode(', ', $command), [
144
            'command' => $command,
145
        ]);
146
147
        return $command;
148
    }
149
150
    private function store(string $output, string $originalOutput): void
151
    {
152
        if ($this->outputDisk?->getAdapter()) {
153
            if (!$this->outputDisk->isLocalDisk()) {
154
                $this->logger->info('Store optimized file to remote disk', [
155
                    'path' => $originalOutput
156
                ]);
157
158
                $this->outputDisk->getAdapter()->writeStream(
159
                    $originalOutput,
160
                    fopen($output, 'r')
161
                );
162
            }
163
        }
164
    }
165
166
    private function cleanup(): void
167
    {
168
        $this->logger->info('Cleanup temporary files');
169
170
        $this->file?->cleanup();
171
        $this->outputDisk?->cleanupTemporaryDirectory();
172
    }
173
174
    private function logResult(Process $process): void
175
    {
176
        if (!$process->isSuccessful()) {
177
            $this->logger->error('Process ended with error', [
178
                'output_log'       => $process->getOutput(),
179
                'error_output_log' => $process->getErrorOutput()
180
            ]);
181
182
            return;
183
        }
184
185
        $this->logger->info('Process successfully ended', [
186
            'output_log' => $process->getOutput()
187
        ]);
188
    }
189
}
190