Completed
Push — master ( 1adcc0...3cb4b6 )
by Sebastien
02:33
created

HackedFileGroup::isBinary()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 22
rs 8.6738
cc 6
eloc 14
nc 6
nop 1
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
        $files = self::scanDirectory(
93
          $this->base_path,
94
          '/.*/',
95
          array(
96
            '.',
97
            '..',
98
            'CVS',
99
            '.svn',
100
            '.git',
101
          )
102
        );
103
104
        foreach ($files as $file) {
105
            $filename = str_replace($this->base_path . DIRECTORY_SEPARATOR, '', $file->filename);
106
            $this->files[] = $filename;
107
        }
108
    }
109
110
    /**
111
     * @param string $dir
112
     * @param int $mask
113
     * @param array $nomask
114
     * @param callable $callback
115
     * @param bool|true $recurse
116
     * @param string $key
117
     * @param int $min_depth
118
     * @param int $depth
119
     *
120
     * @return array
121
     */
122
    public static function scanDirectory(
123
      $dir,
124
      $mask,
125
      $nomask = array('.', '..', 'CVS'),
126
      $callback = null,
127
      $recurse = true,
128
      $key = 'filename',
129
      $min_depth = 0,
130
      $depth = 0
131
    ) {
132
        $key = (in_array($key, array('filename', 'basename', 'name')) ? $key : 'filename');
133
        $files = array();
134
135
        if (is_dir($dir) && $handle = opendir($dir)) {
136
            while (false !== ($file = readdir($handle))) {
137
                if (!in_array($file, $nomask)) {
138
                    if (is_dir($dir . DIRECTORY_SEPARATOR . $file) && $recurse) {
139
                        // Give priority to files in this folder by merging them in after any subdirectory files.
140
                        $files = array_merge(
141
                          self::scanDirectory(
142
                            $dir . DIRECTORY_SEPARATOR . $file,
143
                            $mask,
144
                            $nomask,
145
                            $callback,
146
                            $recurse,
147
                            $key,
148
                            $min_depth,
149
                            $depth + 1
150
                          ),
151
                          $files
152
                        );
153
                    } elseif ($depth >= $min_depth && preg_match($mask, $file)) {
154
                        // Always use this match over anything already set in $files with the same $$key.
155
                        $filename = $dir . DIRECTORY_SEPARATOR . $file;
156
                        $basename = basename($file);
157
                        $name = substr($basename, 0, strrpos($basename, '.'));
158
                        $files[$$key] = new \stdClass();
159
                        $files[$$key]->filename = $filename;
160
                        $files[$$key]->basename = $basename;
161
                        $files[$$key]->name = $name;
162
                        if (is_callable($callback)) {
163
                            $callback($filename);
164
                        }
165
                    }
166
                }
167
            }
168
169
            closedir($handle);
170
        }
171
172
        return $files;
173
    }
174
175
    /**
176
     * Return a new hackedFileGroup listing all files specified.
177
     *
178
     * @param string $path
179
     * @param array $files
180
     *
181
     * @return HackedFileGroup
182
     */
183
    public static function createFromList($path, array $files)
184
    {
185
        $filegroup = new self($path);
186
        // Find all the files in the path, and add them to the file group.
187
        $filegroup->files = $files;
188
189
        return $filegroup;
190
    }
191
192
    /**
193
     * @param string $file
194
     *
195
     * @return string|bool
196
     */
197
    public function getFileHash($file)
198
    {
199
        if (isset($this->files_hashes[$file])) {
200
            return $this->files_hashes[$file];
201
        }
202
203
        return false;
204
    }
205
206
    /**
207
     * @param string $file
208
     * @return string
209
     */
210
    public function getFileLocation($file)
211
    {
212
        return $this->base_path . DIRECTORY_SEPARATOR . $file;
213
    }
214
215
    /**
216
     * @return array
217
     */
218
    public function getFiles()
219
    {
220
        return $this->files;
221
    }
222
223
    /**
224
     * Determine if the given file is binary.
225
     * @param string $file
226
     * @return bool
227
     */
228
    public function isNotBinary($file)
229
    {
230
        return is_readable($this->base_path . DIRECTORY_SEPARATOR . $file)
231
        && !self::isBinary($this->base_path . DIRECTORY_SEPARATOR . $file);
232
    }
233
234
    /**
235
     * Determine if a file is a binary file.
236
     *
237
     * Taken from: http://www.ultrashock.com/forums/server-side/checking-if-a-file-is-binary-98391.html
238
     * and then tweaked in: http://drupal.org/node/760362.
239
     *
240
     * @param string $file
241
     *
242
     * @return bool
243
     */
244
    public static function isBinary($file)
245
    {
246
        if (file_exists($file)) {
247
            if (!is_file($file)) {
248
                return 0;
249
            }
250
            if (!is_readable($file)) {
251
                return 1;
252
            }
253
254
            $fh = fopen($file, "r");
255
            $blk = fread($fh, 512);
256
            fclose($fh);
257
            clearstatcache();
258
259
            return (substr_count($blk, "^\r\n") / 512 > 0.3
260
              || substr_count($blk, "^ -~") / 512 > 0.3
261
              || substr_count($blk, "\x00") > 0);
262
        }
263
264
        return 0;
265
    }
266
267
    /**
268
     * Determine if the given file is readable.
269
     * @param string $file
270
     * @return bool
271
     */
272
    public function isReadable($file)
273
    {
274
        return is_readable($this->base_path . DIRECTORY_SEPARATOR . $file);
275
    }
276
}
277