HackedFileGroup::scanBasePath()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 0
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Cerbere\Model\Hacked;
4
5
/**
6
 * Represents a group of files on the local filesystem.
7
 */
8
class HackedFileGroup
9
{
10
    /**
11
     * @var string
12
     */
13
    protected $base_path = '';
14
15
    /**
16
     * @var array
17
     */
18
    protected $files = array();
19
20
    /**
21
     * @var array
22
     */
23
    protected $files_hashes = array();
24
25
    /**
26
     * @var array
27
     */
28
    protected $file_mtimes = array();
29
30
    /**
31
     * @var HackedFileHasher
32
     */
33
    protected $hasher;
34
35
    /**
36
     * Constructor.
37
     *
38
     * @param string $base_path
39
     * @param HackedFileHasher $hasher
40
     */
41
    public function __construct($base_path, HackedFileHasher $hasher = null)
42
    {
43
        if (null === $hasher) {
44
            $hasher = new HackedFileIgnoreEndingsHasher();
45
        }
46
47
        $this->base_path = $base_path;
48
        $this->hasher = $hasher;
49
    }
50
51
    /**
52
     * Hash all files listed in the file group.
53
     */
54
    public function computeHashes()
55
    {
56
        foreach ($this->files as $filename) {
57
            $this->files_hashes[$filename] = $this->hasher->hash($this->base_path . DIRECTORY_SEPARATOR . $filename);
58
        }
59
    }
60
61
    /**
62
     * Determine if a file exists.
63
     * @param string $file
64
     * @return bool
65
     */
66
    public function fileExists($file)
67
    {
68
        return file_exists($this->base_path . DIRECTORY_SEPARATOR . $file);
69
    }
70
71
    /**
72
     * Return a new hackedFileGroup listing all files inside the given $path.
73
     *
74
     * @param string $path
75
     *
76
     * @return HackedFileGroup
77
     */
78
    public static function createFromDirectory($path)
79
    {
80
        $filegroup = new self($path);
81
        // Find all the files in the path, and add them to the file group.
82
        $filegroup->scanBasePath();
83
84
        return $filegroup;
85
    }
86
87
    /**
88
     * Locate all sensible files at the base path of the file group.
89
     */
90
    public function scanBasePath()
91
    {
92
        $white_list = array('.', '..', 'CVS', '.svn', '.git',);
93
        $files = self::scanDirectory($this->base_path, '/.*/', $white_list);
94
95
        foreach ($files as $file) {
96
            $filename = str_replace($this->base_path . DIRECTORY_SEPARATOR, '', $file->filename);
97
            $this->files[] = $filename;
98
        }
99
    }
100
101
    /**
102
     * @param string $dir
103
     * @param int $mask
104
     * @param array $nomask
105
     * @param callable $callback
106
     * @param bool|true $recurse
107
     * @param string $key
108
     * @param int $min_depth
109
     * @param int $depth
110
     *
111
     * @return array
112
     */
113
    public static function scanDirectory(
114
      $dir,
115
      $mask,
116
      $nomask = array('.', '..', 'CVS'),
117
      $callback = null,
118
      $recurse = true,
119
      $key = 'filename',
120
      $min_depth = 0,
121
      $depth = 0
122
    ) {
123
        $key = (in_array($key, array('filename', 'basename', 'name')) ? $key : 'filename');
124
        $files = array();
125
126
        if (is_dir($dir) && $handle = opendir($dir)) {
127
            while (false !== ($file = readdir($handle))) {
128
                if (!in_array($file, $nomask)) {
129
                    if (is_dir($dir . DIRECTORY_SEPARATOR . $file) && $recurse) {
130
                        // Give priority to files in this folder by merging them in after any subdirectory files.
131
                        $files = array_merge(
132
                          self::scanDirectory(
133
                            $dir . DIRECTORY_SEPARATOR . $file,
134
                            $mask,
135
                            $nomask,
136
                            $callback,
137
                            $recurse,
138
                            $key,
139
                            $min_depth,
140
                            $depth + 1
141
                          ),
142
                          $files
143
                        );
144
                    } elseif ($depth >= $min_depth && preg_match($mask, $file)) {
145
                        // Always use this match over anything already set in $files with the same $$key.
146
                        $filename = $dir . DIRECTORY_SEPARATOR . $file;
147
                        $basename = basename($file);
148
                        $name = substr($basename, 0, strrpos($basename, '.'));
149
                        $files[$$key] = new \stdClass();
150
                        $files[$$key]->filename = $filename;
151
                        $files[$$key]->basename = $basename;
152
                        $files[$$key]->name = $name;
153
                        if (is_callable($callback)) {
154
                            $callback($filename);
155
                        }
156
                    }
157
                }
158
            }
159
160
            closedir($handle);
161
        }
162
163
        return $files;
164
    }
165
166
    /**
167
     * Return a new hackedFileGroup listing all files specified.
168
     *
169
     * @param string $path
170
     * @param array $files
171
     *
172
     * @return HackedFileGroup
173
     */
174
    public static function createFromList($path, array $files)
175
    {
176
        $filegroup = new self($path);
177
        // Find all the files in the path, and add them to the file group.
178
        $filegroup->files = $files;
179
180
        return $filegroup;
181
    }
182
183
    /**
184
     * @param string $file
185
     *
186
     * @return string|bool
187
     */
188
    public function getFileHash($file)
189
    {
190
        if (isset($this->files_hashes[$file])) {
191
            return $this->files_hashes[$file];
192
        }
193
194
        return false;
195
    }
196
197
    /**
198
     * @param string $file
199
     * @return string
200
     */
201
    public function getFileLocation($file)
202
    {
203
        return $this->base_path . DIRECTORY_SEPARATOR . $file;
204
    }
205
206
    /**
207
     * @return array
208
     */
209
    public function getFiles()
210
    {
211
        return $this->files;
212
    }
213
214
    /**
215
     * Determine if the given file is binary.
216
     * @param string $file
217
     * @return bool
218
     */
219
    public function isNotBinary($file)
220
    {
221
        return is_readable($this->base_path . DIRECTORY_SEPARATOR . $file)
222
        && !self::isBinary($this->base_path . DIRECTORY_SEPARATOR . $file);
223
    }
224
225
    /**
226
     * Determine if a file is a binary file.
227
     *
228
     * Taken from: http://www.ultrashock.com/forums/server-side/checking-if-a-file-is-binary-98391.html
229
     * and then tweaked in: http://drupal.org/node/760362.
230
     *
231
     * @param string $file
232
     *
233
     * @return bool
234
     */
235
    public static function isBinary($file)
236
    {
237
        if (file_exists($file)) {
238
            if (!is_file($file)) {
239
                return false;
240
            }
241
            if (!is_readable($file)) {
242
                return true;
243
            }
244
245
            $fh = fopen($file, "r");
246
            $blk = fread($fh, 512);
247
            fclose($fh);
248
            clearstatcache();
249
250
            return (substr_count($blk, "^\r\n") / 512 > 0.3
251
              || substr_count($blk, "^ -~") / 512 > 0.3
252
              || substr_count($blk, "\x00") > 0);
253
        }
254
255
        return false;
256
    }
257
258
    /**
259
     * Determine if the given file is readable.
260
     * @param string $file
261
     * @return bool
262
     */
263
    public function isReadable($file)
264
    {
265
        return is_readable($this->base_path . DIRECTORY_SEPARATOR . $file);
266
    }
267
}
268