Completed
Pull Request — master (#163)
by
unknown
01:42
created

Zippy   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 193
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 13
dl 0
loc 193
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A create() 0 14 3
A open() 0 14 3
A addStrategy() 0 16 3
A getStrategies() 0 4 1
A getAdapterFor() 0 18 6
A load() 0 15 1
A sanitizeExtension() 0 4 1
A guessAdapterExtension() 0 12 3
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;
13
14
use Alchemy\Zippy\Adapter\AdapterContainer;
15
use Alchemy\Zippy\Adapter\AdapterInterface;
16
use Alchemy\Zippy\Archive\ArchiveInterface;
17
use Alchemy\Zippy\Exception\ExceptionInterface;
18
use Alchemy\Zippy\Exception\FormatNotSupportedException;
19
use Alchemy\Zippy\Exception\NoAdapterOnPlatformException;
20
use Alchemy\Zippy\Exception\RuntimeException;
21
use Alchemy\Zippy\FileStrategy\FileStrategyInterface;
22
use Alchemy\Zippy\FileStrategy\TarBz2FileStrategy;
23
use Alchemy\Zippy\FileStrategy\TarFileStrategy;
24
use Alchemy\Zippy\FileStrategy\TarGzFileStrategy;
25
use Alchemy\Zippy\FileStrategy\TB2FileStrategy;
26
use Alchemy\Zippy\FileStrategy\TBz2FileStrategy;
27
use Alchemy\Zippy\FileStrategy\TGzFileStrategy;
28
use Alchemy\Zippy\FileStrategy\ZipFileStrategy;
29
30
class Zippy
31
{
32
    /**
33
     * @var AdapterContainer
34
     */
35
    public $adapters;
36
37
    /**
38
     * @var FileStrategyInterface[][]
39
     */
40
    private $strategies = array();
41
42
    public function __construct(AdapterContainer $adapters)
43
    {
44
        $this->adapters = $adapters;
45
    }
46
47
    /**
48
     * Creates an archive
49
     *
50
     * @param string                         $path
51
     * @param string|array|\Traversable|null $files
52
     * @param bool                           $recursive
53
     * @param string|null                    $type
54
     *
55
     * @return ArchiveInterface
56
     *
57
     * @throws RuntimeException In case of failure
58
     */
59
    public function create($path, $files = null, $recursive = true, $type = null)
60
    {
61
        if (null === $type) {
62
            $type = $this->guessAdapterExtension($path);
63
        }
64
65
        try {
66
            return $this
67
                    ->getAdapterFor($this->sanitizeExtension($type))
68
                    ->create($path, $files, $recursive);
69
        } catch (ExceptionInterface $e) {
70
            throw new RuntimeException('Unable to create archive', $e->getCode(), $e);
71
        }
72
    }
73
74
    /**
75
     * Opens an archive.
76
     *
77
     * @param string $path
78
     *
79
     * @return ArchiveInterface
80
     *
81
     * @throws RuntimeException In case of failure
82
     */
83
    public function open($path, $type = null)
84
    {
85
        if (null === $type) {
86
            $type = $this->guessAdapterExtension($path);
87
        }
88
89
        try {
90
            return $this
91
                    ->getAdapterFor($this->sanitizeExtension($type))
92
                    ->open($path);
93
        } catch (ExceptionInterface $e) {
94
            throw new RuntimeException('Unable to open archive', $e->getCode(), $e);
95
        }
96
    }
97
98
    /**
99
     * Adds a strategy.
100
     *
101
     * The last strategy added is preferred over the other ones.
102
     * You can add a strategy twice ; when doing this, the first one is removed
103
     * when inserting the second one.
104
     *
105
     * @param FileStrategyInterface $strategy
106
     *
107
     * @return Zippy
108
     */
109
    public function addStrategy(FileStrategyInterface $strategy)
110
    {
111
        $extension = $this->sanitizeExtension($strategy->getFileExtension());
112
113
        if (!isset($this->strategies[$extension])) {
114
            $this->strategies[$extension] = array();
115
        }
116
117
        if (false !== $key = array_search($strategy, $this->strategies[$extension], true)) {
118
            unset($this->strategies[$extension][$key]);
119
        }
120
121
        array_unshift($this->strategies[$extension], $strategy);
122
123
        return $this;
124
    }
125
126
    /**
127
     * Returns the strategies as they are stored
128
     *
129
     * @return array
130
     */
131
    public function getStrategies()
132
    {
133
        return $this->strategies;
134
    }
135
136
    /**
137
     * Returns an adapter for a file extension
138
     *
139
     * @param string $extension The extension
140
     *
141
     * @return AdapterInterface
142
     *
143
     * @throws FormatNotSupportedException  When no strategy is defined for this extension
144
     * @throws NoAdapterOnPlatformException When no adapter is supported for this extension on this platform
145
     */
146
    public function getAdapterFor($extension)
147
    {
148
        $extension = $this->sanitizeExtension($extension);
149
150
        if (!$extension || !isset($this->strategies[$extension])) {
151
            throw new FormatNotSupportedException(sprintf('No strategy for %s extension', $extension));
152
        }
153
154
        foreach ($this->strategies[$extension] as $strategy) {
155
            foreach ($strategy->getAdapters() as $adapter) {
156
                if ($adapter->isSupported()) {
157
                    return $adapter;
158
                }
159
            }
160
        }
161
162
        throw new NoAdapterOnPlatformException(sprintf('No adapter available for %s on this platform', $extension));
163
    }
164
165
    /**
166
     * Creates Zippy and loads default strategies
167
     *
168
     * @return Zippy
169
     */
170
    public static function load()
171
    {
172
        $adapters = AdapterContainer::load();
173
        $factory = new static($adapters);
174
175
        $factory->addStrategy(new ZipFileStrategy($adapters));
176
        $factory->addStrategy(new TarFileStrategy($adapters));
177
        $factory->addStrategy(new TarGzFileStrategy($adapters));
178
        $factory->addStrategy(new TarBz2FileStrategy($adapters));
179
        $factory->addStrategy(new TB2FileStrategy($adapters));
180
        $factory->addStrategy(new TBz2FileStrategy($adapters));
181
        $factory->addStrategy(new TGzFileStrategy($adapters));
182
183
        return $factory;
184
    }
185
186
    /**
187
     * Sanitize an extension.
188
     *
189
     * Strips dot from the beginning, converts to lowercase and remove trailing
190
     * whitespaces
191
     *
192
     * @param string $extension
193
     *
194
     * @return string
195
     */
196
    private function sanitizeExtension($extension)
197
    {
198
        return ltrim(trim(mb_strtolower($extension)), '.');
199
    }
200
201
    /**
202
     * Finds an extension that has strategy registered given a file path
203
     *
204
     * Returns null if no matching strategy found.
205
     *
206
     * @param string $path
207
     *
208
     * @return string|null
209
     */
210
    private function guessAdapterExtension($path)
211
    {
212
        $path = strtolower(trim($path));
213
214
        foreach ($this->strategies as $extension => $strategy) {
215
            if ($extension === substr($path, (strlen($extension) * -1))) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $extension (integer) and substr($path, strlen($extension) * -1) (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
216
                return $extension;
217
            }
218
        }
219
220
        return null;
221
    }
222
}
223