ElggRewriteTester::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Elgg RewriteTester.
5
 * Test if URL rewriting is working.
6
 *
7
 * @package    Elgg.Core
8
 * @subpackage Installer
9
 */
10
class ElggRewriteTester {
11
	protected $webserver;
12
	protected $serverSupportsRemoteRead;
13
	protected $rewriteTestPassed;
14
	protected $htaccessIssue;
15
16
	/**
17
	 * Set the webserver as unknown.
18
	 */
19
	public function __construct() {
20
		$this->webserver = 'unknown';
21
	}
22
23
	/**
24
	 * Run the rewrite test and return a status array
25
	 *
26
	 * @param string $url  URL of rewrite test
27
	 * @param string $path Root directory of Elgg with trailing slash
28
	 *
29
	 * @return array
30
	 */
31
	public function run($url, $path) {
32
33
		$this->webserver = \ElggRewriteTester::guessWebServer();
34
35
		$this->rewriteTestPassed = $this->runRewriteTest($url);
36
37
		if ($this->rewriteTestPassed == FALSE) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
38
			if ($this->webserver == 'apache' || $this->webserver == 'unknown') {
39
				if ($this->createHtaccess($url, $path)) {
40
					$this->rewriteTestPassed = $this->runRewriteTest($url);
41
				}
42
			}
43
		}
44
45
		return $this->returnStatus($url);
46
	}
47
48
	/**
49
	 * Guess the web server from $_SERVER['SERVER_SOFTWARE']
50
	 *
51
	 * @return string
52
	 */
53
	public static function guessWebServer() {
54
		$serverString = strtolower($_SERVER['SERVER_SOFTWARE']);
55
		$possibleServers = array('apache', 'nginx', 'lighttpd', 'iis');
56
		foreach ($possibleServers as $server) {
57
			if (strpos($serverString, $server) !== FALSE) {
58
				return $server;
59
			}
60
		}
61
		return 'unknown';
62
	}
63
64
	/**
65
	 * Guess if url contains subdirectory or not.
66
	 *
67
	 * @param string $url Rewrite test URL
68
	 *
69
	 * @return string|bool Subdirectory string with beginning and trailing slash or false if were unable to determine subdirectory 
70
	 * or pointing at root of domain already
71
	 */
72
	public function guessSubdirectory($url) {
73
		$elements = parse_url($url);
74
		if (!$elements || !isset($elements['path'])) {
75
			return false;
76
		}
77
		$subdir = trim(dirname($elements['path']), '/');
78
		if (!$subdir) {
79
			return false;
80
		} else {
81
			return "/$subdir/";
82
		}
83
	}
84
85
	/**
86
	 * Hit the rewrite test URL to determine if the rewrite rules are working
87
	 *
88
	 * @param string $url Rewrite test URL
89
	 *
90
	 * @return bool
91
	 */
92
	public function runRewriteTest($url) {
93
		$this->serverSupportsRemoteRead = ($this->fetchUrl($url) === 'success');
94
		return $this->serverSupportsRemoteRead;
95
	}
96
	
97
	/**
98
	 * Check whether the site homepage can be fetched via curl
99
	 * 
100
	 * @return boolean
101
	 */
102
	public function runLocalhostAccessTest() {
103
		$url = _elgg_services()->config->getSiteUrl();
104
		return (bool)$this->fetchUrl($url);
105
	}
106
107
	/**
108
	 * Fetch a URL
109
	 *
110
	 * @param string $url The URL
111
	 *
112
	 * @return string Note that empty string may imply failure in fetching or empty response
113
	 */
114
	private function fetchUrl($url) {
115
		$response = '';
116
117
		if (ini_get('allow_url_fopen')) {
118
			$ctx = stream_context_create(array(
119
				'http' => array(
120
					'follow_location' => 0,
121
					'timeout' => 5,
122
				),
123
			));
124
			$response = @file_get_contents($url, null, $ctx);
125
		}
126
127
		if (!$response && function_exists('curl_init')) {
128
			$ch = curl_init();
129
			curl_setopt($ch, CURLOPT_URL, $url);
130
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
131
			curl_setopt($ch, CURLOPT_TIMEOUT, 5);
132
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
133
			$response = curl_exec($ch);
134
			curl_close($ch);
135
		}
136
137
		return (string)$response;
138
	}
