feat: implement parallel SOAP requests for vehicle verification and add supervisor configuration
This commit is contained in:
parent
b17b6608d3
commit
f2a15ef113
@ -17,6 +17,7 @@ RUN apk add --no-cache \
|
|||||||
mysql-client \
|
mysql-client \
|
||||||
libreoffice \
|
libreoffice \
|
||||||
ttf-dejavu \
|
ttf-dejavu \
|
||||||
|
supervisor \
|
||||||
&& docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip \
|
&& docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip \
|
||||||
&& echo "upload_max_filesize=150M" > /usr/local/etc/php/conf.d/uploads.ini \
|
&& echo "upload_max_filesize=150M" > /usr/local/etc/php/conf.d/uploads.ini \
|
||||||
&& echo "post_max_size=150M" >> /usr/local/etc/php/conf.d/uploads.ini
|
&& echo "post_max_size=150M" >> /usr/local/etc/php/conf.d/uploads.ini
|
||||||
@ -32,6 +33,9 @@ COPY . .
|
|||||||
COPY entrypoint-dev.sh /usr/local/bin/entrypoint-dev.sh
|
COPY entrypoint-dev.sh /usr/local/bin/entrypoint-dev.sh
|
||||||
RUN chmod +x /usr/local/bin/entrypoint-dev.sh
|
RUN chmod +x /usr/local/bin/entrypoint-dev.sh
|
||||||
|
|
||||||
|
COPY Docker/supervisor/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||||
|
RUN mkdir -p /var/log/supervisor
|
||||||
|
|
||||||
RUN mkdir -p storage/app/keys storage/logs bootstrap/cache
|
RUN mkdir -p storage/app/keys storage/logs bootstrap/cache
|
||||||
|
|
||||||
RUN chown -R www-data:www-data /var/www/repuve-backend-v1/storage /var/www/repuve-backend-v1/bootstrap/cache
|
RUN chown -R www-data:www-data /var/www/repuve-backend-v1/storage /var/www/repuve-backend-v1/bootstrap/cache
|
||||||
@ -40,4 +44,4 @@ RUN chmod -R 775 /var/www/repuve-backend-v1/storage /var/www/repuve-backend-v1/b
|
|||||||
EXPOSE 9000
|
EXPOSE 9000
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/local/bin/entrypoint-dev.sh"]
|
ENTRYPOINT ["/usr/local/bin/entrypoint-dev.sh"]
|
||||||
CMD ["php-fpm"]
|
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
||||||
|
|||||||
@ -14,6 +14,7 @@ RUN apk add --no-cache \
|
|||||||
bash \
|
bash \
|
||||||
libreoffice \
|
libreoffice \
|
||||||
ttf-dejavu \
|
ttf-dejavu \
|
||||||
|
supervisor \
|
||||||
&& docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip \
|
&& docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip \
|
||||||
&& echo "upload_max_filesize=150M" > /usr/local/etc/php/conf.d/uploads.ini \
|
&& echo "upload_max_filesize=150M" > /usr/local/etc/php/conf.d/uploads.ini \
|
||||||
&& echo "post_max_size=150M" >> /usr/local/etc/php/conf.d/uploads.ini
|
&& echo "post_max_size=150M" >> /usr/local/etc/php/conf.d/uploads.ini
|
||||||
@ -29,6 +30,9 @@ COPY . .
|
|||||||
COPY entrypoint-prod.sh /usr/local/bin/entrypoint-prod.sh
|
COPY entrypoint-prod.sh /usr/local/bin/entrypoint-prod.sh
|
||||||
RUN chmod +x /usr/local/bin/entrypoint-prod.sh
|
RUN chmod +x /usr/local/bin/entrypoint-prod.sh
|
||||||
|
|
||||||
|
COPY Docker/supervisor/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||||
|
RUN mkdir -p /var/log/supervisor
|
||||||
|
|
||||||
RUN mkdir -p storage/app/keys storage/logs bootstrap/cache
|
RUN mkdir -p storage/app/keys storage/logs bootstrap/cache
|
||||||
|
|
||||||
RUN chown -R www-data:www-data /var/www/repuve-backend-v1/storage /var/www/repuve-backend-v1/bootstrap/cache
|
RUN chown -R www-data:www-data /var/www/repuve-backend-v1/storage /var/www/repuve-backend-v1/bootstrap/cache
|
||||||
@ -37,4 +41,4 @@ RUN chmod -R 775 /var/www/repuve-backend-v1/storage /var/www/repuve-backend-v1/b
|
|||||||
EXPOSE 9000
|
EXPOSE 9000
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/local/bin/entrypoint-prod.sh"]
|
ENTRYPOINT ["/usr/local/bin/entrypoint-prod.sh"]
|
||||||
CMD ["php-fpm"]
|
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
||||||
|
|||||||
30
Docker/supervisor/supervisord.conf
Normal file
30
Docker/supervisor/supervisord.conf
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
[supervisord]
|
||||||
|
nodaemon=true
|
||||||
|
user=root
|
||||||
|
logfile=/dev/null
|
||||||
|
logfile_maxbytes=0
|
||||||
|
pidfile=/var/run/supervisord.pid
|
||||||
|
|
||||||
|
[program:php-fpm]
|
||||||
|
command=php-fpm
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
priority=1
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
|
||||||
|
[program:queue-worker]
|
||||||
|
command=php /var/www/repuve-backend-v1/artisan queue:work --tries=3 --timeout=300 --sleep=3 --max-time=3600
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
priority=2
|
||||||
|
user=www-data
|
||||||
|
numprocs=1
|
||||||
|
stopwaitsecs=60
|
||||||
|
stdout_logfile=/var/www/repuve-backend-v1/storage/logs/worker.log
|
||||||
|
stdout_logfile_maxbytes=50MB
|
||||||
|
stdout_logfile_backups=5
|
||||||
|
stderr_logfile=/var/www/repuve-backend-v1/storage/logs/worker.log
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
@ -19,6 +19,7 @@
|
|||||||
use App\Services\RepuveService;
|
use App\Services\RepuveService;
|
||||||
use App\Services\PadronEstatalService;
|
use App\Services\PadronEstatalService;
|
||||||
use App\Jobs\ProcessRepuveResponse;
|
use App\Jobs\ProcessRepuveResponse;
|
||||||
|
use App\Supports\SoapParallelExecutor;
|
||||||
use Illuminate\Routing\Controllers\HasMiddleware;
|
use Illuminate\Routing\Controllers\HasMiddleware;
|
||||||
|
|
||||||
class InscriptionController extends Controller implements HasMiddleware
|
class InscriptionController extends Controller implements HasMiddleware
|
||||||
@ -119,8 +120,17 @@ public function vehicleInscription(VehicleStoreRequest $request)
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consultar REPUVE Nacional para corroborar el vehículo y obtener folio_CI
|
// Consultar REPUVE Nacional y verificar robo en paralelo
|
||||||
$repuveNacionalData = $this->repuveService->consultarVehiculo($niv, $placa);
|
$parallelRequests = [
|
||||||
|
'repuve' => $this->repuveService->prepareConsultarVehiculoRequest($niv, $placa),
|
||||||
|
'robo' => $this->repuveService->prepareVerificarRoboRequest($niv, $placa),
|
||||||
|
];
|
||||||
|
$parallelResults = SoapParallelExecutor::execute($parallelRequests);
|
||||||
|
|
||||||
|
// Parsear respuesta REPUVE Nacional
|
||||||
|
$repuveNacionalData = $this->repuveService->parseConsultarVehiculoResponse(
|
||||||
|
$parallelResults['repuve']['response'] ?: ''
|
||||||
|
);
|
||||||
|
|
||||||
// Verificar si hubo error en la consulta a REPUVE Nacional
|
// Verificar si hubo error en la consulta a REPUVE Nacional
|
||||||
if ($repuveNacionalData['has_error'] ?? false) {
|
if ($repuveNacionalData['has_error'] ?? false) {
|
||||||
@ -136,8 +146,11 @@ public function vehicleInscription(VehicleStoreRequest $request)
|
|||||||
$folioRepuve = $repuveNacionalData['folio_CI'] ?? null;
|
$folioRepuve = $repuveNacionalData['folio_CI'] ?? null;
|
||||||
$actionType = empty($folioRepuve) ? 'sustitucion_primera_vez' : 'sustitucion';
|
$actionType = empty($folioRepuve) ? 'sustitucion_primera_vez' : 'sustitucion';
|
||||||
|
|
||||||
// Verificar robo
|
// Parsear respuesta de robo
|
||||||
$roboResult = $this->checkIfStolen($niv, $placa);
|
$roboResult = $this->repuveService->parseRoboResponse(
|
||||||
|
$parallelResults['robo']['response'] ?: '',
|
||||||
|
$niv ?: $placa ?: 'N/A'
|
||||||
|
);
|
||||||
// Solo bloquear si está marcado como robado
|
// Solo bloquear si está marcado como robado
|
||||||
if ($roboResult['is_robado'] ?? false) {
|
if ($roboResult['is_robado'] ?? false) {
|
||||||
DB::rollBack();
|
DB::rollBack();
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use App\Jobs\ProcessRepuveResponse;
|
use App\Jobs\ProcessRepuveResponse;
|
||||||
|
use App\Supports\SoapParallelExecutor;
|
||||||
use App\Models\CatalogTagStatus;
|
use App\Models\CatalogTagStatus;
|
||||||
use Illuminate\Routing\Controllers\HasMiddleware;
|
use Illuminate\Routing\Controllers\HasMiddleware;
|
||||||
|
|
||||||
@ -141,10 +142,20 @@ public function tagSubstitution(Request $request)
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validar vehículo en PadronEstatal antes de proceder con la sustitución
|
// Consultar PadronEstatal, verificar robo y consultar REPUVE en paralelo
|
||||||
|
$parallelRequests = [
|
||||||
|
'padron' => $this->padronEstatalService->prepareConsultarPadronRequest('placa', $vehicle->placa),
|
||||||
|
'robo' => $this->repuveService->prepareVerificarRoboRequest($vehicle->niv, null),
|
||||||
|
'repuve' => $this->repuveService->prepareConsultarVehiculoRequest($vehicle->niv, $vehicle->placa),
|
||||||
|
];
|
||||||
|
$parallelResults = SoapParallelExecutor::execute($parallelRequests);
|
||||||
|
|
||||||
|
// Parsear respuesta del Padrón Estatal
|
||||||
try {
|
try {
|
||||||
$datosEstatal = $this->padronEstatalService->getVehiculoByPlaca($vehicle->placa);
|
$datosEstatalRaw = $this->padronEstatalService->parsearRespuesta(
|
||||||
$vehicleDataEstatal = $this->padronEstatalService->extraerDatosVehiculo($datosEstatal);
|
$parallelResults['padron']['response'] ?: ''
|
||||||
|
);
|
||||||
|
$vehicleDataEstatal = $this->padronEstatalService->extraerDatosVehiculo($datosEstatalRaw);
|
||||||
} catch (PadronEstatalException $e) {
|
} catch (PadronEstatalException $e) {
|
||||||
return ApiResponse::BAD_REQUEST->response([
|
return ApiResponse::BAD_REQUEST->response([
|
||||||
'message' => 'No se pudo validar el vehículo en el Padrón Estatal.',
|
'message' => 'No se pudo validar el vehículo en el Padrón Estatal.',
|
||||||
@ -161,7 +172,11 @@ public function tagSubstitution(Request $request)
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$roboResult = $this->checkIfStolen($vehicle->niv);
|
// Parsear respuesta de robo
|
||||||
|
$roboResult = $this->repuveService->parseRoboResponse(
|
||||||
|
$parallelResults['robo']['response'] ?: '',
|
||||||
|
$vehicle->niv
|
||||||
|
);
|
||||||
|
|
||||||
// Solo bloquear si explícitamente está marcado como robado
|
// Solo bloquear si explícitamente está marcado como robado
|
||||||
if ($roboResult['is_robado'] ?? false) {
|
if ($roboResult['is_robado'] ?? false) {
|
||||||
@ -170,8 +185,10 @@ public function tagSubstitution(Request $request)
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtener folio_CI de REPUVE antes de cancelar (folio anterior)
|
// Parsear respuesta REPUVE para obtener folio_CI anterior
|
||||||
$repuveData = $this->repuveService->consultarVehiculo($vehicle->niv, $vehicle->placa);
|
$repuveData = $this->repuveService->parseConsultarVehiculoResponse(
|
||||||
|
$parallelResults['repuve']['response'] ?: ''
|
||||||
|
);
|
||||||
$folioAnterior = $repuveData['folio_CI'] ?? null;
|
$folioAnterior = $repuveData['folio_CI'] ?? null;
|
||||||
|
|
||||||
DB::beginTransaction();
|
DB::beginTransaction();
|
||||||
|
|||||||
@ -31,7 +31,35 @@ public function getVehiculoByFolio(string $folio): array
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Consulta el padrón vehicular estatal
|
* Consulta el padrón vehicular estatal
|
||||||
|
* Prepara la petición SOAP sin ejecutarla.
|
||||||
|
* Retorna ['url', 'body', 'headers'] para usar con SoapParallelExecutor.
|
||||||
*/
|
*/
|
||||||
|
public function prepareConsultarPadronRequest(string $tipo, string $valor): array
|
||||||
|
{
|
||||||
|
$data = json_encode(['tipo' => $tipo, 'valor' => $valor]);
|
||||||
|
|
||||||
|
$soapBody = <<<XML
|
||||||
|
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdl="http://mx/tgc/ConsultaPadronVehicular.wsdl">
|
||||||
|
<soapenv:Header/>
|
||||||
|
<soapenv:Body>
|
||||||
|
<wsdl:getVehiculosRepuve>
|
||||||
|
<Data>{$data}</Data>
|
||||||
|
</wsdl:getVehiculosRepuve>
|
||||||
|
</soapenv:Body>
|
||||||
|
</soapenv:Envelope>
|
||||||
|
XML;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'url' => $this->soapUrl,
|
||||||
|
'body' => $soapBody,
|
||||||
|
'headers' => [
|
||||||
|
'Content-Type: text/xml; charset=utf-8',
|
||||||
|
'SOAPAction: ""',
|
||||||
|
'Content-Length: ' . strlen($soapBody),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
private function consultarPadron(string $tipo, string $valor): array
|
private function consultarPadron(string $tipo, string $valor): array
|
||||||
{
|
{
|
||||||
$logger = Log::channel('padron_estatal');
|
$logger = Log::channel('padron_estatal');
|
||||||
@ -114,7 +142,7 @@ private function consultarPadron(string $tipo, string $valor): array
|
|||||||
/**
|
/**
|
||||||
* Parsea la respuesta del padrón estatal
|
* Parsea la respuesta del padrón estatal
|
||||||
*/
|
*/
|
||||||
private function parsearRespuesta(string $soapResponse): array
|
public function parsearRespuesta(string $soapResponse): array
|
||||||
{
|
{
|
||||||
$logger = Log::channel('padron_estatal');
|
$logger = Log::channel('padron_estatal');
|
||||||
|
|
||||||
|
|||||||
@ -470,6 +470,84 @@ public function consultarVehiculo(?string $niv = null, ?string $placa = null)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepara la petición SOAP de consultarVehiculo sin ejecutarla.
|
||||||
|
* Retorna ['url', 'body', 'headers'] para usar con SoapParallelExecutor.
|
||||||
|
*/
|
||||||
|
public function prepareConsultarVehiculoRequest(?string $niv = null, ?string $placa = null): array
|
||||||
|
{
|
||||||
|
$this->asegurarCargaCredenciales();
|
||||||
|
|
||||||
|
$url = $this->baseUrl . '/jaxws-consultarpv/ConsultaRpv';
|
||||||
|
|
||||||
|
$campos = array_fill(0, 8, '');
|
||||||
|
$campos[0] = $niv ?? '';
|
||||||
|
$campos[2] = $placa ?? '';
|
||||||
|
$arg2 = implode('|', $campos);
|
||||||
|
|
||||||
|
$soapBody = <<<XML
|
||||||
|
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdl="http://consultaRpv.org/wsdl">
|
||||||
|
<soapenv:Header/>
|
||||||
|
<soapenv:Body>
|
||||||
|
<wsdl:doConsRPV>
|
||||||
|
<arg0>{$this->username}</arg0>
|
||||||
|
<arg1>{$this->password}</arg1>
|
||||||
|
<arg2>{$arg2}</arg2>
|
||||||
|
</wsdl:doConsRPV>
|
||||||
|
</soapenv:Body>
|
||||||
|
</soapenv:Envelope>
|
||||||
|
XML;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'url' => $url,
|
||||||
|
'body' => $soapBody,
|
||||||
|
'headers' => [
|
||||||
|
'Content-Type: text/xml; charset=utf-8',
|
||||||
|
'SOAPAction: "doConsRPV"',
|
||||||
|
'Content-Length: ' . strlen($soapBody),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepara la petición SOAP de verificarRobo sin ejecutarla.
|
||||||
|
* Retorna ['url', 'body', 'headers'] para usar con SoapParallelExecutor.
|
||||||
|
*/
|
||||||
|
public function prepareVerificarRoboRequest(?string $niv = null, ?string $placa = null): array
|
||||||
|
{
|
||||||
|
$this->asegurarCargaCredenciales();
|
||||||
|
|
||||||
|
$url = $this->baseUrl . $this->roboEndpoint;
|
||||||
|
|
||||||
|
$campos = array_fill(0, 8, '');
|
||||||
|
$campos[0] = $niv ?? '';
|
||||||
|
$campos[2] = $placa ?? '';
|
||||||
|
$arg2 = implode('|', $campos);
|
||||||
|
|
||||||
|
$soapBody = <<<XML
|
||||||
|
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdl="http://consultaRpv.org/wsdl">
|
||||||
|
<soapenv:Header/>
|
||||||
|
<soapenv:Body>
|
||||||
|
<wsdl:doConsRepRobo>
|
||||||
|
<arg0>{$this->username}</arg0>
|
||||||
|
<arg1>{$this->password}</arg1>
|
||||||
|
<arg2>{$arg2}</arg2>
|
||||||
|
</wsdl:doConsRepRobo>
|
||||||
|
</soapenv:Body>
|
||||||
|
</soapenv:Envelope>
|
||||||
|
XML;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'url' => $url,
|
||||||
|
'body' => $soapBody,
|
||||||
|
'headers' => [
|
||||||
|
'Content-Type: text/xml; charset=utf-8',
|
||||||
|
'SOAPAction: "doConsRepRobo"',
|
||||||
|
'Content-Length: ' . strlen($soapBody),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
public function inscribirVehiculo(array $datos)
|
public function inscribirVehiculo(array $datos)
|
||||||
{
|
{
|
||||||
$this->asegurarCargaCredenciales();
|
$this->asegurarCargaCredenciales();
|
||||||
@ -674,7 +752,7 @@ private function parsearRespuestaInscripcion(string $soapResponse)
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function parseRoboResponse(string $soapResponse, string $valor): array
|
public function parseRoboResponse(string $soapResponse, string $valor): array
|
||||||
{
|
{
|
||||||
// Extraer contenido del tag <return>
|
// Extraer contenido del tag <return>
|
||||||
preg_match('/<return>(.*?)<\/return>/s', $soapResponse, $matches);
|
preg_match('/<return>(.*?)<\/return>/s', $soapResponse, $matches);
|
||||||
|
|||||||
53
app/Supports/SoapParallelExecutor.php
Normal file
53
app/Supports/SoapParallelExecutor.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Supports;
|
||||||
|
|
||||||
|
class SoapParallelExecutor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Ejecuta múltiples peticiones SOAP en paralelo usando curl_multi_exec.
|
||||||
|
*
|
||||||
|
* @param array $requests Mapa de clave => ['url' => string, 'body' => string, 'headers' => array]
|
||||||
|
* @return array Mapa de clave => ['response' => string|false, 'http_code' => int, 'curl_error' => string]
|
||||||
|
*/
|
||||||
|
public static function execute(array $requests): array
|
||||||
|
{
|
||||||
|
$multiHandle = curl_multi_init();
|
||||||
|
$handles = [];
|
||||||
|
|
||||||
|
foreach ($requests as $key => $req) {
|
||||||
|
$ch = curl_init($req['url']);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $req['body']);
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||||
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $req['headers']);
|
||||||
|
curl_multi_add_handle($multiHandle, $ch);
|
||||||
|
$handles[$key] = $ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
$running = null;
|
||||||
|
do {
|
||||||
|
$status = curl_multi_exec($multiHandle, $running);
|
||||||
|
if ($running) {
|
||||||
|
curl_multi_select($multiHandle);
|
||||||
|
}
|
||||||
|
} while ($running > 0 && $status === CURLM_OK);
|
||||||
|
|
||||||
|
$results = [];
|
||||||
|
foreach ($handles as $key => $ch) {
|
||||||
|
$results[$key] = [
|
||||||
|
'response' => curl_multi_getcontent($ch),
|
||||||
|
'http_code' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
|
||||||
|
'curl_error' => curl_error($ch),
|
||||||
|
];
|
||||||
|
curl_multi_remove_handle($multiHandle, $ch);
|
||||||
|
curl_close($ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_multi_close($multiHandle);
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user