1<?php
2declare(strict_types=1);
3
4namespace ILIAS\Filesystem\Provider\FlySystem;
5
6use ILIAS\Filesystem\FilesystemFacade;
7use ILIAS\Filesystem\Filesystem;
8use ILIAS\Filesystem\Provider\Configuration\LocalConfig;
9use League\Flysystem\Adapter\Local;
10
11/**
12 * Class FlySystemLocalFilesystemFactory
13 *
14 * The local fly system filesystem factory creates instances of the local filesystem adapter which is provided by
15 * the phpleague.
16 *
17 * @author  Nicolas Schäfli <ns@studer-raimann.ch>
18 * @since 5.3
19 */
20final class FlySystemLocalFilesystemFactory
21{
22    const PRIVATE_ACCESS_KEY = 'private';
23    const PUBLIC_ACCESS_KEY = 'public';
24    const FILE_ACCESS_KEY = 'file';
25    const DIRECTORY_ACCESS_KEY = 'dir';
26
27    /**
28     * Creates a new instance of the local filesystem adapter used by fly system.
29     *
30     * @param LocalConfig $config The configuration which should be used to initialise the adapter.
31     *
32     * @return Filesystem
33     */
34    public function getInstance(LocalConfig $config)
35    {
36        $this->validateFileLockMode($config->getLockMode());
37
38        $adapter = new Local(
39            $config->getRootPath(),
40            $config->getLockMode(),
41            $this->mapConfigLinkToLocalLinks($config->getLinkBehaviour()),
42            [
43                self::FILE_ACCESS_KEY => [
44                    self::PRIVATE_ACCESS_KEY => $config->getFileAccessPrivate(),
45                    self::PUBLIC_ACCESS_KEY => $config->getFileAccessPublic()
46                ],
47                self::DIRECTORY_ACCESS_KEY => [
48                    self::PRIVATE_ACCESS_KEY => $config->getDirectoryAccessPrivate(),
49                    self::PUBLIC_ACCESS_KEY => $config->getDirectoryAccessPublic()
50                ]
51            ]
52        );
53
54        //switch the path separator to a forward slash, see Mantis 0022554
55        $reflection = new \ReflectionObject($adapter);
56        $property = $reflection->getProperty("pathSeparator");
57        $property->setAccessible(true);
58        $property->setValue($adapter, '/');
59
60        /* set new path separator in path prefix, the library will replace the old path ending
61           while setting the path prefix.
62        */
63        $adapter->setPathPrefix($adapter->getPathPrefix());
64
65
66        $filesystem = new \League\Flysystem\Filesystem($adapter);
67        $fileAccess = new FlySystemFileAccess($filesystem);
68        $facade = new FilesystemFacade(
69            new FlySystemFileStreamAccess($filesystem),
70            $fileAccess,
71            new FlySystemDirectoryAccess($filesystem, $fileAccess)
72        );
73
74        return $facade;
75    }
76
77
78    /**
79     * Maps a constant of the LocalConfig class into a constant of the Local class.
80     *
81     * Example:
82     *
83     * @param int $configLinkBehaviour The code of the config link behaviour constant.
84     *
85     * @return int The mapped code of the Local filesystem adapter.
86     */
87    private function mapConfigLinkToLocalLinks($configLinkBehaviour)
88    {
89        switch ($configLinkBehaviour) {
90            case LocalConfig::DISALLOW_LINKS:
91                return Local::DISALLOW_LINKS;
92            case LocalConfig::SKIP_LINKS:
93                return Local::SKIP_LINKS;
94            default:
95                throw new \InvalidArgumentException("The supplied value \"$configLinkBehaviour\" is not a valid LocalConfig link behaviour constant.");
96        }
97    }
98
99
100    /**
101     * Checks if the supplied file lock mode is valid.
102     * Valid values are LOCK_SH and LOCK_EX.
103     *
104     * LOCK_SH -> shared lock (read is possible for others)
105     * LOCK_EX -> no access for other processes
106     *
107     * @param int $code The code of the file lock mode which should be checked.
108     *
109     * @see LOCK_SH
110     * @see LOCK_EX
111     */
112    private function validateFileLockMode($code)
113    {
114        if ($code === LOCK_EX || $code === LOCK_SH) {
115            return;
116        }
117
118        throw new \InvalidArgumentException("The supplied value \"$code\" is not a valid file lock mode please check your local file storage configurations.");
119    }
120}
121