Completed
Push — 3.7 ( 81b2d8...ef0909 )
by
unknown
09:42
created

DirectorTest   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 536
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
dl 0
loc 536
rs 9.52
c 0
b 0
f 0
wmc 36
lcom 1
cbo 7

24 Methods

Rating   Name   Duplication   Size   Complexity  
A setUp() 0 32 4
A tearDown() 0 18 3
A testFileExists() 0 19 1
A testAbsoluteURL() 0 43 1
A testAlternativeBaseURL() 0 43 1
A testIsAbsolute() 0 17 2
A testIsAbsoluteUrl() 0 16 1
A testIsRelativeUrl() 0 12 1
A testMakeRelative() 0 20 1
A testIsSiteUrl() 0 7 1
A testQueryIsEnvironment() 0 32 2
A testResetGlobalsAfterTestRequest() 0 20 1
A testTestRequestCarriesGlobals() 0 22 3
A testRouteParams() 0 14 1
A testForceSSLProtectsEntireSite() 0 9 1
A testForceSSLOnTopLevelPagePattern() 0 5 1
A testForceSSLOnSubPagesPattern() 0 5 1
A testForceSSLWithPatternDoesNotMatchOtherPages() 0 9 1
A testForceSSLAlternateDomain() 0 6 1
A testExtractRequestHeaders() 0 28 1
A testUnmatchedRequestReturns404() 0 3 1
B testIsHttps() 0 59 4
A testTestIgnoresHashes() 0 16 1
A testRequestFilterInDirectorTest() 0 30 1
1
<?php
2
/**
3
 * @package framework
4
 * @subpackage tests
5
 *
6
 * @todo test Director::alternateBaseFolder()
7
 */
8
class DirectorTest extends SapphireTest {
9
10
	protected static $originalRequestURI;
11
12
	protected $originalProtocolHeaders = array();
13
14
	protected $originalGet = array();
15
16
	protected $originalSession = array();
17
18
	public function setUp() {
0 ignored issues
show
Coding Style introduced by
setUp 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...
Coding Style introduced by
setUp uses the super-global variable $_GET 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...
Coding Style introduced by
setUp uses the super-global variable $_SESSION 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...
19
		parent::setUp();
20
21
22
		// Hold the original request URI once so it doesn't get overwritten
23
		if(!self::$originalRequestURI) {
24
			self::$originalRequestURI = $_SERVER['REQUEST_URI'];
25
		}
26
		$_SERVER['REQUEST_URI'] = 'http://www.mysite.com';
27
28
		$this->originalGet = $_GET;
29
		$this->originalSession = $_SESSION;
30
		$_SESSION = array();
31
32
		Config::inst()->update('Director', 'rules', array(
33
			'DirectorTestRule/$Action/$ID/$OtherID' => 'DirectorTestRequest_Controller',
34
			'en-nz/$Action/$ID/$OtherID' => array(
35
				'Controller' => 'DirectorTestRequest_Controller',
36
				'Locale' => 'en_NZ'
37
			)
38
		));
39
40
		$headers = array(
41
			'HTTP_X_FORWARDED_PROTOCOL', 'HTTPS', 'SSL'
42
		);
43
44
		foreach($headers as $header) {
45
			if(isset($_SERVER[$header])) {
46
				$this->originalProtocolHeaders[$header] = $_SERVER[$header];
47
			}
48
		}
49
	}
50
51
	public function tearDown() {
0 ignored issues
show
Coding Style introduced by
tearDown uses the super-global variable $_GET 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...
Coding Style introduced by
tearDown uses the super-global variable $_SESSION 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...
Coding Style introduced by
tearDown 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...
52
		// TODO Remove director rule, currently API doesnt allow this
53
54
		$_GET = $this->originalGet;
55
		$_SESSION = $this->originalSession;
56
57
		// Reinstate the original REQUEST_URI after it was modified by some tests
58
		$_SERVER['REQUEST_URI'] = self::$originalRequestURI;
59
60
		if($this->originalProtocolHeaders) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->originalProtocolHeaders of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
61
			foreach($this->originalProtocolHeaders as $header => $value) {
62
				$_SERVER[$header] = $value;
63
			}
64
		}
65
66
67
		parent::tearDown();
68
	}
