Proje dosyaları eklendi

This commit is contained in:
2026-08-29 15:53:53 +03:00
commit d3313400ca
440 changed files with 24827 additions and 0 deletions
@@ -0,0 +1,131 @@
<?php
namespace App\Serp\Providers\DataForSeo;
use App\Serp\Data\SerpResponse;
use App\Serp\Data\SerpResultData;
use App\Serp\Data\SerpSubmission;
use App\Serp\Data\SerpTaskResult;
use App\Serp\Exceptions\UnexpectedProviderResponseException;
final class DataForSeoResponseNormalizer
{
public function __construct(private DataForSeoErrorClassifier $errors) {}
public function live(array $data, int $limit): SerpResponse
{
$task = $this->task($data, [20000]);
return $this->response($task, $limit);
}
public function submission(array $data): SerpSubmission
{
$task = $this->task($data, [20000, 20100]);
$id = $task['id'] ?? null;
if (! is_string($id) || ! preg_match('/^[0-9a-f-]{36}$/i', $id)) {
throw new UnexpectedProviderResponseException('DataForSEO task id is missing.');
}
return new SerpSubmission('dataforseo', $id, $this->cost($task, $data), 'USD', (int) $task['status_code']);
}
public function collection(array $data, string $taskId, int $limit): SerpTaskResult
{
$this->envelope($data);
$tasks = $data['tasks'];
if ($tasks === [] || ! isset($tasks[0]) || ! is_array($tasks[0])) {
return SerpTaskResult::waiting();
}
$task = $tasks[0];
if (($task['id'] ?? null) !== $taskId) {
throw new UnexpectedProviderResponseException('DataForSEO task id mismatch.');
}
$status = $task['status_code'] ?? null;
if (! is_int($status)) {
throw new UnexpectedProviderResponseException('DataForSEO task status is missing.');
}
if (in_array($status, [20100, 40601, 40602], true) || ! array_key_exists('result', $task) || $task['result'] === null) {
return SerpTaskResult::waiting($status);
}
if ($status !== 20000) {
throw $this->errors->api($status);
}
$response = $this->response($task, $limit);
return SerpTaskResult::ready($response, $this->cost($task, $data), 'USD', $status);
}
private function envelope(array $data): void
{
$status = $data['status_code'] ?? null;
if (! is_int($status)) {
throw new UnexpectedProviderResponseException('DataForSEO envelope status is missing.');
}if ($status !== 20000) {
throw $this->errors->api($status);
}if (! isset($data['tasks']) || ! is_array($data['tasks'])) {
throw new UnexpectedProviderResponseException('DataForSEO tasks are missing.');
}
}
private function task(array $data, array $accepted): array
{
$this->envelope($data);
$task = $data['tasks'][0] ?? null;
if (! is_array($task) || ! is_int($task['status_code'] ?? null)) {
throw new UnexpectedProviderResponseException('DataForSEO task is malformed.');
}if (! in_array($task['status_code'], $accepted, true)) {
throw $this->errors->api($task['status_code']);
}
return $task;
}
private function response(array $task, int $limit): SerpResponse
{
$result = $task['result'] ?? null;
if (! is_array($result) || ! isset($result[0]) || ! is_array($result[0])) {
throw new UnexpectedProviderResponseException('DataForSEO result is missing.');
}
$items = $result[0]['items'] ?? null;
if (! is_array($items)) {
throw new UnexpectedProviderResponseException('DataForSEO items are missing.');
}
$rows = [];
$seen = [];
foreach ($items as $item) {
if (! is_array($item) || ($item['type'] ?? null) !== 'organic') {
continue;
}$position = $item['rank_group'] ?? null;
$url = $item['url'] ?? null;
if (! is_int($position) || $position < 1 || $position > $limit || ! is_string($url) || isset($seen[$position])) {
continue;
}try {
$rows[] = new SerpResultData($position, $url, $this->text($item['title'] ?? null, 500), $this->text($item['breadcrumb'] ?? null, 500), $this->text($item['description'] ?? null, 5000));
$seen[$position] = true;
} catch (\InvalidArgumentException) {
}
}
usort($rows, fn ($a, $b) => $a->position <=> $b->position);
return new SerpResponse('dataforseo', $rows, is_string($task['id'] ?? null) ? $task['id'] : null, $limit, $this->cost($task, []), 'USD', is_int($task['status_code'] ?? null) ? $task['status_code'] : null);
}
private function text(mixed $value, int $max): ?string
{
if ($value === null) {
return null;
}if (! is_string($value)) {
return null;
}
return mb_substr($value, 0, $max);
}
private function cost(array $task, array $data): ?float
{
$cost = $task['cost'] ?? $data['cost'] ?? null;
return is_int($cost) || is_float($cost) ? (float) $cost : null;
}
}