Completed
Pull Request — master (#132)
by
unknown
06:43 queued 04:56
created

TargetLocator::getRelativePathFromContext()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
/*
4
 * This file is part of Zippy.
5
 *
6
 * (c) Alchemy <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Alchemy\Zippy\Resource;
13
14
use Alchemy\Zippy\Exception\TargetLocatorException;
15
16
class TargetLocator
17
{
18
    /**
19
     * Locates the target for a resource in a context
20
     *
21
     * For example, adding /path/to/file where the context (current working
22
     * directory) is /path/to will return `file` as target
23
     *
24
     * @param string          $context
25
     * @param string|resource $resource
26
     *
27
     * @return string
28
     *
29
     * @throws TargetLocatorException when the resource is invalid
30
     */
31
    public function locate($context, $resource)
32
    {
33
        switch (true) {
34
            case is_resource($resource):
35
                return $this->locateResource($resource);
36
            case is_string($resource):
37
                return $this->locateString($context, $resource);
38
            case $resource instanceof \SplFileInfo:
39
                return $this->locateString($context, $resource->getRealPath());
40
            default:
41
                throw new TargetLocatorException($resource, 'Unknown resource format');
42
        }
43
    }
44
45
    /**
46
     * Locate the target for a resource.
47
     *
48
     * @param resource $resource
49
     *
50
     * @return string
51
     *
52
     * @throws TargetLocatorException
53
     */
54
    private function locateResource($resource)
55
    {
56
        $meta = stream_get_meta_data($resource);
57
        $data = parse_url($meta['uri']);
58
59
        if (!isset($data['path'])) {
60
            throw new TargetLocatorException($resource, 'Unable to retrieve path from resource');
61
        }
62
63
        return PathUtil::basename($data['path']);
64
    }
65
66
    /**
67
     * Locate the target for a string.
68
     *
69
     * @param        $context
70
     * @param string $resource
71
     *
72
     * @return string
73
     *
74
     * @throws TargetLocatorException
75
     */
76
    private function locateString($context, $resource)
77
    {
78
        $url = parse_url($resource);
79
80
        if (isset($url['scheme']) && $this->isLocalFilesystem($url['scheme'])) {
81
            $resource = $url['path'] = $this->cleanupPath($url['path']);
82
        }
83
84
        // resource is a URI
85
        if (isset($url['scheme'])) {
86
            if ($this->isLocalFilesystem($url['scheme']) && $this->isFileInContext($url['path'], $context)) {
87
                return $this->getRelativePathFromContext($url['path'], $context);
88
            }
89
90
            return PathUtil::basename($resource);
91
        }
92
93
        // resource is a local path
94
        if ($this->isFileInContext($resource, $context)) {
95
            $resource = $this->cleanupPath($resource);
96
97
            return $this->getRelativePathFromContext($resource, $context);
98
        } else {
99
            return PathUtil::basename($resource);
100
        }
101
    }
102
103
    /**
104
     * Removes backward path sequences (..)
105
     *
106
     * @param string $path
107
     *
108
     * @return string
109
     *
110
     * @throws TargetLocatorException In case the path is invalid
111
     */
112
    private function cleanupPath($path)
113
    {
114
        if (false === $cleanPath = realpath($path)) {
115
            throw new TargetLocatorException($path, sprintf('%s is an invalid location', $path));
116
        }
117
118
        return $cleanPath;
119
    }
120
121
    /**
122
     * Checks whether the path belong to the context
123
     *
124
     * @param string $path A resource path
125
     * @param string $context
126
     *
127
     * @return bool
128
     */
129
    private function isFileInContext($path, $context)
130
    {
131
        return 0 === strpos($path, $context);
132
    }
133
134
    /**
135
     * Gets the relative path from the context for the given path
136
     *
137
     * @param string $path A resource path
138
     * @param string $context
139
     *
140
     * @return string
141
     */
142
    private function getRelativePathFromContext($path, $context)
143
    {
144
        return ltrim(str_replace($context, '', $path), '/\\');
145
    }
146
147
    /**
148
     * Checks if a scheme refers to a local filesystem
149
     *
150
     * @param string $scheme
151
     *
152
     * @return bool
153
     */
154
    private function isLocalFilesystem($scheme)
155
    {
156
        return 'plainfile' === $scheme || 'file' === $scheme;
157
    }
158
}
159