69
70
	public function testFileExists() {
71
		$tempFileName = 'DirectorTest_testFileExists.tmp';
72
		$tempFilePath = TEMP_FOLDER . '/' . $tempFileName;
73
74
		// create temp file
75
		file_put_contents($tempFilePath, '');
76
77
		$this->assertTrue(
78
			Director::fileExists($tempFilePath),
79
			'File exist check with absolute path'
80
		);
81
82
		$this->assertTrue(
83
			Director::fileExists($tempFilePath . '?queryparams=1&foo[bar]=bar'),
84
			'File exist check with query params ignored'
85
		);
86
87
		unlink($tempFilePath);
88
	}
89
90
	public function testAbsoluteURL() {
0 ignored issues
show
Coding Style introduced by
testAbsoluteURL 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...
91
92
		$rootURL = Director::protocolAndHost();
93
		$_SERVER['REQUEST_URI'] = "$rootURL/mysite/sub-page/";
94
		Config::inst()->update('Director', 'alternate_base_url', '/mysite/');
95
96
		//test empty URL
97
		$this->assertEquals("$rootURL/mysite/sub-page/", Director::absoluteURL(''));
98
99
		//test absolute - /
100
		$this->assertEquals("$rootURL/", Director::absoluteURL('/'));
101
102
		//test relative
103
		$this->assertEquals("$rootURL/mysite/sub-page/", Director::absoluteURL('./'));
104
		$this->assertEquals("$rootURL/mysite/sub-page/", Director::absoluteURL('.'));
105
106
		// Test already absolute url
107
		$this->assertEquals($rootURL, Director::absoluteURL($rootURL));
0 ignored issues
show
Security Bug introduced by
It seems like $rootURL defined by \Director::protocolAndHost() on line 92 can also be of type false; however, Director::absoluteURL() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
108
		$this->assertEquals($rootURL, Director::absoluteURL($rootURL, true));
0 ignored issues
show
Security Bug introduced by
It seems like $rootURL defined by \Director::protocolAndHost() on line 92 can also be of type false; however, Director::absoluteURL() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
109
		$this->assertEquals('http://www.mytest.com', Director::absoluteURL('http://www.mytest.com'));
110
		$this->assertEquals('http://www.mytest.com', Director::absoluteURL('http://www.mytest.com', true));
111
		$this->assertEquals("$rootURL/test", Director::absoluteURL("$rootURL/test"));
112
		$this->assertEquals("$rootURL/test", Director::absoluteURL("$rootURL/test", true));
113
114
		// Test relative to base
115
		$this->assertEquals("$rootURL/mysite/test", Director::absoluteURL("test", true));
116
		$this->assertEquals("$rootURL/mysite/test/url", Director::absoluteURL("test/url", true));
117
		$this->assertEquals("$rootURL/root", Director::absoluteURL("/root", true));
118
		$this->assertEquals("$rootURL/root/url", Director::absoluteURL("/root/url", true));
119
120
		// Test relative to requested page
121
		$this->assertEquals("$rootURL/mysite/sub-page/test", Director::absoluteURL("test"));
122
		// Legacy behaviour resolves this to $rootURL/mysite/test/url
123
		//$this->assertEquals("$rootURL/mysite/sub-page/test/url", Director::absoluteURL("test/url"));
0 ignored issues
show
Unused Code Comprehensibility introduced by
74% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
124
		$this->assertEquals("$rootURL/root", Director::absoluteURL("/root"));
125
		$this->assertEquals("$rootURL/root/url", Director::absoluteURL("/root/url"));
126
127
		// Test that javascript links are not left intact
128
		$this->assertStringStartsNotWith('javascript', Director::absoluteURL('javascript:alert("attack")'));
129
		$this->assertStringStartsNotWith('alert', Director::absoluteURL('javascript:alert("attack")'));
130
		$this->assertStringStartsNotWith('javascript', Director::absoluteURL('alert("attack")'));
131
		$this->assertStringStartsNotWith('alert', Director::absoluteURL('alert("attack")'));
132
	}
