4 * This file is part of Zippy.
6 * (c) Alchemy <info@alchemy.fr>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Alchemy\Zippy\Resource;
14 use Alchemy\Zippy\Exception\TargetLocatorException;
19 * Locates the target for a resource in a context
21 * For example, adding /path/to/file where the context (current working
22 * directory) is /path/to will return `file` as target
24 * @param string $context
25 * @param string|resource $resource
29 * @throws TargetLocatorException when the resource is invalid
31 public function locate($context, $resource)
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());
41 throw new TargetLocatorException($resource, 'Unknown resource format');
46 * Locate the target for a resource.
48 * @param resource $resource
52 * @throws TargetLocatorException
54 private function locateResource($resource)
56 $meta = stream_get_meta_data($resource);
57 $data = parse_url($meta['uri']);
59 if (!isset($data['path'])) {
60 throw new TargetLocatorException($resource, 'Unable to retrieve path from resource');
63 return PathUtil::basename($data['path']);
67 * Locate the target for a string.
70 * @param string $resource
74 * @throws TargetLocatorException
76 private function locateString($context, $resource)
78 $url = parse_url($resource);
80 if (isset($url['scheme']) && $this->isLocalFilesystem($url['scheme'])) {
81 $resource = $url['path'] = $this->cleanupPath($url['path']);
85 if (isset($url['scheme'])) {
86 if ($this->isLocalFilesystem($url['scheme']) && $this->isFileInContext($url['path'], $context)) {
87 return $this->getRelativePathFromContext($url['path'], $context);
90 return PathUtil::basename($resource);
93 // resource is a local path
94 if ($this->isFileInContext($resource, $context)) {
95 $resource = $this->cleanupPath($resource);
97 return $this->getRelativePathFromContext($resource, $context);
99 return PathUtil::basename($resource);
104 * Removes backward path sequences (..)
106 * @param string $path
110 * @throws TargetLocatorException In case the path is invalid
112 private function cleanupPath($path)
114 if (false === $cleanPath = realpath($path)) {
115 throw new TargetLocatorException($path, sprintf('%s is an invalid location', $path));
122 * Checks whether the path belong to the context
124 * @param string $path A resource path
125 * @param string $context
129 private function isFileInContext($path, $context)
131 return 0 === strpos($path, $context);
135 * Gets the relative path from the context for the given path
137 * @param string $path A resource path
138 * @param string $context
142 private function getRelativePathFromContext($path, $context)
144 return ltrim(str_replace($context, '', $path), '/\\');
148 * Checks if a scheme refers to a local filesystem
150 * @param string $scheme
154 private function isLocalFilesystem($scheme)
156 return 'plainfile' === $scheme || 'file' === $scheme;