1<?php
2
3/**
4 * @see       https://github.com/laminas/laminas-filter for the canonical source repository
5 * @copyright https://github.com/laminas/laminas-filter/blob/master/COPYRIGHT.md
6 * @license   https://github.com/laminas/laminas-filter/blob/master/LICENSE.md New BSD License
7 */
8
9namespace Laminas\Filter;
10
11use Laminas\Stdlib\ErrorHandler;
12use Traversable;
13
14class RealPath extends AbstractFilter
15{
16    /**
17     * @var array $options
18     */
19    protected $options = [
20        'exists' => true
21    ];
22
23    /**
24     * Class constructor
25     *
26     * @param  bool|Traversable $existsOrOptions Options to set
27     */
28    public function __construct($existsOrOptions = true)
29    {
30        if ($existsOrOptions !== null) {
31            if (! static::isOptions($existsOrOptions)) {
32                $this->setExists($existsOrOptions);
33            } else {
34                $this->setOptions($existsOrOptions);
35            }
36        }
37    }
38
39    /**
40     * Sets if the path has to exist
41     * TRUE when the path must exist
42     * FALSE when not existing paths can be given
43     *
44     * @param  bool $flag Path must exist
45     * @return self
46     */
47    public function setExists($flag = true)
48    {
49        $this->options['exists'] = (bool) $flag;
50        return $this;
51    }
52
53    /**
54     * Returns true if the filtered path must exist
55     *
56     * @return bool
57     */
58    public function getExists()
59    {
60        return $this->options['exists'];
61    }
62
63    /**
64     * Defined by Laminas\Filter\FilterInterface
65     *
66     * Returns realpath($value)
67     *
68     * If the value provided is non-scalar, the value will remain unfiltered
69     *
70     * @param  string $value
71     * @return string|mixed
72     */
73    public function filter($value)
74    {
75        if (! is_string($value)) {
76            return $value;
77        }
78        $path = (string) $value;
79
80        if ($this->options['exists']) {
81            return realpath($path);
82        }
83
84        ErrorHandler::start();
85        $realpath = realpath($path);
86        ErrorHandler::stop();
87        if ($realpath) {
88            return $realpath;
89        }
90
91        $drive = '';
92        if (stripos(PHP_OS, 'WIN') === 0) {
93            $path = preg_replace('/[\\\\\/]/', DIRECTORY_SEPARATOR, $path);
94            if (preg_match('/([a-zA-Z]\:)(.*)/', $path, $matches)) {
95                list(, $drive, $path) = $matches;
96            } else {
97                $cwd   = getcwd();
98                $drive = substr($cwd, 0, 2);
99                if (strpos($path, DIRECTORY_SEPARATOR) !== 0) {
100                    $path = substr($cwd, 3) . DIRECTORY_SEPARATOR . $path;
101                }
102            }
103        } elseif (strpos($path, DIRECTORY_SEPARATOR) !== 0) {
104            $path = getcwd() . DIRECTORY_SEPARATOR . $path;
105        }
106
107        $stack = [];
108        $parts = explode(DIRECTORY_SEPARATOR, $path);
109        foreach ($parts as $dir) {
110            if ($dir !== '' && $dir !== '.') {
111                if ($dir === '..') {
112                    array_pop($stack);
113                } else {
114                    $stack[] = $dir;
115                }
116            }
117        }
118
119        return $drive . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $stack);
120    }
121}
122