1<?php
2/**
3 * Copyright since 2007 PrestaShop SA and Contributors
4 * PrestaShop is an International Registered Trademark & Property of PrestaShop SA
5 *
6 * NOTICE OF LICENSE
7 *
8 * This source file is subject to the Open Software License (OSL 3.0)
9 * that is bundled with this package in the file LICENSE.md.
10 * It is also available through the world-wide-web at this URL:
11 * https://opensource.org/licenses/OSL-3.0
12 * If you did not receive a copy of the license and are unable to
13 * obtain it through the world-wide-web, please send an email
14 * to license@prestashop.com so we can send you a copy immediately.
15 *
16 * DISCLAIMER
17 *
18 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
19 * versions in the future. If you wish to customize PrestaShop for your
20 * needs please refer to https://devdocs.prestashop.com/ for more information.
21 *
22 * @author    PrestaShop SA and Contributors <contact@prestashop.com>
23 * @copyright Since 2007 PrestaShop SA and Contributors
24 * @license   https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
25 */
26
27namespace PrestaShop\PrestaShop\Core\Foundation\Filesystem;
28
29use SplFileInfo;
30
31class FileSystem
32{
33    /**
34     * Default mode for directories
35     */
36    public const DEFAULT_MODE_FOLDER = 0755;
37
38    /**
39     * Default mode for files
40     */
41    public const DEFAULT_MODE_FILE = 0644;
42
43    /**
44     * Replaces directory separators with the system's native one
45     * and trims the trailing separator.
46     */
47    public function normalizePath($path)
48    {
49        return rtrim(
50            str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path),
51            DIRECTORY_SEPARATOR
52        );
53    }
54
55    private function joinTwoPaths($a, $b)
56    {
57        return $this->normalizePath($a) . DIRECTORY_SEPARATOR . $this->normalizePath($b);
58    }
59
60    /**
61     * Joins an arbitrary number of paths, normalizing them along the way.
62     */
63    public function joinPaths()
64    {
65        if (func_num_args() < 2) {
66            throw new Exception('joinPaths requires at least 2 arguments.');
67        }
68        if (func_num_args() === 2) {
69            $arg_O = func_get_arg(0);
70            $arg_1 = func_get_arg(1);
71
72            return $this->joinTwoPaths($arg_O, $arg_1);
73        }
74
75        $func_args = func_get_args();
76        $arg_0 = func_get_arg(0);
77
78        return $this->joinPaths(
79            $arg_0,
80            call_user_func_array(
81                [$this,
82                    'joinPaths', ],
83                array_slice($func_args, 1)
84            )
85        );
86    }
87
88    /**
89     * Performs a depth first listing of directory entries.
90     * Throws exception if $path is not a file.
91     * If $path is a file and not a directory, just gets the file info for it
92     * and return it in an array.
93     *
94     * @param string $path
95     *
96     * @return SplFileInfo[] Array of SplFileInfo object indexed by file path
97     */
98    public function listEntriesRecursively($path)
99    {
100        if (!file_exists($path)) {
101            throw new Exception(sprintf('No such file or directory: %s', $path));
102        }
103
104        if (!is_dir($path)) {
105            throw new Exception(sprintf('%s is not a directory', $path));
106        }
107
108        $entries = [];
109
110        foreach (scandir($path) as $entry) {
111            if ($entry === '.' || $entry === '..') {
112                continue;
113            }
114
115            $newPath = $this->joinPaths($path, $entry);
116            $info = new SplFileInfo($newPath);
117
118            $entries[$newPath] = $info;
119
120            if ($info->isDir()) {
121                $entries = array_merge(
122                    $entries,
123                    $this->listEntriesRecursively($newPath)
124                );
125            }
126        }
127
128        return $entries;
129    }
130
131    /**
132     * Filter used by listFilesRecursively.
133     */
134    private function matchOnlyFiles(SplFileInfo $info)
135    {
136        return $info->isFile();
137    }
138
139    /**
140     * Same as listEntriesRecursively but returns only files.
141     */
142    public function listFilesRecursively($path)
143    {
144        return array_filter(
145            $this->listEntriesRecursively($path),
146            [$this, 'matchOnlyFiles']
147        );
148    }
149}
150