133
134
	public function testAlternativeBaseURL() {
0 ignored issues
show
Coding Style introduced by
testAlternativeBaseURL 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...
135
		// Get original protocol and hostname
136
		$rootURL = Director::protocolAndHost();
137
138
		// relative base URLs - you should end them in a /
139
		Config::inst()->update('Director', 'alternate_base_url', '/relativebase/');
140
		$_SERVER['REQUEST_URI'] = "$rootURL/relativebase/sub-page/";
141
142
		$this->assertEquals('/relativebase/', Director::baseURL());
143
		$this->assertEquals($rootURL . '/relativebase/', Director::absoluteBaseURL());
144
		$this->assertEquals(
145
			$rootURL . '/relativebase/subfolder/test',
146
			Director::absoluteURL('subfolder/test')
147
		);
148
149
		// absolute base URLs - you should end them in a /
150
		Config::inst()->update('Director', 'alternate_base_url', 'http://www.example.org/');
151
		$_SERVER['REQUEST_URI'] = "http://www.example.org/sub-page/";
152
		$this->assertEquals('http://www.example.org/', Director::baseURL());
153
		$this->assertEquals('http://www.example.org/', Director::absoluteBaseURL());
154
		$this->assertEquals('http://www.example.org/sub-page/', Director::absoluteURL(''));
155
		$this->assertEquals('http://www.example.org/', Director::absoluteURL('', true));
156
		/*
157
		 * See Legacy behaviour in testAbsoluteURL - sub-pages with '/' in the string are not correctly evaluated
158
		$this->assertEquals(
159
			'http://www.example.org/sub-page/subfolder/test',
160
			Director::absoluteURL('subfolder/test')
161
		);*/
162
		$this->assertEquals(
163
			'http://www.example.org/subfolder/test',
164
			Director::absoluteURL('subfolder/test', true)
165
		);
166
167
		// Setting it to false restores functionality
168
		Config::inst()->update('Director', 'alternate_base_url', false);
169
		$_SERVER['REQUEST_URI'] = $rootURL;
170
		$this->assertEquals(BASE_URL.'/', Director::baseURL());
171
		$this->assertEquals($rootURL.BASE_URL.'/', Director::absoluteBaseURL(BASE_URL));
0 ignored issues
show
Unused Code introduced by
The call to Director::absoluteBaseURL() has too many arguments starting with BASE_URL.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
172
		$this->assertEquals(
173
			$rootURL.BASE_URL . '/subfolder/test',
174
			Director::absoluteURL('subfolder/test')
175
		);
176
	}
177
178
	/**
179
	 * Tests that {@link Director::is_absolute()} works under different environment types
180
	 */
181
	public function testIsAbsolute() {
182
		$expected = array (
183
			'C:/something' => true,
184
			'd:\\'         => true,
185
			'e/'           => false,
186
			's:/directory' => true,
187
			'/var/www'     => true,
188
			'\\Something'  => true,
189
			'something/c:' => false,
190
			'folder'       => false,
191
			'a/c:/'        => false
192
		);
193
194
		foreach($expected as $path => $result) {
195
			$this->assertEquals(Director::is_absolute($path), $result, "Test result for $path");
196
		}
197
	}
198
199
	public function testIsAbsoluteUrl() {
200
		$this->assertTrue(Director::is_absolute_url('http://test.com/testpage'));
201
		$this->assertTrue(Director::is_absolute_url('ftp://test.com'));
202
		$this->assertFalse(Director::is_absolute_url('test.com/testpage'));
203
		$this->assertFalse(Director::is_absolute_url('/relative'));
204
		$this->assertFalse(Director::is_absolute_url('relative'));
205
		$this->assertFalse(Director::is_absolute_url("/relative/?url=http://foo.com"));
206
		$this->assertFalse(Director::is_absolute_url("/relative/#http://foo.com"));
207
		$this->assertTrue(Director::is_absolute_url("https://test.com/?url=http://foo.com"));
208
		$this->assertTrue(Director::is_absolute_url("trickparseurl:http://test.com"));
209
		$this->assertTrue(Director::is_absolute_url('//test.com'));
210
		$this->assertTrue(Director::is_absolute_url('/////test.com'));
211
		$this->assertTrue(Director::is_absolute_url('  ///test.com'));
212
		$this->assertTrue(Director::is_absolute_url('http:test.com'));
213
		$this->assertTrue(Director::is_absolute_url('//http://test.com'));
214
	}
