Skip to content

Commit

Permalink
refactoring: use symfony/http-client
Browse files Browse the repository at this point in the history
  • Loading branch information
Gemorroj committed Feb 7, 2021
1 parent b3321ac commit cc5b88a
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 88 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"license": "LGPL-3.0",
"require": {
"php": ">=7.3",
"ext-json": "*"
"symfony/http-client": "^4.4.11|^5.0"
},
"authors": [
{
Expand Down
160 changes: 73 additions & 87 deletions src/HTMLValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

namespace HTMLValidator;

use Symfony\Component\HttpClient\HttpClient;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

class HTMLValidator
{
/**
Expand All @@ -17,17 +25,22 @@ class HTMLValidator
private $options;

/**
* Default context for http request.
*
* @see https://www.php.net/manual/en/context.php
*
* @var array
* @var HttpClientInterface
*/
private $context = [];
private $httpClient;

public function __construct(Options $options = null)
public function __construct(Options $options = null, HttpClientInterface $httpClient = null)
{
$this->setOptions($options ?: new Options());
if (!$httpClient) {
$httpClient = HttpClient::createForBaseUri($this->getValidatorUri(), [
'headers' => [
'User-Agent' => 'gemorroj/htmlvalidator',
'Content-Type' => 'text/html; charset=UTF-8',
],
]);
}
$this->setHttpClient($httpClient);
}

public function getOptions(): Options
Expand All @@ -42,14 +55,14 @@ public function setOptions(Options $options): self
return $this;
}

public function getContext(): array
public function getHttpClient(): HttpClientInterface
{
return $this->context;
return $this->httpClient;
}

public function setContext(array $context): self
public function setHttpClient(HttpClientInterface $httpClient): self
{
$this->context = $context;
$this->httpClient = $httpClient;

return $this;
}
Expand All @@ -66,27 +79,6 @@ public function setValidatorUri(string $validatorUri): self
return $this;
}

/**
* @throws Exception
*/
protected function sendRequest(string $uri, array $context): string
{
$context = \array_merge($this->getContext(), $context);

if (isset($context['http']['header'])) {
$context['http']['header'] .= "\r\nUser-Agent: gemorroj/htmlvalidator";
} else {
$context['http']['header'] = 'User-Agent: gemorroj/htmlvalidator';
}

$data = @\file_get_contents($uri, false, \stream_context_create($context));
if (false === $data) {
throw new Exception(\error_get_last()['message']);
}

return $data;
}

/**
* Validates a given URI.
*
Expand All @@ -95,97 +87,91 @@ protected function sendRequest(string $uri, array $context): string
*
* @param string $uri The address to the page to validate ex: http://example.com/
*
* @throws Exception|\JsonException
* @throws Exception
* @throws DecodingExceptionInterface When the body cannot be decoded to an array
* @throws TransportExceptionInterface When a network error occurs
* @throws RedirectionExceptionInterface On a 3xx when $throw is true and the "max_redirects" option has been reached
* @throws ClientExceptionInterface On a 4xx when $throw is true
* @throws ServerExceptionInterface On a 5xx when $throw is true
*/
public function validateUri(string $uri): Response
{
$query = \http_build_query(\array_merge(
$this->getOptions()->buildOptions(),
['doc' => $uri, 'out' => 'json', 'showsource' => 'yes']
));

$context = [
'http' => [
'method' => 'GET',
'header' => 'Content-Type: text/html; charset=utf-8',
],
];

$data = $this->sendRequest($this->validatorUri.'?'.$query, $context);

return $this->parseJsonResponse($data);
$response = $this->getHttpClient()->request('GET', '', [
'query' => \array_merge(
$this->getOptions()->buildOptions(),
['doc' => $uri, 'out' => 'json', 'showsource' => 'yes']
),
]);

return $this->parseResponse($response->toArray());
}

/**
* Validates the local file.
*
* Requests validation on the local file, from an instance of the W3C validator.
*
* @param string $file file to be validated
* @param string $file path to file to be validated
*
* @throws Exception|\JsonException
*
* @return Response object HTMLValidator\Response
* @throws Exception
* @throws DecodingExceptionInterface When the body cannot be decoded to an array
* @throws TransportExceptionInterface When a network error occurs
* @throws RedirectionExceptionInterface On a 3xx when $throw is true and the "max_redirects" option has been reached
* @throws ClientExceptionInterface On a 4xx when $throw is true
* @throws ServerExceptionInterface On a 5xx when $throw is true
*/
public function validateFile(string $file): Response
{
if (true !== \file_exists($file)) {
throw new Exception('File not found');
}
if (true !== \is_readable($file)) {
throw new Exception('File not readable');
}

$data = @\file_get_contents($file);
if (false === $data) {
$f = @\fopen($file, 'rb');
if (false === $f) {
throw new Exception(\error_get_last()['message']);
}

return $this->validateFragment($data);
$response = $this->getHttpClient()->request('POST', '', [
'body' => $f,
'query' => \array_merge(
$this->getOptions()->buildOptions(),
['out' => 'json', 'showsource' => 'yes']
),
]);

return $this->parseResponse($response->toArray());
}

/**
* Validate an html string.
*
* @param string $html full html document fragment
*
* @throws Exception|\JsonException
*
* @return Response object HTMLValidator\Response
* @throws Exception
* @throws DecodingExceptionInterface When the body cannot be decoded to an array
* @throws TransportExceptionInterface When a network error occurs
* @throws RedirectionExceptionInterface On a 3xx when $throw is true and the "max_redirects" option has been reached
* @throws ClientExceptionInterface On a 4xx when $throw is true
* @throws ServerExceptionInterface On a 5xx when $throw is true
*/
public function validateFragment(string $html): Response
{
$query = \http_build_query(\array_merge(
$this->getOptions()->buildOptions(),
['out' => 'json', 'showsource' => 'yes']
));

$context = [
'http' => [
'method' => 'POST',
'header' => 'Content-Type: text/html; charset=utf-8',
'content' => $html,
],
];

$data = $this->sendRequest($this->validatorUri.'?'.$query, $context);

return $this->parseJsonResponse($data);
$response = $this->getHttpClient()->request('POST', '', [
'body' => $html,
'query' => \array_merge(
$this->getOptions()->buildOptions(),
['out' => 'json', 'showsource' => 'yes']
),
]);

return $this->parseResponse($response->toArray());
}

/**
* Parse an JSON response from the validator.
*
* This function parses a JSON response json string from the validator.
*
* @param string $json the raw JSON response from the validator
*
* @throws Exception|\JsonException
* @throws Exception
*/
protected function parseJsonResponse(string $json): Response
protected function parseResponse(array $data): Response
{
$data = \json_decode($json, true, 512, \JSON_THROW_ON_ERROR);

$response = new Response();

$response->setEncoding($data['source']['encoding'] ?? null);
Expand Down

0 comments on commit cc5b88a

Please sign in to comment.