PHP
declarar(tipos_estrictos=1);
require_once __DIR__ . '/config.php';
$dbHost = 'localhost';
$dbName = 'u385429974_libros';
$dbUser = 'u385429974_libros';
$dbPass = 'Iliass1983&&&&&&';
$dbCharset = 'utf8mb4';
$opciones = [
PDO::ATTR_ERRMODE = PDO::ERRMODE_EXCEPCIÓN,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => falso,
];
función create_pdo(cadena $host, ?cadena $base de datos, cadena $usuario, cadena $contraseña, cadena $conjunto de caracteres, matriz $opciones): PDO
{
$dsn = "mysql:host={$host};charset={$charset}";
si ($base de datos! == nulo) {
$dsn = "mysql:host={$host};dbname={$database};charset={$charset}";
}
devolver nuevo PDO($dsn, $usuario, $contraseña, $opciones);
}
función install_database(PDO $pdo): nulo
{
$rutaesquema = __DIR__ . '/../base de datos/base de datos.sql';
si (!is_file($schemaPath)) {
throw new RuntimeException('Falta base de datos/database.sql.');
}
$hasAdminsTable = $pdo->query("MOSTRAR TABLAS COMO 'admins'")->fetchColumn();
si ($hasAdminsTable) {
regresar;
}
$esquema = (cadena) file_get_contents($schemaPath);
$esquema = preg_replace('/^\s*CREATE\s+DATABASE\b.*?;\s*$/mi', '', $esquema)?? $esquema;
$esquema = preg_replace('/^\s*USE\s+`?[\w-]+`?\s*;\s*$/mi', '', $esquema)?? $esquema;
$esquema = str_replace('CREAR TABLA ', 'CREAR TABLA SI NO EXISTE ', $esquema);
$esquema = str_replace('INSERT INTO ', 'INSERT IGNORE INTO ', $schema);
foreach (array_filter(array_map('recortar', explotar(';', $esquema))) como $declaración) {
$pdo->exec($declaración);
}
}
prueba {
prueba {
$pdo = create_pdo($dbHost, $dbName, $dbUser, $dbPass, $dbCharset, $opciones);
} captura (PDOException $e) {
$serverPdo = create_pdo($dbHost, null, $dbUser, $dbPass, $dbCharset, $opciones);
$serverPdo->exec("CREAR BASE DE DATOS SI NO EXISTE `{$dbName}` CONJUNTO DE CARACTERES utf8mb4 COLLATE utf8mb4_unicode_ci");
$pdo = create_pdo($dbHost, $dbName, $dbUser, $dbPass, $dbCharset, $opciones);
}
install_database($pdo);
} captura (PDOException $e) {
http_response_code(500);
exit('Error de conexión a la base de datos: ' . $e->getMessage());
} captura (tirable $e) {
http_response_code(500);
exit('Error de instalación de base de datos: ' . $e->getMessage());
}
PHP
declarar(tipos_estrictos=1);
función e(?cadena $valor): cadena
{
return htmlspecialchars((cadena) $valor, ENT_QUOTES, 'UTF-8');
}
texto de función (? cadena $ valor): cadena
{
$decodificado = html_entity_decode((cadena) $valor, ENT_QUOTES | ENT_HTML5, 'UTF-8');
devolver htmlspecialchars($decodificado, ENT_QUOTES, 'UTF-8');
}
función is_valid_real_email(cadena $correo electrónico): bool
{
if (!filter_var($correo electrónico, FILTER_VALIDATE_EMAIL)) {
devolver falso;
}
// Verifica si el dominio tiene registros MX para asegurar que pueda recibir correos electrónicos
$dominio = substr(strrchr($correo electrónico, "@"), 1);
si (!$dominio) {
devolver falso;
}
si (function_exists('checkdnsrr')) {
devolver checkdnsrr($dominio, 'MX');
}
devolver verdadero;
}
función render_markdown_inline(cadena $texto): cadena
{
$texto = e($texto);
$texto = preg_replace_callback(
'/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/i',
fn estática(matriz $match): cadena = ''. $coincidencia[1] . '',
$texto
) ?? $texto;
$texto = preg_replace('/\*\*([^*]+)\*\*/', '$1', $texto)?? $texto;
$texto = preg_replace('/`([^`]+)`/', '$1', $texto)?? $texto;
$texto = preg_replace('/(?\*)\*([^*]+)\*(?!\*)/', '<em$1', $texto)?? $texto;
devolver $texto;
}
función render_markdown(cadena $markdown): cadena
{
$líneas = preg_split('/\R/', trim($markdown))?: [];
$html = [];
$párrafo = [];
$tipo de lista = nulo;
$flushParagraph = función estática () uso (&$párrafo, &$html): void {
si ($párrafo) {
$html[] = '
'. render_markdown_inline(implode(' ', $párrafo)) . '
'; $párrafo = []; } }; $closeList = función estática () uso (&$listType, &$html): void { si ($tipo de lista! == nulo) { $html[] = ''. $tipo de lista. ''; $tipo de lista = nulo; } }; foreach ($líneas como $línea) { $línea = recortar($línea); si ($línea === '') { $flushParagraph(); $cerrarLista(); continuar; } if (preg_match('/^(#{1,4})\s+(.+)$/', $línea, $match)) { $flushParagraph(); $cerrarLista(); $nivel = strlen($partido[1]); $html[] = "'; continuar; } if (preg_match('/^---+$/', $línea)) { $flushParagraph(); $cerrarLista(); $html[] = ''. render_markdown_inline($match[1]) . '
(.*)<\/p>$/si', $contenido, $coincidencia) && preg_match('/(?:^|\s)#{1,4}\s+\S|\*\*[^*]+\*\*|\[[^\]]+\]\(https?:\/\//i', $match[1]) ) { $contenido = html_entity_decode($match[1], ENT_QUOTES | ENT_HTML5, 'UTF-8'); $contenido = expand_collapsed_markdown_headings($contenido, $título); $contenido = preg_replace('/\s+(\d+\.\s+)/', "\n$1", $contenido)?? $contenido; $contenido = preg_replace('/\s+([-*]\s+)/', "\n$1", $contenido)?? $contenido; devolver render_markdown($contenido); } return preg_match('/<\s*\/?\s*[a-z][^>]*>/i', $contenido) ? $contenido : render_markdown($contenido); } función blog_featured_image(?cadena $imagen, cadena $slug, int $ancho = 1200): cadena { $imagen = recortar((cadena) $imagen); si ($imagen !== '' && !str_starts_with($imagen, 'datos:imagen/')) { devolver $imagen; } $ alternativas = [ 'https://images.unsplash.com/photo-1499750310107-5fef28a66643', 'https://images.unsplash.com/photo-1455390582262-044cdead277a', 'https://images.unsplash.com/photo-1516321318423-f06f85e504b3', 'https://images.unsplash.com/photo-1558655146-364adaf1fcc9', 'https://images.unsplash.com/photo-1542744095-fcf48d80b0fd', 'https://images.unsplash.com/photo-1484480974693-6ca0a78fb36b', 'https://images.unsplash.com/photo-1518770660439-4636190af475', 'https://images.unsplash.com/photo-1517245386807-bb43f82c33c4', 'https://images.unsplash.com/photo-1522202176988-66273c2fd55f', 'https://images.unsplash.com/photo-1552664730-d307ca884978', 'https://images.unsplash.com/photo-1556761175-b413da4baf72', 'https://images.unsplash.com/photo-1460925895917-afdab827c52f', 'https://images.unsplash.com/photo-1531482615713-2afd69097998', 'https://images.unsplash.com/photo-1504384308090-c894fdcc538d', 'https://images.unsplash.com/photo-1498050108023-c5249f4df085', 'https://images.unsplash.com/photo-1521737711867-e3b97375f902', ]; $index = (int) (hexdec(substr(hash('sha256', $slug), 0, 8)) % count($fallbacks)); devolver $fallbacks[$index] . '?w='. $ancho. '&q=80'; } función dinero(flotante $cantidad): cadena { devolver '$'. number_format($cantidad, 2); } función slugify(cadena $texto): cadena { $texto = strtolower(trim($texto)); $texto = preg_replace('/[^a-z0-9]+/i', '-', $texto)?: ''; return trim($texto, '-')?: bin2hex(random_bytes(4)); } redirección de función (cadena $ ruta): nunca { encabezado('Ubicación: ' . $ruta); salida; } configuración de función (PDO $pdo, cadena $clave, cadena $fallback = ''): cadena { $stmt = $pdo->prepare('SELECCIONE valor_configuración DESDE la configuración DONDE clave_configuración =? LÍMITE 1'); $stmt->execute([$clave]); return (cadena) ($stmt->fetchColumn()?: $fallback); } función get_setting(cadena $clave, cadena $predeterminado = ''): cadena { global $pdo; estático $caché = []; si (!array_key_exists($clave, $cache)) { $r = $pdo->prepare('SELECCIONAR valor_configuración DESDE la configuración DONDE clave_configuración=? LÍMITE 1'); $r->ejecutar([$clave]); $val = $r->fetchColumn(); $caché[$clave] = ($val!== false && $val!== nulo && $val!== '')? (cadena)$val: $predeterminado; } devolver $caché[$clave]; } función active_categories(PDO $pdo): matriz { return $pdo->query('SELECT * FROM categorías DONDE estado = "activo" ORDENAR POR nombre')->fetchAll(); } función cart_count(): int { return array_sum(array_map('intval', $_SESSION['cart'] ?? [])); } función cart_items(PDO $pdo): matriz { $carrito = $_SESSION['carrito'] ?? []; si (!$carro) { devolver []; } $ids = array_keys($carro); $marcadores de posición = implode(',', array_fill(0, count($ids), '?')); $stmt = $pdo->prepare("SELECCIONAR * DE productos DONDE id EN ({$placeholders}) Y estado EN ('activo', 'plan')"); $stmt->ejecutar($ids); $elementos = []; foreach ($stmt->fetchAll() como $producto) { $cantidad = max(1, (int) ($carrito[$producto['id']] ?? 1)); $precio = (flotante) ($producto['precio_venta'] ?: $producto['precio']); $producto['cantidad'] = $cantidad; $producto['line_total'] = $precio * $cantidad; $artículos[] = $producto; } devolver $artículos; } función cart_total(PDO $pdo): flotante { return array_sum(array_column(cart_items($pdo), 'line_total')); } función generar_download_token(PDO $pdo, int $orderId, int $productId): cadena { $token = bin2hex(random_bytes(32)); $expires = (new DateTime('+7 días'))->format('Y-m-d H:i:s'); $stmt = $pdo->prepare('INSERT INTO download_tokens (order_id, product_id, token, expires_at) VALORES (?, ?, ?, ?)'); $stmt->execute([$orderId, $productId, $token, $expires]); devolver $token; } función validar_upload(matriz $archivo, matriz $allowedMimeTypes, int $maxBytes): nulo { $error = (int) ($archivo['error'] ?? UPLOAD_ERR_NO_FILE); si ($error === UPLOAD_ERR_NO_FILE) { regresar; } si ($error!== UPLOAD_ERR_OK) { $mensajes = [ UPLOAD_ERR_INI_SIZE => 'El archivo cargado excede el límite de carga del servidor.', UPLOAD_ERR_FORM_SIZE => 'El archivo cargado excede el límite de carga del formulario.', UPLOAD_ERR_PARTIAL => 'La carga del archivo fue interrumpida. Por favor inténtalo de nuevo.', UPLOAD_ERR_NO_TMP_DIR => 'Falta el directorio de carga temporal del servidor.', UPLOAD_ERR_CANT_WRITE => 'El servidor no pudo escribir el archivo cargado.', UPLOAD_ERR_EXTENSION => 'Una extensión del servidor detuvo la carga del archivo.', ]; throw new RuntimeException($messages[$error] ?? 'Error en la carga del archivo'); } if ((int) ($archivo['tamaño'] ?? 0) > $maxBytes) { throw new RuntimeException('El archivo cargado excede el ' . round($maxBytes / 1024 / 1024) . ' Límite de MB.'); } $finfo = nuevo finfo(FILEINFO_MIME_TYPE); $mime = $finfo->file($file['tmp_name']); if (!in_array($mime, $allowedMimeTypes, verdadero)) { throw new RuntimeException('El tipo de archivo no está permitido.'); } } función cloudinary_config():?matriz { estático $config = falso; si ($config === falso) { $cargado = is_file(CLOUDINARY_CONFIG_FILE)? requiere CLOUDINARY_CONFIG_FILE: nulo; $config = is_array($cargado)? $cargado: nulo; } si (!is_array($config)) { devolver nulo; } foreach (['nombre_nube', 'api_key', 'api_secret'] como $clave) { if (empty($config[$key]) || str_starts_with((string) $config[$key], 'ADD_')) { devolver nulo; } } devolver $config; } función upload_image_to_cloudinary(matriz $archivo, cadena $directorio): ?cadena { $config = cloudinary_config(); si ($config === nulo) { devolver nulo; } $carpeta = str_contains(str_replace('\\', '/', $directorio), '/blog') ? 'ivomercado/blog' : 'ivomarket/productos'; $punto final = 'https://api.cloudinary.com/v1_1/'. rawurlencode((string) $config['cloud_name']) . '/imagen/cargar'; $postFields = [ 'archivo' => curl_file_create((cadena) $archivo['tmp_name'], (cadena) ($archivo['tipo'] ?? ''), (cadena) ($archivo['nombre'] ?? 'imagen')), 'carpeta' => $carpeta, 'unique_filename' => 'verdadero', 'sobrescribir' => 'falso', ]; $curl = curl_init($punto final); curl_setopt_array($curl, [ CURLOPT_POST => verdadero, CURLOPT_POSTFIELDS => $postFields, CURLOPT_RETURNTRANSFER => verdadero, CURLOPT_TIMEOUT => 60, CURLOPT_USERPWD => $config['api_key'] . ':'. $config['api_secret'], ]); $respuesta = curl_exec($curl); $estado = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE); $curlError = curl_error($curl); curl_close($curl); $datos = is_string($respuesta)? json_decode($respuesta, verdadero): nulo; if ($estado >= 200 && $estado < 300 && is_array($datos) && !empty($datos['secure_url'])) { return (cadena) $datos['secure_url']; } $mensaje = is_array($datos)? (cadena) ($datos['error']['mensaje'] ?? ''): ''; throw new RuntimeException('Error en la carga de Cloudinary: ' . ($mensaje?: $curlError ?: 'respuesta inesperada.')); } función upload_data_image_to_cloudinary(cadena $dataUrl, cadena $carpeta = 'ivomarket/blog'): cadena { $config = cloudinary_config(); si ($config === nulo) { throw new RuntimeException('Cloudinary no está configurado para imágenes base64'); } if (!preg_match('#^data:image/(?:jpeg|png|gif|webp);base64,#i', $dataUrl)) { throw new RuntimeException('La imagen destacada debe ser una URL de imagen válida o una imagen base64.'); } $punto final = 'https://api.cloudinary.com/v1_1/'. rawurlencode((string) $config['cloud_name']) . '/imagen/cargar'; $curl = curl_init($punto final); curl_setopt_array($curl, [ CURLOPT_POST => verdadero, CURLOPT_POSTFIELDS => [ 'archivo' => $dataUrl, 'carpeta' => $carpeta, 'unique_filename' => 'verdadero', 'sobrescribir' => 'falso', ], CURLOPT_RETURNTRANSFER => verdadero, CURLOPT_TIMEOUT => 90, CURLOPT_USERPWD => $config['api_key'] . ':'. $config['api_secret'], ]); $respuesta = curl_exec($curl); $estado = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE); $curlError = curl_error($curl); curl_close($curl); $datos = is_string($respuesta)? json_decode($respuesta, verdadero): nulo; if ($estado >= 200 && $estado < 300 && is_array($datos) && !empty($datos['secure_url'])) { return (cadena) $datos['secure_url']; } $mensaje = is_array($datos)? (cadena) ($datos['error']['mensaje'] ?? ''): ''; throw new RuntimeException('Error en la carga de Cloudinary base64: ' . ($mensaje?: $curlError ?: 'respuesta inesperada.')); } función save_upload(matriz $archivo, cadena $directorio, matriz $allowedMimeTypes, int $maxBytes): ?cadena { if (($archivo['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) { volver nulo; } validar_upload($archivo, $allowedMimeTypes, $maxBytes); si (!is_dir($directorio)) { mkdir($directorio, 0755, verdadero); } $mime = (new finfo(FILEINFO_MIME_TYPE))->file($file['tmp_name']); $isImage = in_array($mime, ['image/jpeg','image/png','image/gif','image/webp'], verdadero); $extensión = strtolower(pathinfo((cadena) $archivo['nombre'], PATHINFO_EXTENSION)); si ($isImage && cloudinary_config() !== nulo) { devolver upload_image_to_cloudinary($archivo, $directorio); } // Convertir imágenes a WebP (archivos más pequeños, carga más rápida) si ($isImage && function_exists('imagewebp')) { $src = coincidencia($mime) { 'image/jpeg' => imagecreatefromjpeg($archivo['tmp_name']), 'image/png' => imagecreatefrompng($archivo['tmp_name']), 'imagen/gif' => imagecreatefromgif($archivo['tmp_name']), 'image/webp' => imagecreatefromwebp($archivo['tmp_name']), predeterminado => falso, }; si ($src! == falso) { // Cambiar el tamaño si es más ancho que 1400 px $w = imágenesx($src); $h = imágenesy($src); si ($w > 1400) { $nuevoH = (int) ronda($h * 1400 / $w); $lienzo = imagecreatetruecolor(1400, $newH); imagealphablending($lienzo, falso); imágenesavealpha($lienzo, verdadero); imagefill($canvas, 0, 0, imagecolorallocatealpha($canvas, 0, 0, 0, 127)); imagecopyresampled($canvas, $src, 0, 0, 0, 0, 1400, $newH, $w, $h); destrucción de imágenes($src); $src = $lienzo; } $nombre de archivo = bin2hex(random_bytes(16)) . '.webp'; $objetivo = rtrim($directorio, DIRECTORY_SEPARATOR). DIRECTORIO_SEPARADOR . $nombre de archivo; if (imagewebp($src, $objetivo, 82)) { destrucción de imágenes($src); devolver $nombre de archivo; } destrucción de imágenes($src); } } // Alternativa: guardar el original (los archivos que no son de imagen o GD no están disponibles) $nombre de archivo = bin2hex(random_bytes(16)) . ($extensión ? ".{$extensión}" : ''); $objetivo = rtrim($directorio, DIRECTORY_SEPARATOR). DIRECTORIO_SEPARADOR . $nombre de archivo; if (!move_uploaded_file($archivo['tmp_name'], $objetivo)) { throw new RuntimeException('No se pudo almacenar el archivo cargado'); } devolver $nombre de archivo; } función social_http_request(cadena $método, cadena $url, matriz $datos = [], matriz $encabezados = []): matriz { $curl = curl_init($url); $headers[] = 'Aceptar: aplicación/json'; curl_setopt_array($curl, [ CURLOPT_RETURNTRANSFER => verdadero, CURLOPT_TIMEOUT => 30, CURLOPT_CUSTOMREQUEST => $método, CURLOPT_HTTPHEADER => $encabezados, ]); si ($datos) { $isJson = in_array('Tipo de contenido: aplicación/json', $headers, verdadero); curl_setopt($curl, CURLOPT_POSTFIELDS, $isJson? json_encode($datos): http_build_query($datos)); } $cuerpo = curl_exec($curl); $estado = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE); $error = curl_error($curl); curl_close($curl); $json = is_string($cuerpo)? json_decode($cuerpo, verdadero): nulo; return ['ok' => $estado >= 200 && $estado < 300, 'estado' => $estado, 'datos' => is_array($json)? $json : [], 'error' => $error ?: (cadena) $cuerpo]; } función social_log(PDO $pdo, int $postId, cadena $plataforma, cadena $estado, cadena $mensaje): void { $pdo->exec("CREAR TABLA SI NO EXISTE social_publish_logs ( id INT CLAVE PRIMARIA AUTO_INCREMENT SIN FIRMAR, post_id INT NO NULO, plataforma VARCHAR(30) NO NULA, estado ENUM('éxito','omitido','error') NO NULO, mensaje de texto, creado_en TIMESTAMP DEFAULT CURRENT_TIMESTAMP, ÍNDICE idx_social_post (post_id, plataforma) ) MOTOR=InnoDB CONJUNTO DE CARACTERES PREDETERMINADO=utf8mb4"); $pdo->prepare('INSERTAR EN social_publish_logs (post_id,plataforma,estado,mensaje) VALORES (?,?,?,?)') ->execute([$postId, $plataforma, $status, mb_substr($message, 0, 1500)]); } función social_image_url(cadena $imagen, cadena $siteUrl): cadena { si ($imagen === '') { regresar ''; } si (str_starts_with($imagen, 'http')) { devolver $imagen; } devolver rtrim($siteUrl, '/') . '/subidas/imágenes/blog/' . ltrim($imagen, '/'); } función publicar_blog_post_social(PDO $pdo, matriz $post): matriz { if (($publicación['estado'] ?? '') !== 'publicado') { devolver []; } $postId = (int) ($post['id'] ?? 0); $siteUrl = rtrim(get_setting('app_url', APP_URL), '/'); $postUrl = $siteUrl . '/publicación de blog?slug=' . urlencode((cadena) $post['slug']); $imageUrl = social_image_url(trim((string) ($post['featured_image'] ?? '')), $siteUrl); $título = trim((cadena) ($publicación['título'] ?? '')); $extracto = trim(strip_tags((cadena) ($publicación['extracto'] ?? ''))); $caption = trim($title . "\n\n" . $extracto . "\n\n" . $postUrl); $graphVersion = preg_replace('/[^0-9.]/', '', get_setting('meta_graph_version', '25.0'))?: '25.0'; $resultados = []; if (get_setting('social_auto_facebook', '0') === '1') { $pageId = trim(get_setting('meta_page_id')); $token = trim(get_setting('meta_page_access_token')); si ($pageId === '' || $token === '') { $resultados['facebook'] = ['status' => 'skipped', 'message' => 'Falta ID de página o token de acceso a página.']; } más { $respuesta = social_http_request('POST', "https://graph.facebook.com/v{$graphVersion}/{$pageId}/feed", [ 'mensaje' => $título, 'enlace' => $postUrl, 'token_acceso' => $token, ]); $resultados['facebook'] = $respuesta['ok'] ? ['status' => 'éxito', 'mensaje' => 'Publicación de la página de Facebook creada']. : ['estado' => 'error', 'mensaje' => (cadena) ($respuesta['datos']['error']['mensaje'] ?? $respuesta['error'])]; } } if (get_setting('social_auto_instagram', '0') === '1') { $accountId = trim(get_setting('instagram_business_id')); $token = trim(get_setting('meta_page_access_token')); if ($accountId === '' || $token === '' || $imageUrl === '') { $resultados['instagram'] = ['status' => 'skipped', 'message' => 'Falta el ID de empresa de Instagram, el Meta token o la imagen destacada.']; } más { $create = social_http_request('POST', "https://graph.facebook.com/v{$graphVersion}/{$accountId}/media", [ 'image_url' => $imageUrl, 'título' => $título, 'token_acceso' => $token, ]); $creationId = (cadena) ($create['datos']['id'] ?? ''); $publicar = $creationId !== '' ? social_http_request('POST', "https://graph.facebook.com/v{$graphVersion}/{$accountId}/media_publish", [ 'creación_id' => $creaciónId, 'token_acceso' => $token, ]) : $crear; $resultados['instagram'] = $publicar['ok'] ? ['estado' => 'éxito', 'mensaje' => 'Publicación de Instagram creada.'] : ['estado' => 'error', 'mensaje' => (cadena) ($publicar['datos']['error']['mensaje'] ?? $publicar['error'])]; } } if (get_setting('social_auto_pinterest', '0') === '1') { $token = trim(get_setting('pinterest_access_token')); $boardId = trim(get_setting('pinterest_board_id')); si ($token === '' || $boardId === '' || $imageUrl === '') { $resultados['pinterest'] = ['status' => 'skipped', 'message' => 'Falta el token de Pinterest, el ID del tablero o la imagen destacada.']; } más { $respuesta = social_http_request('POST', 'https://api.pinterest.com/v5/pins', [ 'board_id' => $boardId, 'título' => mb_substr($título, 0, 100), 'descripción' => mb_substr($extracto, 0, 500), 'enlace' => $postUrl, 'media_source' => ['source_type' => 'image_url', 'url' => $imageUrl], ], ['Autorización: Portador ' . $token, 'Tipo de contenido: aplicación/json']); $resultados['pinterest'] = $respuesta['ok'] ? ['estado' => 'éxito', 'mensaje' => 'Pin de Pinterest creado']. : ['estado' => 'error', 'mensaje' => (cadena) ($respuesta['datos']['mensaje'] ?? $respuesta['error'])]; } } foreach ($resultados como $plataforma => $resultado) { social_log($pdo, $postId, $plataforma, $resultado['estado'], $resultado['mensaje']); } devolver $resultados; } función seo_meta(cadena $título, cadena $descripción): matriz { regresar [ 'título' => $título? $título. ' | '. APLICACIÓN_NOMBRE: APLICACIÓN_NOMBRE, 'description' => $description?: 'PLR, MRR, plantillas, cursos, libros electrónicos y activos digitales premium listos para vender.', ]; } /** * Remitente de eventos de API de conversiones de Facebook (CAPI) */ función send_fb_capi_event(cadena $nombre_evento, cadena $id_evento, matriz $custom_data = [], matriz $user_data_extra = []): vacío { $pixel_id = get_setting('facebook_pixel_id'); $acceso_token = get_setting('facebook_capi_token'); $test_code = get_setting('facebook_capi_test_code'); // Opcional para pruebas si (!$pixel_id || !$access_token) { regresar; } $url = "https://graph.facebook.com/v19.0/{$pixel_id}/events"; // Datos del usuario (Prioridad a fbp y fbc de las cookies) $datos_usuario = [ 'client_ip_address' => $_SERVER['REMOTE_ADDR'] ?? '', 'client_user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '', ]; si (!empty($_COOKIE['_fbp'])) { $user_data['fbp'] = $_COOKIE['_fbp']; } si (!empty($_COOKIE['_fbc'])) { $user_data['fbc'] = $_COOKIE['_fbc']; } // Fusionar datos de usuario adicionales (como correo electrónico, teléfono, etc.) $datos_usuario = array_merge($datos_usuario, $datos_usuario_extra); $evento = [ 'nombre_evento' => $nombre_evento, 'event_time' => hora(), 'action_source' => 'sitio web', 'event_id' => $event_id, // Crítico para la deduplicación 'datos_usuario' => $datos_usuario, ]; si (!vacío($datos_personalizados)) { $evento['custom_data'] = $custom_data; } $carga útil = [ 'datos' => [$evento], 'token_acceso' => $token_acceso ]; si ($código_prueba) { $carga útil['test_event_code'] = $test_code; } // Solicitud de cURL asíncrona (tiempo de espera de 1 segundo para no bloquear la interfaz de usuario) $ch = curl_init($url); curl_setopt($ch, CURLOPT_POST, verdadero); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($carga útil)); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Tipo de contenido: aplicación/json']); curl_setopt($ch, CURLOPT_RETURNTRANSFER, verdadero); curl_setopt($ch, CURLOPT_TIMEOUT, 2); curl_exec($ch); curl_close($ch); }