Completed
Push — master ( cd354f...fa948f )
by cam
04:36
created

livrer_fichier.php ➔ spip_livrer_fichier_partie()   C

Complexity

Conditions 10
Paths 13

Size

Total Lines 96

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
nc 13
nop 2
dl 0
loc 96
rs 6.2206
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/***************************************************************************\
4
 *  SPIP, Syst�me de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright � avec tendresse depuis 2001                                 *
7
 *  Arnaud Martin, Antoine Pitrou, Philippe Rivi�re, Emmanuel Saint-James  *
8
 *                                                                         *
9
 *  Ce programme est un logiciel libre distribu� sous licence GNU/GPL.     *
10
 *  Pour plus de d�tails voir le fichier COPYING.txt ou l'aide en ligne.   *
11
\***************************************************************************/
12
13
/**
14
 * Gestion des emails et de leur envoi
15
 *
16
 * @package SPIP\Core\Fichier
17
 **/
18
if (!defined('_ECRIRE_INC_VERSION')) {
19
	return;
20
}
21
22
/**
23
 * Envoyer un fichier dont on fourni le chemin, le mime type, en attachment ou non, avec un expire
24
 *
25
 * @use spip_livrer_fichier_entetes()
26
 * @use spip_livrer_fichier_entier()
27
 * @use spip_livrer_fichier_partie()
28
 *
29
 * @param string $fichier
30
 * @param string $content_type
31
 * @param array $options
32
 *   bool $attachment
33
 *   int $expires
34
 *   int|null range
35
 * @throws Exception
36
 */
37
function spip_livrer_fichier($fichier, $content_type = 'application/octet-stream', $options = []){
38
39
	$defaut = [
40
	    'attachment' => false,
41
		'expires' => 3600,
42
		'range' => null
43
	];
44
	$options = array_merge($options, $defaut);
45
	if (is_numeric($options['expire']) and $options['expire'] > 0) {
46
		$options['expire'] = gmdate('D, d M Y H:i:s', time() + $options['expires']) . ' GMT';
47
	}
48
49
	if (is_null($options) and isset($_SERVER['HTTP_RANGE'])) {
50
		$options['range'] = $_SERVER['HTTP_RANGE'];
51
	}
52
53
	spip_livrer_fichier_entetes($fichier, $content_type, $options['attachment'] && !$options['range'], $options['expires']);
54
55
	if (!is_null($options['range'])) {
56
		spip_livrer_fichier_partie($fichier, $options['range']);
57
	}
58
	else {
59
		spip_livrer_fichier_entier($fichier);
60
	}
61
}
62
63
/**
64
 * Envoyer les entetes du fichier, sauf ce qui est lie au mode d'envoi (entier ou par parties)
65
 *
66
 * @see spip_livrer_fichier()
67
 * @param string $fichier
68
 * @param string $content_type
69
 * @param false $attachment
0 ignored issues
show
Documentation introduced by
Should the type for parameter $attachment not be boolean?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
70
 * @param int|string $expires
0 ignored issues
show
Documentation introduced by
Consider making the type for parameter $expires a bit more specific; maybe use integer.
Loading history...
71
 */
72
function spip_livrer_fichier_entetes($fichier, $content_type = 'application/octet-stream', $attachment = false, $expires=0){
73
	// toujours envoyer un content type, meme vide !
74
	header('Accept-Ranges: bytes');
75
	header('Content-Type: ' . $content_type);
76
77
	if ($attachment) {
78
		$f = basename($fichier);
79
		// ce content-type est necessaire pour eviter des corruptions de zip dans ie6
80
		header('Content-Type: application/octet-stream');
81
82
		header("Content-Disposition: attachment; filename=\"$f\";");
83
		header('Content-Transfer-Encoding: binary');
84
85
		// fix for IE caching or PHP bug issue
86
		header('Expires: 0'); // set expiration time
87
		header('Pragma: public');
88
		header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
89
	}
90
	else {
91
		$f = basename($fichier);
92
		header("Content-Disposition: inline; filename=\"$f\";");
93
		header('Expires: ' . $expires); // set expiration time
94
	}
95
96
}
97
98
/**
99
 * Envoyer les contenu entier du fichier
100
 * @param string $fichier
101
 */
