Path   A
last analyzed

Complexity

Total Complexity 39

Size/Duplication

Total Lines 175
Duplicated Lines 0 %

Importance

Changes 11
Bugs 1 Features 5
Metric Value
eloc 66
c 11
b 1
f 5
dl 0
loc 175
rs 9.28
wmc 39

14 Methods

Rating   Name   Duplication   Size   Complexity  
A writeFile() 0 9 2
A read() 0 7 2
A createBinaries() 0 7 3
A ensureDir() 0 7 2
A expand() 0 15 5
A readAsJson() 0 3 1
A getExtension() 0 3 1
A findFiles() 0 23 4
A initPhintPath() 0 13 4
A isAbsolute() 0 7 3
A loadClasses() 0 10 2
A getRelativePath() 0 10 3
A join() 0 12 4
A getPhintPath() 0 9 3
1
<?php
2
3
/*
4
 * This file is part of the PHINT package.
5
 *
6
 * (c) Jitendra Adhikari <[email protected]>
7
 *     <https://github.com/adhocore>
8
 *
9
 * Licensed under MIT license.
10
 */
11
12
namespace Ahc\Phint\Util;
13
14
use Ahc\Json\Comment;
15
use Symfony\Component\Finder\Finder;
16
17
class Path
18
{
19
    /** @var string */
20
    protected $phintPath;
21
22
    /**
23
     * Platform agnostic absolute path detection.
24
     *
25
     * @param string $path
26
     *
27
     * @return bool
28
     */
29
    public function isAbsolute(string $path): bool
30
    {
31
        if (\DIRECTORY_SEPARATOR === '\\') {
32
            return \strpos($path, ':') === 1;
33
        }
34
35
        return isset($path[0]) && $path[0] === '/';
36
    }
37
38
    public function getRelativePath(string $fullPath, string ...$basePaths): string
39
    {
40
        foreach ($basePaths as $basePath) {
41
            if (\strpos($fullPath, $basePath) === 0) {
42
                return \substr($fullPath, \strlen($basePath));
43
            }
44
        }
45
46
        // Hmm!
47
        return $fullPath;
48
    }
49
50
    public function ensureDir(string $dir, $mode = 0777): bool
51
    {
52
        if (!\is_dir($dir)) {
53
            return \mkdir($dir, $mode, true);
54
        }
55
56
        return true;
57
    }
58
59
    public function getExtension(string $filePath): string
60
    {
61
        return \pathinfo($filePath, \PATHINFO_EXTENSION);
0 ignored issues
show
Bug Best Practice introduced by
The expression return pathinfo($filePath, PATHINFO_EXTENSION) could return the type array which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
62
    }
63
64
    public function readAsJson(string $filePath, bool $asArray = true)
65
    {
66
        return (new Comment)->decode($this->read($filePath) ?? 'null', $asArray);
67
    }
68
69
    public function read(string $filePath): ?string
70
    {
71
        if (\is_file($filePath)) {
72
            return \file_get_contents($filePath);
73
        }
74
75
        return null;
76
    }
77
78
    public function writeFile(string $file, $content, int $mode = null): bool
79
    {
80
        $this->ensureDir(\dirname($file));
81
82
        if (!\is_string($content)) {
83
            $content = \json_encode($content, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES);
84
        }
85
86
        return \file_put_contents($file, $content, $mode) > 0;
0 ignored issues
show
Bug introduced by
It seems like $mode can also be of type null; however, parameter $flags of file_put_contents() does only seem to accept integer, 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

86
        return \file_put_contents($file, $content, /** @scrutinizer ignore-type */ $mode) > 0;
Loading history...
87
    }
88
89
    public function getPhintPath(string $subpath = ''): string
90
    {
91
        $this->initPhintPath();
92
93
        if ($subpath && $this->phintPath) {
94
            return $this->phintPath . '/' . \ltrim($subpath, '/');
95
        }
96
97
        return $this->phintPath;
98
    }
99
100
    public function createBinaries(array $bins, string $basePath)
101
    {
102
        foreach ($bins as $bin) {
103
            $bin = $this->join($basePath, $bin);
104
105
            if ($this->writeFile($bin, "#!/usr/bin/env php\n<?php\n")) {
106
                @\chmod($bin, 0755);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

106
                /** @scrutinizer ignore-unhandled */ @\chmod($bin, 0755);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
107
            }
108
        }
109
    }
110
111
    public function join(...$paths): string
112
    {
113
        if (\is_array($paths[0] ?? '')) {
114
            $paths = $paths[0];
115
        }
116
117
        $join = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $join is dead and can be removed.
Loading history...
118
        foreach ($paths as $i => &$path) {
119
            $path = $i === 0 ? \rtrim($path, '\\/') : \trim($path, '\\/');
120
        }
121
122
        return \implode('/', $paths);
123
    }
124
125
    public function findFiles(array $inPaths, string $ext, bool $dotfiles = false): array
126
    {
127
        $finder = new Finder;
128
129
        if ($ext !== '*') {
130
            $ext = '.' . \ltrim($ext, '.');
131
            $len = \strlen($ext);
132
133
            $finder->filter(function ($file) use ($ext, $len) {
134
                return \substr($file, -$len) === $ext;
135
            });
136
        }
137
138
        foreach ($inPaths as $path) {
139
            $finder->in($path);
140
        }
141
142
        $files = [];
143
        foreach ($finder->files()->ignoreDotFiles($dotfiles) as $file) {
144
            $files[] = (string) $file;
145
        }
146
147
        return $files;
148
    }
149
150
    public function loadClasses(array $inPaths, array $namespaces, string $ext = 'php'): array
151
    {
152
        foreach ($this->findFiles($inPaths, $ext) as $file) {
153
            _require($file);
154
        }
155
156
        $namespaces = \implode('\|', $namespaces);
157
        $allClasses = \array_merge(\get_declared_interfaces(), \get_declared_classes(), \get_declared_traits());
158
159
        return \preg_grep('~^' . \preg_quote($namespaces) . '~', $allClasses);
160
    }
161
162
    public function expand(string $path, string $from = ''): string
163
    {
164
        if ($path === '.') {
165
            return $from;
166
        }
167
168
        if ($path[0] === '~') {
169
            return \str_replace('~', \getenv('HOME'), $path);
170
        }
171
172
        if (\strlen($from) > 0 && !$this->isAbsolute($path)) {
173
            return $this->join($from, $path);
174
        }
175
176
        return $path;
177
    }
178
179
    protected function initPhintPath()
180
    {
181
        if (null !== $this->phintPath) {
182
            return;
183
        }
184
185
        $this->phintPath = '';
186
187
        if (false !== $home = ($_SERVER['HOME'] ?? \getenv('HOME'))) {
188
            $path = \rtrim($home, '/') . '/.phint';
189
190
            if ($this->ensureDir($path)) {
191
                $this->phintPath = $path;
192
            }
193
        }
194
    }
195
}
196
197
/**
198
 * Isolated file require.
199
 *
200
 * @param string $file
201
 *
202
 * @return void
203
 */
204
function _require(string $file)
205
{
206
    require_once $file;
207
}
208