Skip to content

Commit c6f9b72

Browse files
committed
Centralise and allow override of site verify URL
1 parent 42f8ca5 commit c6f9b72

File tree

7 files changed

+133
-60
lines changed

7 files changed

+133
-60
lines changed

src/ReCaptcha/ReCaptcha.php

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,30 @@ class ReCaptcha
3737
*/
3838
const VERSION = 'php_1.2';
3939

40+
/**
41+
* URL for reCAPTCHA sitevrerify API
42+
* @const string
43+
*/
44+
const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
45+
4046
/**
4147
* Invalid JSON received
4248
* @const string
4349
*/
4450
const E_INVALID_JSON = 'invalid-json';
4551

52+
/**
53+
* Could not connect to service
54+
* @const string
55+
*/
56+
const E_BAD_CONNECTION = 'bad-connection';
57+
58+
/**
59+
* Did not receive a 200 from the service
60+
* @const string
61+
*/
62+
const E_BAD_RESPONSE = 'bad-response';
63+
4664
/**
4765
* Not a success, but no error codes received!
4866
* @const string
@@ -115,12 +133,7 @@ public function __construct($secret, RequestMethod $requestMethod = null)
115133
}
116134

117135
$this->secret = $secret;
118-
119-
if (!is_null($requestMethod)) {
120-
$this->requestMethod = $requestMethod;
121-
} else {
122-
$this->requestMethod = new RequestMethod\Post();
123-
}
136+
$this->requestMethod = (is_null($requestMethod)) ? new RequestMethod\Post() : $requestMethod;
124137
}
125138

126139
/**

src/ReCaptcha/RequestMethod/CurlPost.php

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
namespace ReCaptcha\RequestMethod;
2828

29+
use ReCaptcha\ReCaptcha;
2930
use ReCaptcha\RequestMethod;
3031
use ReCaptcha\RequestParameters;
3132

@@ -36,25 +37,28 @@
3637
*/
3738
class CurlPost implements RequestMethod
3839
{
39-
/**
40-
* URL to which requests are sent via cURL.
41-
* @const string
42-
*/
43-
const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
44-
4540
/**
4641
* Curl connection to the reCAPTCHA service
4742
* @var Curl
4843
*/
4944
private $curl;
5045

51-
public function __construct(Curl $curl = null)
46+
/**
47+
* URL for reCAPTCHA sitevrerify API
48+
* @var string
49+
*/
50+
private $siteVerifyUrl;
51+
52+
/**
53+
* Only needed if you want to override the defaults
54+
*
55+
* @param Curl $curl Curl resource
56+
* @param string $siteVerifyUrl URL for reCAPTCHA sitevrerify API
57+
*/
58+
public function __construct(Curl $curl = null, $siteVerifyUrl = null)
5259
{
53-
if (!is_null($curl)) {
54-
$this->curl = $curl;
55-
} else {
56-
$this->curl = new Curl();
57-
}
60+
$this->curl = (is_null($curl)) ? new Curl() : $curl;
61+
$this->siteVerifyUrl = (is_null($siteVerifyUrl)) ? ReCaptcha::SITE_VERIFY_URL : $siteVerifyUrl;
5862
}
5963

6064
/**
@@ -65,7 +69,7 @@ public function __construct(Curl $curl = null)
6569
*/
6670
public function submit(RequestParameters $params)
6771
{
68-
$handle = $this->curl->init(self::SITE_VERIFY_URL);
72+
$handle = $this->curl->init($this->siteVerifyUrl);
6973

7074
$options = array(
7175
CURLOPT_POST => true,

src/ReCaptcha/RequestMethod/Post.php

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
namespace ReCaptcha\RequestMethod;
2828

29+
use ReCaptcha\ReCaptcha;
2930
use ReCaptcha\RequestMethod;
3031
use ReCaptcha\RequestParameters;
3132

@@ -35,10 +36,20 @@
3536
class Post implements RequestMethod
3637
{
3738
/**
38-
* URL to which requests are POSTed.
39-
* @const string
39+
* URL for reCAPTCHA sitevrerify API
40+
* @var string
4041
*/
41-
const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
42+
private $siteVerifyUrl;
43+
44+
/**
45+
* Only needed if you want to override the defaults
46+
*
47+
* @param string $siteVerifyUrl URL for reCAPTCHA sitevrerify API
48+
*/
49+
public function __construct($siteVerifyUrl = null)
50+
{
51+
$this->siteVerifyUrl = (is_null($siteVerifyUrl)) ? ReCaptcha::SITE_VERIFY_URL : $siteVerifyUrl;
52+
}
4253

4354
/**
4455
* Submit the POST request with the specified parameters.
@@ -48,23 +59,16 @@ class Post implements RequestMethod
4859
*/
4960
public function submit(RequestParameters $params)
5061
{
51-
/**
52-
* PHP 5.6.0 changed the way you specify the peer name for SSL context options.
53-
* Using "CN_name" will still work, but it will raise deprecated errors.
54-
*/
55-
$peer_key = version_compare(PHP_VERSION, '5.6.0', '<') ? 'CN_name' : 'peer_name';
5662
$options = array(
5763
'http' => array(
5864
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
5965
'method' => 'POST',
6066
'content' => $params->toQueryString(),
6167
// Force the peer to validate (not needed in 5.6.0+, but still works)
6268
'verify_peer' => true,
63-
// Force the peer validation to use www.google.com
64-
$peer_key => 'www.google.com',
6569
),
6670
);
6771
$context = stream_context_create($options);
68-
return file_get_contents(self::SITE_VERIFY_URL, false, $context);
72+
return file_get_contents($this->siteVerifyUrl, false, $context);
6973
}
7074
}

src/ReCaptcha/RequestMethod/SocketPost.php

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
namespace ReCaptcha\RequestMethod;
2828

29+
use ReCaptcha\ReCaptcha;
2930
use ReCaptcha\RequestMethod;
3031
use ReCaptcha\RequestParameters;
3132

@@ -36,26 +37,15 @@
3637
*/
3738
class SocketPost implements RequestMethod
3839
{
39-
/**
40-
* reCAPTCHA service host.
41-
* @const string
42-
*/
43-
const RECAPTCHA_HOST = 'www.google.com';
44-
45-
/**
46-
* @const string reCAPTCHA service path
47-
*/
48-
const SITE_VERIFY_PATH = '/recaptcha/api/siteverify';
49-
5040
/**
5141
* @const string Bad request error
5242
*/
53-
const BAD_REQUEST = '{"success": false, "error-codes": ["invalid-request"]}';
43+
const BAD_CONNECTION = '{"success": false, "error-codes": ["'.ReCaptcha::E_BAD_CONNECTION.'"]}';
5444

5545
/**
5646
* @const string Bad response error
5747
*/
58-
const BAD_RESPONSE = '{"success": false, "error-codes": ["invalid-response"]}';
48+
const BAD_RESPONSE = '{"success": false, "error-codes": ["'.ReCaptcha::E_BAD_RESPONSE.'"]}';
5949

6050
/**
6151
* Socket to the reCAPTCHA service
@@ -64,17 +54,15 @@ class SocketPost implements RequestMethod
6454
private $socket;
6555

6656
/**
67-
* Constructor
57+
* Only needed if you want to override the defaults
6858
*
6959
* @param \ReCaptcha\RequestMethod\Socket $socket optional socket, injectable for testing
60+
* @param string $siteVerifyUrl URL for reCAPTCHA sitevrerify API
7061
*/
71-
public function __construct(Socket $socket = null)
62+
public function __construct(Socket $socket = null, $siteVerifyUrl = null)
7263
{
73-
if (!is_null($socket)) {
74-
$this->socket = $socket;
75-
} else {
76-
$this->socket = new Socket();
77-
}
64+
$this->socket = (is_null($socket)) ? new Socket() : $socket;
65+
$this->siteVerifyUrl = (is_null($siteVerifyUrl)) ? ReCaptcha::SITE_VERIFY_URL : $siteVerifyUrl;
7866
}
7967

8068
/**
@@ -87,15 +75,16 @@ public function submit(RequestParameters $params)
8775
{
8876
$errno = 0;
8977
$errstr = '';
78+
$urlParsed = parse_url($this->siteVerifyUrl);
9079

91-
if (false === $this->socket->fsockopen('ssl://' . self::RECAPTCHA_HOST, 443, $errno, $errstr, 30)) {
92-
return self::BAD_REQUEST;
80+
if (false === $this->socket->fsockopen('ssl://' . $urlParsed['host'], 443, $errno, $errstr, 30)) {
81+
return self::BAD_CONNECTION;
9382
}
9483

9584
$content = $params->toQueryString();
9685

97-
$request = "POST " . self::SITE_VERIFY_PATH . " HTTP/1.1\r\n";
98-
$request .= "Host: " . self::RECAPTCHA_HOST . "\r\n";
86+
$request = "POST " . $urlParsed['path'] . " HTTP/1.1\r\n";
87+
$request .= "Host: " . $urlParsed['host'] . "\r\n";
9988
$request .= "Content-Type: application/x-www-form-urlencoded\r\n";
10089
$request .= "Content-length: " . strlen($content) . "\r\n";
10190
$request .= "Connection: close\r\n\r\n";

tests/ReCaptcha/RequestMethod/CurlPostTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,30 @@ public function testSubmit()
6262
$response = $pc->submit(new RequestParameters("secret", "response"));
6363
$this->assertEquals('RESPONSEBODY', $response);
6464
}
65+
66+
public function testOverrideSiteVerifyUrl()
67+
{
68+
$url = 'OVERRIDE';
69+
70+
$curl = $this->getMockBuilder(\ReCaptcha\RequestMethod\Curl::class)
71+
->disableOriginalConstructor()
72+
->setMethods(array('init', 'setoptArray', 'exec', 'close'))
73+
->getMock();
74+
$curl->expects($this->once())
75+
->method('init')
76+
->with($url)
77+
->willReturn(new \stdClass);
78+
$curl->expects($this->once())
79+
->method('setoptArray')
80+
->willReturn(true);
81+
$curl->expects($this->once())
82+
->method('exec')
83+
->willReturn('RESPONSEBODY');
84+
$curl->expects($this->once())
85+
->method('close');
86+
87+
$pc = new CurlPost($curl, $url);
88+
$response = $pc->submit(new RequestParameters("secret", "response"));
89+
$this->assertEquals('RESPONSEBODY', $response);
90+
}
6591
}

tests/ReCaptcha/RequestMethod/PostTest.php

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,20 @@ public function testSSLContextOptions()
6161
$this->assertEquals(1, $this->runcount, "The assertion was ran");
6262
}
6363

64+
public function testOverrideVerifyUrl()
65+
{
66+
$req = new Post('https://over.ride/some/path');
67+
self::$assert = array($this, 'overrideUrlOptions');
68+
$req->submit($this->parameters);
69+
$this->assertEquals(1, $this->runcount, "The assertion was ran");
70+
}
71+
72+
public function overrideUrlOptions(array $args)
73+
{
74+
$this->runcount++;
75+
$this->assertEquals('https://over.ride/some/path', $args[0]);
76+
}
77+
6478
public function httpContextOptionsCallback(array $args)
6579
{
6680
$this->runcount++;
@@ -93,11 +107,6 @@ public function sslContextOptionsCallback(array $args)
93107
$this->assertArrayHasKey('http', $options);
94108
$this->assertArrayHasKey('verify_peer', $options['http']);
95109
$this->assertTrue($options['http']['verify_peer']);
96-
97-
$key = version_compare(PHP_VERSION, "5.6.0", "<") ? "CN_name" : "peer_name";
98-
99-
$this->assertArrayHasKey($key, $options['http']);
100-
$this->assertEquals("www.google.com", $options['http'][$key]);
101110
}
102111

103112
protected function assertCommonOptions(array $args)

tests/ReCaptcha/RequestMethod/SocketPostTest.php

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,34 @@ public function testSubmitSuccess()
5757
$this->assertEquals('RESPONSEBODY', $response);
5858
}
5959

60+
public function testOverrideSiteVerifyUrl()
61+
{
62+
$socket = $this->getMockBuilder(\ReCaptcha\RequestMethod\Socket::class)
63+
->disableOriginalConstructor()
64+
->setMethods(array('fsockopen', 'fwrite', 'fgets', 'feof', 'fclose'))
65+
->getMock();
66+
$socket->expects($this->once())
67+
->method('fsockopen')
68+
->with('ssl://over.ride', 443, 0, '', 30)
69+
->willReturn(true);
70+
$socket->expects($this->once())
71+
->method('fwrite')
72+
->with($this->matchesRegularExpression('/^POST \/some\/path.*Host: over\.ride/s'));
73+
$socket->expects($this->once())
74+
->method('fgets')
75+
->willReturn("HTTP/1.1 200 OK\n\nRESPONSEBODY");
76+
$socket->expects($this->exactly(2))
77+
->method('feof')
78+
->will($this->onConsecutiveCalls(false, true));
79+
$socket->expects($this->once())
80+
->method('fclose')
81+
->willReturn(true);
82+
83+
$ps = new SocketPost($socket, 'https://over.ride/some/path');
84+
$response = $ps->submit(new RequestParameters("secret", "response", "remoteip", "version"));
85+
$this->assertEquals('RESPONSEBODY', $response);
86+
}
87+
6088
public function testSubmitBadResponse()
6189
{
6290
$socket = $this->getMockBuilder(\ReCaptcha\RequestMethod\Socket::class)
@@ -94,6 +122,6 @@ public function testSubmitBadRequest()
94122
->willReturn(false);
95123
$ps = new SocketPost($socket);
96124
$response = $ps->submit(new RequestParameters("secret", "response", "remoteip", "version"));
97-
$this->assertEquals(SocketPost::BAD_REQUEST, $response);
125+
$this->assertEquals(SocketPost::BAD_CONNECTION, $response);
98126
}
99127
}

0 commit comments

Comments
 (0)