Skip to content

Commit

Permalink
Update EasyHandle in order to avoid bad writes of properties relate…
Browse files Browse the repository at this point in the history
…d to the handle
  • Loading branch information
phansys committed Jun 8, 2021
1 parent de6f1e5 commit e2720da
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 15 deletions.
57 changes: 42 additions & 15 deletions src/Handler/EasyHandle.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,39 +26,39 @@ final class EasyHandle
public $sink;

/**
* @var array Received HTTP headers so far
* @var RequestInterface Request being sent
*/
public $headers = [];
public $request;

/**
* @var ResponseInterface|null Received response (if any)
* @var array Request options
*/
public $response;
public $options = [];

/**
* @var RequestInterface Request being sent
* @var \Throwable|null Exception during on_headers (if any)
*/
public $request;
public $onHeadersException;

/**
* @var array Request options
* @var \Exception|null Exception during createResponse (if any)
*/
public $options = [];
public $createResponseException;

/**
* @var int cURL error number (if any)
*/
public $errno = 0;
private $errno = CURLE_OK;

/**
* @var \Throwable|null Exception during on_headers (if any)
* @var array Received HTTP headers so far
*/
public $onHeadersException;
private $headers = [];

/**
* @var \Exception|null Exception during createResponse (if any)
* @var ResponseInterface|null Received response (if any)
*/
public $createResponseException;
private $response;

/**
* Attach a response to the easy handle based on the received headers.
Expand Down Expand Up @@ -104,9 +104,36 @@ public function createResponse(): void
*
* @throws \BadMethodCallException
*/
public function __get($name)
public function &__get($name)
{
$msg = $name === 'handle' ? 'The EasyHandle has been released' : 'Invalid property: ' . $name;
if (in_array($name, ['errno', 'headers', 'onHeadersException', 'response'], true)) {
return $this->{$name};
}

$msg = $name === 'handle'
? 'The EasyHandle has been released'
: sprintf('Undefined property: %s::$%s', __CLASS__, $name);

throw new \BadMethodCallException($msg);
}

public function __set($name, $value)
{
if (in_array($name, ['errno', 'headers', 'onHeadersException', 'response'], true)) {
if ('response' === $name) {
// BC: Change to `\Error` when bumping PHP version to ^7.0
throw new \LogicException(sprintf('Cannot set private property %s::$%s', __CLASS__, $name));
}

if (!isset($this->handle) || !is_resource($this->handle) || 'curl' !== get_resource_type($this->handle)) {
throw new \LogicException(sprintf('Property %s::$%s could not be set when there isn\'t a valid handle', __CLASS__, $name));
}

if ('errno' === $name && CURLE_OK !== ($handleErrno = curl_errno($this->handle)) && $value !== $handleErrno) {
throw new \LogicException(sprintf('Property %s::$errno could not be set with %u since the handle is reporting error %u', __CLASS__, $value, $handleErrno));
}
}

$this->{$name} = $value;
}
}
102 changes: 102 additions & 0 deletions tests/Handler/EasyHandleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,106 @@ public function testEnsuresHandleExists()
$this->expectExceptionMessage('The EasyHandle has been released');
$easy->handle;
}

public function testSettingProperties()
{
$handle = curl_init();
$stream = new Psr7\Stream(fopen('php://temp', 'r'));
$headers = [];
$request = new Psr7\Request('HEAD', '/');
$options = [];
$easy = new EasyHandle;
$easy->handle = $handle;
$easy->sink = $stream;
$easy->headers = $headers;
$easy->request = $request;
$easy->options = $options;

$this->assertSame($handle, $easy->handle);
$this->assertSame($stream, $easy->sink);
$this->assertSame($headers, $easy->headers);
$this->assertSame($request, $easy->request);
$this->assertSame($options, $easy->options);
curl_close($easy->handle);
unset($handle, $stream, $easy->handle);
}

/**
* @expectedException \LogicException
* @expectedExceptionMessage Property GuzzleHttp\Handler\EasyHandle::$headers could not be set when there isn't a valid handle
*/
public function testSettingHeadersWithoutHandle()
{
$easy = new EasyHandle;
$easy->headers = [];
}

public function testSettingErrnoWithHandle()
{
$easy = new EasyHandle;
$easy->handle = curl_init();
$easy->errno = CURLE_OK;

$this->assertSame(CURLE_OK, $easy->errno);

curl_close($easy->handle);
unset($easy->handle);
}

/**
* @expectedException \LogicException
* @expectedExceptionMessage Property GuzzleHttp\Handler\EasyHandle::$errno could not be set with 0 since the handle is reporting error 3
*/
public function testChangingHandleErrno()
{
$easy = new EasyHandle;
$easy->handle = curl_init();
curl_exec($easy->handle);
$easy->errno = CURLE_OK;

$this->assertSame(CURLE_OK, $easy->errno);

curl_close($easy->handle);
unset($easy->handle);
}

/**
* @expectedException \LogicException
* @expectedExceptionMessage Cannot set private property GuzzleHttp\Handler\EasyHandle::$response
*/
public function testSettingResponse()
{
$easy = new EasyHandle;
$easy->response = new Psr7\Response();
}

/**
* @expectedException \LogicException
* @expectedExceptionMessage Property GuzzleHttp\Handler\EasyHandle::$errno could not be set when there isn't a valid handle
*/
public function testSettingErrnoWithoutHandle()
{
$easy = new EasyHandle;
$easy->errno = CURLE_COULDNT_RESOLVE_HOST;
}

/**
* @expectedException \LogicException
* @expectedExceptionMessage Undefined property: GuzzleHttp\Handler\EasyHandle::$nonexistent
*/
public function testGetInvalidProperty()
{
$easy = new EasyHandle;
$easy->nonexistent;
}

public function testPropertyOverload()
{
$overloadedValue = 42;

$easy = new EasyHandle;
$easy->nonexistent = $overloadedValue;

$this->assertSame($overloadedValue, $easy->nonexistent);
}
}

0 comments on commit e2720da

Please sign in to comment.