Transfer   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 96
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 83.33%

Importance

Changes 0
Metric Value
wmc 18
lcom 1
cbo 0
dl 0
loc 96
c 0
b 0
f 0
ccs 40
cts 48
cp 0.8333
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getHeaders() 0 4 1
A getFormattedHeaders() 0 10 2
A getStatus() 0 4 1
A getTransferInformations() 0 12 2
A stream() 0 14 4
B getCachingHeaders() 0 18 5
A server_value() 0 4 2
1
<?php
2
3
/**
4
 * Image Manager
5
 */
6
namespace Onigoetz\Imagecache;
7
8
/**
9
 * Image manager
10
 *
11
 * Prepares the images for the cache
12
 *
13
 *
14
 * @author Stéphane Goetz
15
 */
16
class Transfer
17
{
18
    protected $path;
19
20
    protected $headers = [];
21
    protected $status = 200;
22
23 6
    public function __construct($path)
24
    {
25 6
        $this->path = $path;
26
27 6
        $this->getTransferInformations();
28 6
    }
29
30 6
    public function getHeaders()
31
    {
32 6
        return $this->headers;
33
    }
34
35
    public function getFormattedHeaders()
36
    {
37
        $headers = [];
38
39
        foreach ($this->headers as $name => $value) {
40
            $headers[] = "$name: $value";
41
        }
42
43
        return $headers;
44
    }
45
46 6
    public function getStatus()
47
    {
48 6
        return $this->status;
49
    }
50
51 6
    protected function getTransferInformations()
52
    {
53 6
        $size = getimagesize($this->path);
54 6
        $this->headers['Content-Type'] = $size['mime'];
55
56 6
        if ($fileinfo = stat($this->path)) {
57 6
            $this->headers['Content-Length'] = $fileinfo[7];
58 6
            $this->headers['Expires'] = gmdate('D, d M Y H:i:s', time() + 1209600) . ' GMT';
59 6
            $this->headers['Cache-Control'] = 'max-age=1209600, private, must-revalidate';
60 6
            $this->getCachingHeaders($fileinfo);
61 2
        }
62 6
    }
63
64
    /**
65
     * Transfer an image to the browser
66
     */
67 3
    public function stream()
68
    {
69 3
        if (ob_get_level()) {
70 3
            ob_end_clean();
71 1
        }
72
73
        // Transfer file in 1024 byte chunks to save memory usage.
74 3
        if ($fd = fopen($this->path, 'rb')) {
75 3
            while (!feof($fd)) {
76 3
                echo fread($fd, 1024);
77 1
            }
78 3
            fclose($fd);
79 1
        }
80 3
    }
81
82
    /**
83
     * Set file headers that handle "If-Modified-Since" correctly for the
84
     * given fileinfo.
85
     *
86
     * @param array $fileinfo Array returned by stat().
87
     */
88 6
    protected function getCachingHeaders($fileinfo)
89
    {
90
        // Set default values:
91 6
        $last_modified = gmdate('D, d M Y H:i:s', $fileinfo[9]) . ' GMT';
92 6
        $etag = md5($last_modified);
93
94
        // See if the client has provided the required HTTP headers:
95 6
        $if_modified_since = $this->server_value('HTTP_IF_MODIFIED_SINCE', false);
96 6
        $if_none_match = $this->server_value('HTTP_IF_NONE_MATCH', false);
97
98 6
        if ($if_modified_since && $if_none_match && $if_none_match == $etag && $if_modified_since == $last_modified) {
99
            $this->status = 304;
100
        }
101
102
        // Send appropriate response:
103 6
        $this->headers['Last-Modified'] = $last_modified;
104 6
        $this->headers['ETag'] = $etag;
105 6
    }
106
107 6
    protected function server_value($key, $default)
0 ignored issues
show
Coding Style introduced by
server_value uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
108
    {
109 6
        return array_key_exists($key, $_SERVER) ? $_SERVER[$key] : $default;
110
    }
111
}
112