215
216
	public function testIsRelativeUrl() {
217
		$siteUrl = Director::absoluteBaseURL();
218
		$this->assertFalse(Director::is_relative_url('http://test.com'));
219
		$this->assertFalse(Director::is_relative_url('https://test.com'));
220
		$this->assertFalse(Director::is_relative_url('   https://test.com/testpage   '));
221
		$this->assertTrue(Director::is_relative_url('test.com/testpage'));
222
		$this->assertFalse(Director::is_relative_url('ftp://test.com'));
223
		$this->assertTrue(Director::is_relative_url('/relative'));
224
		$this->assertTrue(Director::is_relative_url('relative'));
225
		$this->assertTrue(Director::is_relative_url('/relative/?url=http://test.com'));
226
		$this->assertTrue(Director::is_relative_url('/relative/#=http://test.com'));
227
	}
228
229
	public function testMakeRelative() {
230
		$siteUrl = Director::absoluteBaseURL();
231
		$siteUrlNoProtocol = preg_replace('/https?:\/\//', '', $siteUrl);
232
233
		$this->assertEquals(Director::makeRelative("$siteUrl"), '');
234
		$this->assertEquals(Director::makeRelative("https://$siteUrlNoProtocol"), '');
235
		$this->assertEquals(Director::makeRelative("http://$siteUrlNoProtocol"), '');
236
237
		$this->assertEquals(Director::makeRelative("   $siteUrl/testpage   "), 'testpage');
238
		$this->assertEquals(Director::makeRelative("$siteUrlNoProtocol/testpage"), 'testpage');
239
240
		$this->assertEquals(Director::makeRelative('ftp://test.com'), 'ftp://test.com');
241
		$this->assertEquals(Director::makeRelative('http://test.com'), 'http://test.com');
242
243
		$this->assertEquals(Director::makeRelative('relative'), 'relative');
244
		$this->assertEquals(Director::makeRelative("$siteUrl/?url=http://test.com"), '?url=http://test.com');
245
246
		$this->assertEquals("test", Director::makeRelative("https://".$siteUrlNoProtocol."/test"));
247
		$this->assertEquals("test", Director::makeRelative("http://".$siteUrlNoProtocol."/test"));
248
	}
249
250
	/**
251
	 * Mostly tested by {@link testIsRelativeUrl()},
252
	 * just adding the host name matching aspect here.
253
	 */
254
	public function testIsSiteUrl() {
255
		$this->assertFalse(Director::is_site_url("http://test.com"));
256
		$this->assertTrue(Director::is_site_url(Director::absoluteBaseURL()));
0 ignored issues
show
Security Bug introduced by
It seems like \Director::absoluteBaseURL() targeting Director::absoluteBaseURL() can also be of type false; however, Director::is_site_url() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
257
		$this->assertFalse(Director::is_site_url("http://test.com?url=" . Director::absoluteBaseURL()));
258
		$this->assertFalse(Director::is_site_url("http://test.com?url=" . urlencode(Director::absoluteBaseURL())));
259
		$this->assertFalse(Director::is_site_url("//test.com?url=" . Director::absoluteBaseURL()));
260
	}
261
262
	/**
263
	 * Tests isDev, isTest, isLive set from querystring
264
	 */
265
	public function testQueryIsEnvironment() {
0 ignored issues
show
Coding Style introduced by
testQueryIsEnvironment uses the super-global variable $_SESSION 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...
Coding Style introduced by
testQueryIsEnvironment uses the super-global variable $_GET 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...
266
		// Reset
267
		unset($_SESSION['isDev']);
268
		unset($_SESSION['isLive']);
269
		unset($_GET['isTest']);
270
		unset($_GET['isDev']);
271
		$_SESSION = $_SESSION ?: array();
272
273
		// Test isDev=1
274
		$_GET['isDev'] = '1';
275
		$this->assertTrue(Director::isDev());
276
		$this->assertFalse(Director::isTest());
277
		$this->assertFalse(Director::isLive());
278
279
		// Test persistence
280
		unset($_GET['isDev']);
281
		$this->assertTrue(Director::isDev());
282
		$this->assertFalse(Director::isTest());
283
		$this->assertFalse(Director::isLive());
284
285
		// Test change to isTest
286
		$_GET['isTest'] = '1';
287
		$this->assertFalse(Director::isDev());
288
		$this->assertTrue(Director::isTest());
289
		$this->assertFalse(Director::isLive());
290
291
		// Test persistence
292
		unset($_GET['isTest']);
293
		$this->assertFalse(Director::isDev());
294
		$this->assertTrue(Director::isTest());
295
		$this->assertFalse(Director::isLive());
296
	}
