Visão Geral da Arquitetura
Uma plataforma de automação de interações sociais construída em PHP adota uma arquitetura modular, na qual cada arquivo assume uma responsabilidade bem definida. O objetivo é criar um ponto central de processamento de requisições, oferecer endpoints para ações automatizadas e integrar autenticação via provedores sociais sem armazenar senhas dos usuários. A estrutura típica inclui roteamento via Apache, um controlador frontal em index.php, serviços de API em api.php, configurações centralizadas em config.php e módulos específicos para OAuth, como social.php e qqauth.php.
- Roteamento Amigável com Apache e .htaccess
O Apache processa as requisições HTTP e, por meio do módulo mod_rewrite, converte URLs semânticas em parâmetros internos. Essa abordagem oculta o arquivo de entrada, melhora a indexação por mecanismos de busca e protege a estrutura interna da aplicação.
1.1 Regras Básicas de Reescrita
O arquivo .htaccess na raiz do projeto deve ativar o mecanismo de reescrita e redirecionar todas as requisições dinâmicas para index.php, preservando recursos estáticos como imagens, CSS e JavaScript.
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?rota=$1 [QSA,L]
Na prática, uma requisição como /tarefa/criar é transformada internamente em index.php?rota=tarefa/criar, permitindo que o PHP realize o roteamento programático. O parâmetro QSA assegura que parâmetros de consulta existentes sejam preservados, enquanto L indica que aquela é a última regra aplicável.
1.2 Restrições de Acesso e Proteção de Arquivos
Arquivos sensíveis, como configurações e scripts auxiliares, devem ser bloqueados para acesso externo.
<FilesMatch "\.(sql|log|bak|config)$">
Require all denied
</FilesMatch>
<Files "config.php">
Require all denied
</Files>
Além disso, é recomendável desabilitar a execução de scripts em diretórios de upload e forçar conexões HTTPS para endpoints administrativos:
RewriteCond %{HTTPS} off
RewriteRule ^(admin|painel)/ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
1.3 Compressão e Cache
Para melhorar a performance, o servidor pode comprimir respostas de texto e definir cabeçalhos de cache para recursos estáticos.
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/css application/javascript
</IfModule>
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/png "access plus 1 month"
ExpiresByType text/css "access plus 1 week"
</IfModule>
- Controlador Frontal em index.php
O arquivo index.php atua como controlador frontal. Sua função é analisar a rota presente na URL, iniciar a sessão, verificar autenticação e delegar o processamento aos controladores específicos.
<?php
require_once 'config.php';
session_start();
$rota = isset($_GET['rota']) ? trim($_GET['rota'], '/') : 'inicio';
$segmentos = explode('/', filter_var($rota, FILTER_SANITIZE_URL));
$controlador = $segmentos[0] ?: 'inicio';
$acao = $segmentos[1] ?? 'index';
$rotasPublicas = ['inicio', 'login', 'registrar', 'oauth'];
$rotasProtegidas = ['painel', 'tarefas', 'perfil'];
if (in_array($controlador, $rotasProtegidas) && empty($_SESSION['usuario_id'])) {
header('Location: /login');
exit;
}
$arquivoControlador = "controllers/{$controlador}.php";
if (file_exists($arquivoControlador)) {
require_once $arquivoControlador;
} else {
http_response_code(404);
require_once 'views/erros/404.php';
}
Esse padrão mantém a lógica de roteamento concentrada e facilita a adição de novas funcionalidades sem modificar o servidor web.
- API em api.php
O arquivo api.php expõe uma interface programática para ações da plataforma. Ele recebe requisições HTTP, valida parâmetros, executa regras de negócio e retorna respostas JSON uniformes.
3.1 Estrutura de Resposta Padronizada
Todas as respostas seguem um formato comum, facilitando o consumo por clientes web, mobile ou scripts externos.
<?php
require_once 'config.php';
header('Content-Type: application/json; charset=utf-8');
$resposta = [
'status' => 'sucesso',
'codigo' => 200,
'dados' => null,
'mensagem' => ''
];
try {
$metodo = $_SERVER['REQUEST_METHOD'];
$entrada = json_decode(file_get_contents('php://input'), true) ?? [];
if ($metodo !== 'POST') {
throw new Exception('Método não permitido', 405);
}
$acao = $entrada['acao'] ?? '';
switch ($acao) {
case 'criarTarefa':
$resposta['dados'] = criarTarefa($entrada);
break;
case 'consultarStatus':
$resposta['dados'] = consultarStatus($entrada);
break;
default:
throw new Exception('Ação não encontrada', 404);
}
} catch (Exception $e) {
$resposta['status'] = 'erro';
$resposta['codigo'] = $e->getCode() ?: 500;
$resposta['mensagem'] = $e->getMessage();
http_response_code($resposta['codigo']);
}
echo json_encode($resposta, JSON_UNESCAPED_UNICODE);
3.2 Validação de Dados de Entrada
A criação de uma tarefa exige validações rigorosas para evitar injeção de dados, abuso de recursos e inconsistências.
function criarTarefa(array $payload): array
{
$camposObrigatorios = ['plataforma', 'destino', 'quantidade'];
foreach ($camposObrigatorios as $campo) {
if (empty($payload[$campo])) {
throw new Exception("Campo obrigatório ausente: {$campo}", 400);
}
}
$plataformasPermitidas = ['qq', 'wechat', 'weibo'];
if (!in_array($payload['plataforma'], $plataformasPermitidas, true)) {
throw new Exception('Plataforma não suportada', 400);
}
if (!filter_var($payload['destino'], FILTER_VALIDATE_URL)) {
throw new Exception('URL de destino inválida', 400);
}
$quantidade = filter_var($payload['quantidade'], FILTER_VALIDATE_INT);
if ($quantidade < 1 || $quantidade > 500) {
throw new Exception('Quantidade fora do intervalo permitido', 400);
}
$pdo = conectarBanco();
$stmt = $pdo->prepare(
"INSERT INTO tarefas (plataforma, destino, quantidade, criado_em)
VALUES (:plataforma, :destino, :quantidade, NOW())"
);
$stmt->execute([
':plataforma' => $payload['plataforma'],
':destino' => $payload['destino'],
':quantidade' => $quantidade
]);
return ['tarefa_id' => $pdo->lastInsertId()];
}
3.3 Comunicação Segura com Serviços Externos
Quando a plataforma precisa consumir APIs de redes sociais, o cURL é configurado para forçar validação de certificado e limitar o tempo de espera.
function requisitarApiExterna(string $url, array $dados = [], array $cabecalhos = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
if (!empty($dados)) {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($dados));
}
if (!empty($cabecalhos)) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $cabecalhos);
}
$resultado = curl_exec($ch);
if (curl_errno($ch)) {
$erro = curl_error($ch);
curl_close($ch);
throw new Exception("Falha na requisição: {$erro}", 502);
}
$codigoHttp = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return ['codigo' => $codigoHttp, 'corpo' => $resultado];
}
- Integração OAuth2.0 com Redes Sociais
A autenticação por meio de provedores sociais permite que o usuário acesse a plataforma sem criar uma nova senha. O fluxo segue o padrão OAuth2.0 com o modo authorization code.
4.1 Geração da URL de Autorização
Antes de redirecionar o usuário, o sistema gera um valor aleatório state e o armazena na sessão para prevenir ataques CSRF.
<?php
session_start();
$appId = getenv('QQ_APP_ID');
$uriCallback = urlencode('https://exemplo.com/qqauth.php');
$scope = 'get_user_info';
$estado = bin2hex(random_bytes(16));
$_SESSION['oauth_state'] = $estado;
$urlAutorizacao = "https://graph.qq.com/oauth2.0/authorize?" .
"client_id={$appId}" .
"&redirect_uri={$uriCallback}" .
"&response_type=code" .
"&scope={$scope}" .
"&state={$estado}";
header("Location: {$urlAutorizacao}");
exit;
4.2 Tratamento do Callback e Obtenção do Token
Após o usuário autorizar, o provedor redireciona de volta com o parâmetro code. O sistema valida o state e troca o código por um access_token.
<?php
session_start();
if (!isset($_GET['code'], $_GET['state'])) {
http_response_code(400);
exit('Parâmetros de callback incompletos.');
}
$stateRecebido = $_GET['state'];
if (!hash_equals($_SESSION['oauth_state'] ?? '', $stateRecebido)) {
http_response_code(403);
exit('Token state inválido.');
}
unset($_SESSION['oauth_state']);
$appId = getenv('QQ_APP_ID');
$appSecret = getenv('QQ_APP_SECRET');
$callback = 'https://exemplo.com/qqauth.php';
$parametros = [
'grant_type' => 'authorization_code',
'client_id' => $appId,
'client_secret' => $appSecret,
'code' => $_GET['code'],
'redirect_uri' => $callback
];
$urlToken = 'https://graph.qq.com/oauth2.0/token?' . http_build_query($parametros);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $urlToken);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$resposta = curl_exec($ch);
curl_close($ch);
parse_str($resposta, $tokens);
if (empty($tokens['access_token'])) {
http_response_code(401);
exit('Não foi possível obter o access token.');
}
$_SESSION['access_token'] = $tokens['access_token'];
4.3 Obtenção de Dados do Usuário e Criação de Sessão
Com o token em mãos, a aplicação consulat a API do provedor e cria ou atualiza o usuário local.
function obterOpenId(string $accessToken): ?string
{
$url = 'https://graph.qq.com/oauth2.0/me?access_token=' . urlencode($accessToken);
$retorno = file_get_contents($url);
if (preg_match('/callback\((.*?)\);/i', $retorno, $correspondencias)) {
$dados = json_decode($correspondencias[1], true);
return $dados['openid'] ?? null;
}
return null;
}
function sincronizarUsuarioSocial(string $provedor, array $dados): int
{
$pdo = conectarBanco();
$stmt = $pdo->prepare(
"SELECT usuario_id FROM vinculos_sociais
WHERE provedor = :provedor AND identificador_externo = :id"
);
$stmt->execute([
':provedor' => $provedor,
':id' => $dados['openid']
]);
if ($linha = $stmt->fetch()) {
return (int) $linha['usuario_id'];
}
$pdo->beginTransaction();
try {
$stmt = $pdo->prepare(
"INSERT INTO usuarios (nome, avatar, criado_em) VALUES (:nome, :avatar, NOW())"
);
$stmt->execute([
':nome' => $dados['nome'],
':avatar' => $dados['avatar']
]);
$novoUsuarioId = (int) $pdo->lastInsertId();
$stmt = $pdo->prepare(
"INSERT INTO vinculos_sociais
(usuario_id, provedor, identificador_externo, token_acesso)
VALUES (:usuario, :provedor, :id, :token)"
);
$stmt->execute([
':usuario' => $novoUsuarioId,
':provedor' => $provedor,
':id' => $dados['openid'],
':token' => $dados['access_token']
]);
$pdo->commit();
return $novoUsuarioId;
} catch (Exception $e) {
$pdo->rollBack();
throw $e;
}
}
Para ambientes com múltiplos servidores, o armazenamento de sessão pode ser centralizado no Redis.
ini_set('session.save_handler', 'redis');
ini_set('session.save_path', 'tcp://127.0.0.1:6379');
session_start();
- Configuração Centralizada em config.php
O arquivo config.php concentra todas as definições sensíveis e comportamentais do sistema. Em vez de valores hardcoded, ele lê variáveis de ambiente, conforme os princípios do Twelve-Factor App.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
if (file_exists(__DIR__ . '/.env')) {
$linhas = parse_ini_file(__DIR__ . '/.env', false, INI_SCANNER_TYPED);
foreach ($linhas as $chave => $valor) {
putenv("{$chave}={$valor}");
}
}
define('AMBIENTE', getenv('APP_ENV') ?: 'producao');
define('DEPURACAO', AMBIENTE === 'desenvolvimento');
define('DB_HOST', getenv('DB_HOST'));
define('DB_USUARIO', getenv('DB_USUARIO'));
define('DB_SENHA', getenv('DB_SENHA'));
define('DB_NOME', getenv('DB_NOME'));
define('DB_CHARSET', 'utf8mb4');
define('QQ_APP_ID', getenv('QQ_APP_ID'));
define('QQ_APP_SECRET', getenv('QQ_APP_SECRET'));
define('WECHAT_APP_ID', getenv('WECHAT_APP_ID'));
define('WECHAT_APP_SECRET', getenv('WECHAT_APP_SECRET'));
function conectarBanco(): PDO
{
static $pdo = null;
if ($pdo === null) {
$dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NOME . ";charset=" . DB_CHARSET;
$opcoes = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
];
$pdo = new PDO($dsn, DB_USUARIO, DB_SENHA, $opcoes);
}
return $pdo;
}
O arquivo .env correspondente deve ter permissões restritas e nunca ser versionado.
APP_ENV=producao
DB_HOST=localhost
DB_USUARIO=app_user
DB_SENHA=senha_segura_aqui
DB_NOME=plataforma_social
QQ_APP_ID=123456789
QQ_APP_SECRET=chave_secreta
WECHAT_APP_ID=wx1234567890
WECHAT_APP_SECRET=segredo_wechat
- Considerações de Segurança e Conformidade
Ao operar uma plataforma que interage com redes sociais, é essencial adotar práticas que protejam tanto a aplicação quanto os usuários finais.
6.1 Proteção Contra Ataques Comuns
- Injeção SQL: usar instruções preparadas em todas as consultas ao banco de dados.
- Cross-Site Scripting (XSS): escapar saídas com
htmlspecialchars()antes de exibir dados. - Cross-Site Request Forgery (CSRF): exigir tokens em formulários e validar o parâmetro
stateno OAuth. - Exposição de arquivos: bloquear acesso a
.env,config.phpe backups via.htaccess.
6.2 Controle de Taxa de Requisições
Limitar o número de chamadas por usuário ou IP reduz o risco de abuso e protege contra ataques de força bruta.
function verificarLimiteRequisicoes(string $identificador, int $limite = 60, int $janela = 60): bool
{
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$chave = "rate_limit:{$identificador}";
$atual = $redis->incr($chave);
if ($atual === 1) {
$redis->expire($chave, $janela);
}
return $atual <= $limite;
}
6.3 Conformidade e Uso Responsável
Plataformas de automação social devem respeitar os termos de serviço de cada rede integrada e as legislações de proteção de dados, como a LGPD no Brasil e o GDPR na Europa. A coleta de informações deve ser limitada ao estritamente necessário, e os usuários devem ser informados sobre como seus dados são armazenados e processados. O armazenamento de tokens de acesso deve ser criptografado, e logs de acesso devem ser mantidos apenas pelo tempo necessário, com anonimização quando apropriado.
| Componente | Responsabilidade Principal | Tecnologias Envolvidas |
|---|---|---|
.htaccess |
Roteamento, restrição de acesso, compressão | Apache, mod_rewrite, mod_deflate |
index.php |
Controlador frontal e distribuição de rotas | PHP, sessões |
api.php |
Endpoints para criação e consulta de tarefas | PHP, JSON, PDO |
social.php |
Abstração de provedores OAuth | OAuth2.0, cURL |
config.php |
Centralização de configurações e conexão com banco | PHP, variáveis de ambiente |
| Redis | Armazenamento de sessões e rate limiting | Redis, extensão phpredis |