139
140
	/**
141
	 * Create Elgg's .htaccess file or confirm that it exists
142
	 *
143
	 * @param string $url  URL of rewrite test
144
	 * @param string $path Elgg's root directory with trailing slash
145
	 *
146
	 * @return bool
147
	 */
148
	public function createHtaccess($url, $path) {
149
		$filename = "{$path}.htaccess";
150
		if (file_exists($filename)) {
151
			// check that this is the Elgg .htaccess
152
			$data = file_get_contents($filename);
153
			if ($data === FALSE) {
154
				// don't have permission to read the file
155
				$this->htaccessIssue = 'read_permission';
156
				return FALSE;
157
			}
158
			if (strpos($data, 'Elgg') === FALSE) {
159
				$this->htaccessIssue = 'non_elgg_htaccess';
160
				return FALSE;
161
			} else {
162
				// check if this is an old Elgg htaccess
163
				if (strpos($data, 'RewriteRule ^rewrite.php$ install.php') == FALSE) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strpos($data, 'RewriteRu...rite.php$ install.php') of type integer to the boolean FALSE. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
164
					$this->htaccessIssue = 'old_elgg_htaccess';
165
					return FALSE;
166
				}
167
				return TRUE;
168
			}
169
		}
170
171
		if (!is_writable($path)) {
172
			$this->htaccessIssue = 'write_permission';
173
			return FALSE;
174
		}
175
176
		// create the .htaccess file
177
		$result = copy("{$path}install/config/htaccess.dist", $filename);
178
		if (!$result) {
179
			$this->htaccessIssue = 'cannot_copy';
180
			return FALSE;
181
		}
182
		
183
		// does default RewriteBase work already?
184
		if (!$this->runRewriteTest($url)) {
185
			//try to rewrite to guessed subdirectory
186
			if ($subdir = $this->guessSubdirectory($url)) {
187
				$contents = file_get_contents($filename);
188
				$contents = preg_replace("/#RewriteBase \/(\r?\n)/", "RewriteBase $subdir\$1", $contents);
189
				if ($contents) {
190
					file_put_contents($filename, $contents);
191
				}
192
			}
193
		}
194
195
		return TRUE;
196
	}
197
198
	/**
199
	 * Create the status array required by the ElggInstaller
200
	 *
201
	 * @param string $url Rewrite test URL
202
	 *
203
	 * @return array
204
	 */
205
	protected function returnStatus($url) {
206
		if ($this->rewriteTestPassed) {
207
			return array(
208
				'severity' => 'pass',
209
				'message' => _elgg_services()->translator->translate('install:check:rewrite:success'),
210
			);
211
		}
212
213
		if ($this->serverSupportsRemoteRead == FALSE) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
214
			$msg = _elgg_services()->translator->translate('install:warning:rewrite:unknown', array($url));
215
			$msg .= elgg_view('install/js_rewrite_check', array('url' => $url));
216
			
217
			return array(
218
				'severity' => 'warning',
219
				'message' => $msg,
220
			);
221
		}
222
223
		if ($this->webserver == 'apache') {
224
			$serverString = _elgg_services()->translator->translate('install:error:rewrite:apache');
225
			$msg = "$serverString\n\n";
226
			if (!isset($this->htaccessIssue)) {
227
				$msg .= _elgg_services()->translator->translate('install:error:rewrite:allowoverride');
228
				$msg .= elgg_view('install/js_rewrite_check', array('url' => $url));
229
			
230
				return array(
231
					'severity' => 'failure',
232
					'message' => $msg,
233
				);
234
			}
235
			$msg .= _elgg_services()->translator->translate("install:error:rewrite:htaccess:{$this->htaccessIssue}");
236
			return array(
237
				'severity' => 'failure',
238
				'message' => $msg,
239
			);
240
		}
241
242
		if ($this->webserver != 'unknown') {
243
			$serverString = _elgg_services()->translator->translate("install:error:rewrite:{$this->webserver}");
244
			$msg = "$serverString\n\n";
245
			$msg .= _elgg_services()->translator->translate("install:error:rewrite:altserver");
246
			return array(
247
				'severity' => 'failure',
248
				'message' => $msg,
249
			);
250
		}
251
252
		return array(
253
			'severity' => 'failure',
254
			'message' => _elgg_services()->translator->translate('install:error:rewrite:unknown'),
255
		);
256
	}
257
}