297
298
	public function testResetGlobalsAfterTestRequest() {
0 ignored issues
show
Coding Style introduced by
testResetGlobalsAfterTestRequest uses the super-global variable $_GET 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...
Coding Style introduced by
testResetGlobalsAfterTestRequest uses the super-global variable $_POST 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...
Coding Style introduced by
testResetGlobalsAfterTestRequest uses the super-global variable $_COOKIE 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...
299
		$_GET = array('somekey' => 'getvalue');
300
		$_POST = array('somekey' => 'postvalue');
301
		$_COOKIE = array('somekey' => 'cookievalue');
302
303
		$cookies = Injector::inst()->createWithArgs(
304
			'Cookie_Backend',
305
			array(array('somekey' => 'sometestcookievalue'))
306
		);
307
308
		$getresponse = Director::test('errorpage?somekey=sometestgetvalue', array('somekey' => 'sometestpostvalue'),
309
			null, null, null, null, $cookies);
310
311
		$this->assertEquals('getvalue', $_GET['somekey'],
312
			'$_GET reset to original value after Director::test()');
313
		$this->assertEquals('postvalue', $_POST['somekey'],
314
			'$_POST reset to original value after Director::test()');
315
		$this->assertEquals('cookievalue', $_COOKIE['somekey'],
316
			'$_COOKIE reset to original value after Director::test()');
317
	}
318
319
	public function testTestRequestCarriesGlobals() {
320
		$fixture = array('somekey' => 'sometestvalue');
321
		foreach(array('get', 'post') as $method) {
322
			foreach(array('return%sValue', 'returnRequestValue', 'returnCookieValue') as $testfunction) {
323
				$url = 'DirectorTestRequest_Controller/' . sprintf($testfunction, ucfirst($method))
324
					. '?' . http_build_query($fixture);
325
326
				$getresponse = Director::test(
327
					$url,
328
					$fixture,
329
					null,
330
					strtoupper($method),
331
					null,
332
					null,
333
					Injector::inst()->createWithArgs('Cookie_Backend', array($fixture))
334
				);
335
336
				$this->assertInstanceOf('SS_HTTPResponse', $getresponse, 'Director::test() returns SS_HTTPResponse');
337
				$this->assertEquals($fixture['somekey'], $getresponse->getBody(), 'Director::test() ' . $testfunction);
338
			}
339
		}
340
	}
341
342
	/**
343
	 * Tests that additional parameters specified in the routing table are
344
	 * saved in the request
345
	 */
346
	public function testRouteParams() {
347
		Director::test('en-nz/myaction/myid/myotherid', null, null, null, null, null, null, $request);
348
349
		$this->assertEquals(
350
			$request->params(),
351
			array(
352
				'Controller' => 'DirectorTestRequest_Controller',
353
				'Action' => 'myaction',
354
				'ID' => 'myid',
355
				'OtherID' => 'myotherid',
356
				'Locale' => 'en_NZ'
357
			)
358
		);
359
	}