102
function spip_livrer_fichier_entier($fichier){
103
	if (!file_exists($fichier)){
104
		throw new \Exception(sprintf('File not found: %s', $fichier));
105
	}
106
107
	if (!is_readable($fichier)){
108
		throw new \Exception(sprintf('File not readable: %s', $fichier));
109
	}
110
111
	if ($size = filesize($fichier)) {
112
		header(sprintf('Content-Length: %d', $size));
113
	}
114
115
	readfile($fichier);
116
	exit();
117
}
118
119
/**
120
 * Envoyer une partie du fichier
121
 * Prendre en charge l'entete Range:bytes=0-456 utilise par les player medias
122
 * source : https://github.com/pomle/php-serveFilePartial/blob/master/ServeFilePartial.inc.php
123
 *
124
 * @param string $fichier
125
 * @param string $range
0 ignored issues
show
Documentation introduced by
Should the type for parameter $range not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
126
 * @throws Exception
127
 */
128
function spip_livrer_fichier_partie($fichier, $range=null){
129
	if (!file_exists($fichier)){
130
		throw new \Exception(sprintf('File not found: %s', $fichier));
131
	}
132
133
	if (!is_readable($fichier)){
134
		throw new \Exception(sprintf('File not readable: %s', $fichier));
135
	}
136
137
138
	// Par defaut on envoie tout
139
	$byteOffset = 0;
0 ignored issues
show
Unused Code introduced by
$byteOffset is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
140
	$byteLength = $fileSize = filesize($fichier);
141
142
143
	// Parse Content-Range header for byte offsets, looks like "bytes=11525-" OR "bytes=11525-12451"
144
	if ($range and preg_match('%bytes=(\d+)-(\d+)?%i', $range, $match)){
145
		### Offset signifies where we should begin to read the file
146
		$byteOffset = (int)$match[1];
147
148
149
		### Length is for how long we should read the file according to the browser, and can never go beyond the file size
150
		if (isset($match[2])){
151
			$finishBytes = (int)$match[2];
152
			$byteLength = $finishBytes+1;
153
		} else {
154
			$finishBytes = $fileSize-1;
155
		}
156
157
		$cr_header = sprintf('Content-Range: bytes %d-%d/%d', $byteOffset, $finishBytes, $fileSize);
158
	}
159
	else {
160
		// si pas de range valide, on delegue a la methode d'envoi complet
161
		spip_livrer_fichier_entier($fichier);
162
		// redondant, mais facilite la comprehension du code
163
		exit();
164
	}
165
166
	// Remove headers that might unnecessarily clutter up the output
167
	header_remove('Cache-Control');
168
	header_remove('Pragma');
169
170
	// partial content
171
	header("HTTP/1.1 206 Partial content");
172
	header($cr_header);  ### Decrease by 1 on byte-length since this definition is zero-based index of bytes being sent
173
174
175
	$byteRange = $byteLength-$byteOffset;
176
177
	header(sprintf('Content-Length: %d', $byteRange));
178
179
	// Variable containing the buffer
180
	$buffer = '';
0 ignored issues
show
Unused Code introduced by
$buffer is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
181
	// Just a reasonable buffer size
182
	$bufferSize = 512*16;
183
	// Contains how much is left to read of the byteRange
184
	$bytePool = $byteRange;
185
186
	if (!$handle = fopen($fichier, 'r')){
187
		throw new \Exception(sprintf("Could not get handle for file %s", $fichier));
188
	}
189
190
	if (fseek($handle, $byteOffset, SEEK_SET)==-1){
191
		throw new \Exception(sprintf("Could not seek to byte offset %d", $byteOffset));
192
	}
193
194
195
	while ($bytePool>0){
196
		// How many bytes we request on this iteration
197
		$chunkSizeRequested = min($bufferSize, $bytePool);
198
199
		// Try readin $chunkSizeRequested bytes from $handle and put data in $buffer
200
		$buffer = fread($handle, $chunkSizeRequested);
201
202
		// Store how many bytes were actually read
203
		$chunkSizeActual = strlen($buffer);
204
205
		// If we didn't get any bytes that means something unexpected has happened since $bytePool should be zero already
206
		if ($chunkSizeActual==0){
207
			// For production servers this should go in your php error log, since it will break the output
208
			trigger_error('Chunksize became 0', E_USER_WARNING);
209
			break;
210
		}
211
212
		// Decrease byte pool with amount of bytes that were read during this iteration
213
		$bytePool -= $chunkSizeActual;
214
215
		// Write the buffer to output
216
		print $buffer;
217
218
		// Try to output the data to the client immediately
219
		flush();
220
	}
221
222
	exit();
223
}
224