FileTrait   A
last analyzed

Complexity

Total Complexity 8

Size/Duplication

Total Lines 50
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 8
eloc 18
c 1
b 0
f 1
dl 0
loc 50
rs 10

1 Method

Rating   Name   Duplication   Size   Complexity  
B readFileStream() 0 38 8
1
<?php
2
declare(strict_types=1);
3
4
namespace BEdita\ImportTools\Utility;
5
6
use BEdita\Core\Filesystem\FilesystemRegistry;
7
8
/**
9
 * Utilities to help reading files from either the local filesystem or an adapter configured in BEdita.
10
 *
11
 * This provides `readFileStream` method to open "read-only" file stream (you can use local filesystem or adapter).
12
 *
13
 * Usage example:
14
 * ```php
15
 * use BEdita\ImportTools\Utility\FileTrait;
16
 *
17
 * class MyImporter
18
 * {
19
 *     use FileTrait;
20
 *
21
 *     public function read(string $file): void
22
 *     {
23
 *         [$fh, $close] = $this->readFileStream($path);
24
 *
25
 *         try {
26
 *             flock($fh, LOCK_SH);
27
 *             // do your stuff
28
 *         } finally {
29
 *             $close();
30
 *         }
31
 *     }
32
 * }
33
 * ```
34
 */
35
trait FileTrait
36
{
37
    /**
38
     * Open read-only file stream. Possible sources are:
39
     *  - `-` for STDIN
40
     *  - local paths
41
     *  - any URL supported by a registered PHP stream wrapper — notably, remote URLs via HTTP(S)
42
     *  - (if `bedita/core` is available) any mountpoint registered in `FilesystemRegistry`
43
     *
44
     * @param string $path Path to open file from.
45
     * @return array{resource, callable(): void}
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{resource, callable(): void} at position 2 could not be parsed: Expected ':' at position 2, but found 'resource'.
Loading history...
46
     */
47
    protected static function readFileStream(string $path): array
48
    {
49
        /**
50
         * Create a function to close the requested resource.
51
         *
52
         * @param resource|null $fh Resource to be closed.
53
         * @return callable(): void Function to be used for closing the resource.
54
         */
55
        $closerFactory = fn ($resource): callable => function () use ($resource): void {
56
            if (is_resource($resource)) {
57
                fclose($resource);
58
            }
59
        };
60
61
        if ($path === '-') {
62
            return [STDIN, $closerFactory(null)]; // We don't really want to close STDIN.
63
        }
64
65
        if (!str_contains($path, '://') || in_array(explode('://', $path, 2)[0], stream_get_wrappers(), true)) {
66
            try {
67
                $fh = fopen($path, 'rb');
68
                if ($fh === false) {
69
                    trigger_error(sprintf('fopen(%s): falied to open stream', $path), E_USER_ERROR);
70
                }
71
            } catch (\Exception $previous) {
72
                throw new \RuntimeException(sprintf('Cannot open file: %s', $path), 0, $previous);
73
            }
74
75
            return [$fh, $closerFactory($fh)];
76
        }
77
78
        if (!class_exists(FilesystemRegistry::class)) {
79
            trigger_error(sprintf('Unsupported stream wrapper protocol: %s', $path), E_USER_ERROR);
80
        }
81
82
        $fh = FilesystemRegistry::getMountManager()->readStream($path);
83
84
        return [$fh, $closerFactory($fh)];
85
    }
86
}
87