360
361
	public function testForceSSLProtectsEntireSite() {
0 ignored issues
show
Coding Style introduced by
testForceSSLProtectsEntireSite 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...
362
		$_SERVER['REQUEST_URI'] = Director::baseURL() . 'admin';
363
		$output = Director::forceSSL();
364
		$this->assertEquals($output, 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
365
366
		$_SERVER['REQUEST_URI'] = Director::baseURL() . 'some-url';
367
		$output = Director::forceSSL();
368
		$this->assertEquals($output, 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
369
	}
370
371
	public function testForceSSLOnTopLevelPagePattern() {
0 ignored issues
show
Coding Style introduced by
testForceSSLOnTopLevelPagePattern 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...
372
		$_SERVER['REQUEST_URI'] = Director::baseURL() . 'admin';
373
		$output = Director::forceSSL(array('/^admin/'));
374
		$this->assertEquals($output, 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
375
	}
376
377
	public function testForceSSLOnSubPagesPattern() {
0 ignored issues
show
Coding Style introduced by
testForceSSLOnSubPagesPattern 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...
378
		$_SERVER['REQUEST_URI'] = Director::baseURL() . Config::inst()->get('Security', 'login_url');
379
		$output = Director::forceSSL(array('/^Security/'));
380
		$this->assertEquals($output, 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
381
	}
382
383
	public function testForceSSLWithPatternDoesNotMatchOtherPages() {
0 ignored issues
show
Coding Style introduced by
testForceSSLWithPatternDoesNotMatchOtherPages 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...
384
		$_SERVER['REQUEST_URI'] = Director::baseURL() . 'normal-page';
385
		$output = Director::forceSSL(array('/^admin/'));
386
		$this->assertFalse($output);
387
388
		$_SERVER['REQUEST_URI'] = Director::baseURL() . 'just-another-page/sub-url';
389
		$output = Director::forceSSL(array('/^admin/', '/^Security/'));
390
		$this->assertFalse($output);
391
	}
392
393
	public function testForceSSLAlternateDomain() {
0 ignored issues
show
Coding Style introduced by
testForceSSLAlternateDomain 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...
394
		Config::inst()->update('Director', 'alternate_base_url', '/');
395
		$_SERVER['REQUEST_URI'] = Director::baseURL() . 'admin';
396
		$output = Director::forceSSL(array('/^admin/'), 'secure.mysite.com');
397
		$this->assertEquals($output, 'https://secure.mysite.com/admin');
398
	}
399
400
	/**
401
	 * @covers Director::extract_request_headers()
402
	 */
403
	public function testExtractRequestHeaders() {
404
		$request = array(
405
			'REDIRECT_STATUS'      => '200',
406
			'HTTP_HOST'            => 'host',
407
			'HTTP_USER_AGENT'      => 'User Agent',
408
			'HTTP_ACCEPT'          => 'text/html',
409
			'HTTP_ACCEPT_LANGUAGE' => 'en-us',
410
			'HTTP_COOKIE'          => 'MyCookie=1',
411
			'SERVER_PROTOCOL'      => 'HTTP/1.1',
412
			'REQUEST_METHOD'       => 'GET',
413
			'REQUEST_URI'          => '/',
414
			'SCRIPT_NAME'          => FRAMEWORK_DIR . '/main.php',
415
			'CONTENT_TYPE'         => 'text/xml',
416
			'CONTENT_LENGTH'       => 10
417
		);
418
419
		$headers = array(
420
			'Host'            => 'host',
421
			'User-Agent'      => 'User Agent',
422
			'Accept'          => 'text/html',
423
			'Accept-Language' => 'en-us',
424
			'Cookie'          => 'MyCookie=1',
425
			'Content-Type'    => 'text/xml',
426
			'Content-Length'  => '10'
427
		);
428
429
		$this->assertEquals($headers, Director::extract_request_headers($request));
430
	}
431
432
	public function testUnmatchedRequestReturns404() {
433
		$this->assertEquals(404, Director::test('no-route')->getStatusCode());
434
	}
435
436
	public function testIsHttps() {
0 ignored issues
show
Coding Style introduced by
testIsHttps 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...
437
		if(!TRUSTED_PROXY) {
438
			$this->markTestSkipped('Test cannot be run without trusted proxy');
439
		}
440
		// nothing available
441
		$headers = array(
442
			'HTTP_X_FORWARDED_PROTOCOL', 'HTTPS', 'SSL'
443
		);
444
445
		$origServer = $_SERVER;
446
447
		foreach($headers as $header) {
448
			if(isset($_SERVER[$header])) {
449
				unset($_SERVER['HTTP_X_FORWARDED_PROTOCOL']);
450
			}
451
		}
452
453
		$this->assertFalse(Director::is_https());
454
455
		$_SERVER['HTTP_X_FORWARDED_PROTOCOL'] = 'https';
456
		$this->assertTrue(Director::is_https());
457
458
		$_SERVER['HTTP_X_FORWARDED_PROTOCOL'] = 'http';
459
		$this->assertFalse(Director::is_https());
460
461
		$_SERVER['HTTP_X_FORWARDED_PROTOCOL'] = 'ftp';
462
		$this->assertFalse(Director::is_https());
463
464
		$_SERVER['HTTP_X_FORWARDED_PROTO'] = 'https';
465
		$this->assertTrue(Director::is_https());
466
467
		$_SERVER['HTTP_X_FORWARDED_PROTO'] = 'http';
468
		$this->assertFalse(Director::is_https());
469
470
		$_SERVER['HTTP_X_FORWARDED_PROTO'] = 'ftp';
471
		$this->assertFalse(Director::is_https());
472
473
		$_SERVER['HTTP_FRONT_END_HTTPS'] = 'On';
474
		$this->assertTrue(Director::is_https());
475
476
		$_SERVER['HTTP_FRONT_END_HTTPS'] = 'Off';
477
		$this->assertFalse(Director::is_https());
478
479
		// https via HTTPS
480
		$_SERVER['HTTPS'] = 'true';
481
		$this->assertTrue(Director::is_https());
482
483
		$_SERVER['HTTPS'] = '1';
484
		$this->assertTrue(Director::is_https());
485
486
		$_SERVER['HTTPS'] = 'off';
487
		$this->assertFalse(Director::is_https());
488
489
		// https via SSL
490
		$_SERVER['SSL'] = '';
491
		$this->assertTrue(Director::is_https());
492
493
		$_SERVER = $origServer;
494
	}
495
496
	public function testTestIgnoresHashes() {
497
		//test that hashes are ignored
498
		$url = "DirectorTestRequest_Controller/returnGetValue?somekey=key";
499
		$hash = "#test";
500
		$response = Director::test($url . $hash, null, null, null, null, null, null, $request);
501
		$this->assertFalse($response->isError());
502
		$this->assertEquals('key', $response->getBody());
503
		$this->assertEquals($request->getURL(true), $url);
504
505
		//test encoded hashes are accepted
506
		$url = "DirectorTestRequest_Controller/returnGetValue?somekey=test%23key";
507
		$response = Director::test($url, null, null, null, null, null, null, $request);
508
		$this->assertFalse($response->isError());
509
		$this->assertEquals('test#key', $response->getBody());
510
		$this->assertEquals($request->getURL(true), $url);
511
	}
512
513
	public function testRequestFilterInDirectorTest() {
514
		$filter = new TestRequestFilter;
515
516
		$processor = new RequestProcessor(array($filter));
517
518
		Injector::inst()->registerService($processor, 'RequestProcessor');
519
520
		$response = Director::test('some-dummy-url');
521
522
		$this->assertEquals(1, $filter->preCalls);
523
		$this->assertEquals(1, $filter->postCalls);
524
525
		$filter->failPost = true;
526
527
		$this->setExpectedException('SS_HTTPResponse_Exception');
528
529
		$response = Director::test('some-dummy-url');
530
531
		$this->assertEquals(2, $filter->preCalls);
532
		$this->assertEquals(2, $filter->postCalls);
533
534
		$filter->failPre = true;
535
536
		$response = Director::test('some-dummy-url');
537
538
		$this->assertEquals(3, $filter->preCalls);
539
540
		// preCall 'false' will trigger an exception and prevent post call execution
541
		$this->assertEquals(2, $filter->postCalls);
542
	}
543
}
544
545
class TestRequestFilter implements RequestFilter, TestOnly {
546
	public $preCalls = 0;
547
	public $postCalls = 0;
548
549
	public $failPre = false;
550
	public $failPost = false;
551
552
	public function preRequest(\SS_HTTPRequest $request, \Session $session, \DataModel $model) {
553
		++$this->preCalls;
554
555
		if ($this->failPre) {
556
			return false;
557
		}
558
	}
559
560
	public function postRequest(\SS_HTTPRequest $request, \SS_HTTPResponse $response, \DataModel $model) {
561
		++$this->postCalls;
562
563
		if ($this->failPost) {
564
			return false;
565
		}
566
	}
567
568
	public function reset() {
569
		$this->preCalls = 0;
570
		$this->postCalls = 0;
571
	}
572
573
}
574
575
class DirectorTestRequest_Controller extends Controller implements TestOnly {
576
577
	private static $allowed_actions = array(
578
		'returnGetValue',
579
		'returnPostValue',
580
		'returnRequestValue',
581
		'returnCookieValue',
582
	);
583
584
	public function returnGetValue($request)		{ return $_GET['somekey']; }
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Coding Style introduced by
returnGetValue uses the super-global variable $_GET 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...
585
586
	public function returnPostValue($request)		{ return $_POST['somekey']; }
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Coding Style introduced by
returnPostValue uses the super-global variable $_POST 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...
587
588
	public function returnRequestValue($request)	{ return $_REQUEST['somekey']; }
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Coding Style introduced by
returnRequestValue uses the super-global variable $_REQUEST 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...
589
590
	public function returnCookieValue($request)		{ return $_COOKIE['somekey']; }
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Coding Style introduced by
returnCookieValue uses the super-global variable $_COOKIE 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...
591
592
}
593