https://github.com/rubenslyra/aula-crud-php
https://github.com/rubenslyra/aula-crud-php
Last synced: 5 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/rubenslyra/aula-crud-php
- Owner: rubenslyra
- License: mit
- Created: 2025-04-09T14:42:56.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2025-04-09T14:43:07.000Z (6 months ago)
- Last Synced: 2025-04-09T15:44:27.384Z (6 months ago)
- Size: 7.81 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Análise e Refatoração do Sistema CRUD PHP
## Sumário Executivo
O código apresentado é um sistema CRUD (Create, Read, Update, Delete) básico em PHP para gerenciar contatos. A aplicação utiliza PHP e MySQL, com uma estrutura simples de arquivos separados para cada operação. Embora funcional, o código apresenta várias oportunidades de melhoria em termos de segurança, organização, manutenibilidade e boas práticas de desenvolvimento.
## Análise da Estrutura Atual
### Estrutura de Arquivos
```
create.php
delete.php
functions.php
index.php
list.txt
read.php
style.css
update.php
```Esta estrutura organiza o código por funcionalidade, mas não segue padrões modernos de desenvolvimento web.
## Problemas Identificados e Recomendações
### 1. Credenciais de Banco de Dados Expostas
**Problema:** As credenciais do banco de dados estão diretamente no código fonte (`functions.php`).
```php
$DATABASE_HOST = '127.0.0.1';
$DATABASE_USER = 'root';
$DATABASE_PASS = 'Local@123456789';
$DATABASE_NAME = 'crud_aula';
```**Recomendação:**
- Utilizar arquivo `.env` para armazenar configurações sensíveis
- Implementar um sistema de configuração que carregue essas variáveis de ambiente### 2. Ausência de Validação e Sanitização
**Problema:** Falta validação adequada dos dados de entrada do usuário.
```php
$name = isset($_POST['name']) ? $_POST['name'] : '';
```**Recomendação:**
- Implementar validação rigorosa de todos os inputs
- Utilizar funções de sanitização como `filter_var()` ou bibliotecas como PHP Filter
- Validar tipos, formatos (email, telefone) e tamanhos dos campos### 3. Proteção Insuficiente Contra SQL Injection
**Problema:** Embora use prepared statements, o código permite que o ID seja definido pelo usuário.
```php
$id = isset($_POST['id']) && !empty($_POST['id']) && $_POST['id'] != 'auto' ? $_POST['id'] : NULL;
```**Recomendação:**
- Nunca permitir que o usuário defina IDs diretamente
- Remover o campo ID dos formulários de criação
- Garantir que todas as queries utilizem prepared statements corretamente### 4. Falta de Estrutura MVC
**Problema:** O código mistura lógica de negócio, acesso a dados e apresentação.
**Recomendação:**
- Implementar padrão MVC (Model-View-Controller)
- Criar classes para representar modelos (Contact)
- Separar lógica de acesso a dados em repositórios
- Implementar controllers para gerenciar o fluxo da aplicação### 5. Ausência de Tratamento de Erros Robusto
**Problema:** Tratamento de erros básico, com mensagens potencialmente expostas ao usuário.
```php
exit('Failed to connect to database!');
```**Recomendação:**
- Implementar sistema de logging
- Utilizar try/catch consistentemente
- Exibir mensagens amigáveis ao usuário, mas logar detalhes técnicos
- Implementar handling de exceções centralizado### 6. Duplicação de Código
**Problema:** Lógica similar repetida em vários arquivos.
**Recomendação:**
- Centralizar funcionalidades comuns
- Implementar classes base e herança quando apropriado
- Aplicar princípio DRY (Don't Repeat Yourself)### 7. Ausência de Namespace e Autoloading
**Problema:** Não utiliza recursos modernos do PHP como namespaces e autoloading.
**Recomendação:**
- Implementar namespaces para organizar o código
- Utilizar Composer para gerenciamento de dependências
- Configurar autoloading PSR-4### 8. Falta de Proteção CSRF
**Problema:** Formulários não têm proteção contra CSRF (Cross-Site Request Forgery).
**Recomendação:**
- Implementar tokens CSRF em todos os formulários
- Validar tokens no servidor para cada requisição POST## Proposta de Refatoração
### Nova Estrutura de Diretórios
```
/app
/Config
Database.php
App.php
/Controllers
ContactController.php
HomeController.php
/Models
Contact.php
/Views
/contacts
create.php
delete.php
index.php
update.php
/layouts
main.php
/home
index.php
/Core
Router.php
Request.php
Response.php
Controller.php
Database.php
Session.php
Validator.php
/public
index.php
.htaccess
/assets
/css
style.css
/js
script.js
/config
.env
composer.json
```### Exemplo de Implementação Refatorada
#### 1. Configuração de Ambiente (.env)
```
DB_HOST=127.0.0.1
DB_NAME=phpcrud
DB_USER=root
DB_PASS=senha_segura
APP_URL=http://localhost
APP_ENV=development
```#### 2. Model: Contact.php
```php
db = Database::getInstance()->getConnection();
}
public function getAll(int $limit = null, int $offset = null): array {
$sql = 'SELECT * FROM contacts ORDER BY id';
if ($limit !== null && $offset !== null) {
$sql .= ' LIMIT :offset, :limit';
}
$stmt = $this->db->prepare($sql);
if ($limit !== null && $offset !== null) {
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
}
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function getById(int $id): ?array {
$stmt = $this->db->prepare('SELECT * FROM contacts WHERE id = :id');
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result ?: null;
}
public function count(): int {
return $this->db->query('SELECT COUNT(*) FROM contacts')->fetchColumn();
}
public function create(array $data): bool {
$stmt = $this->db->prepare('
INSERT INTO contacts (name, email, phone, title, created)
VALUES (:name, :email, :phone, :title, :created)
');
return $this->bindAndExecute($stmt, $data);
}
public function update(int $id, array $data): bool {
$stmt = $this->db->prepare('
UPDATE contacts
SET name = :name, email = :email, phone = :phone, title = :title, created = :created
WHERE id = :id
');
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
return $this->bindAndExecute($stmt, $data);
}
public function delete(int $id): bool {
$stmt = $this->db->prepare('DELETE FROM contacts WHERE id = :id');
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
return $stmt->execute();
}
private function bindAndExecute(\PDOStatement $stmt, array $data): bool {
$stmt->bindValue(':name', $data['name'] ?? '', PDO::PARAM_STR);
$stmt->bindValue(':email', $data['email'] ?? '', PDO::PARAM_STR);
$stmt->bindValue(':phone', $data['phone'] ?? '', PDO::PARAM_STR);
$stmt->bindValue(':title', $data['title'] ?? '', PDO::PARAM_STR);
$stmt->bindValue(':created', $data['created'] ?? date('Y-m-d H:i:s'), PDO::PARAM_STR);
return $stmt->execute();
}
}
```#### 3. Controller: ContactController.php
```php
contactModel = new Contact();
}
public function index() {
$request = new Request();
$page = $request->getQueryParam('page', 1);
$recordsPerPage = 5;
$contacts = $this->contactModel->getAll($recordsPerPage, ($page - 1) * $recordsPerPage);
$totalContacts = $this->contactModel->count();
return $this->view('contacts/index', [
'contacts' => $contacts,
'currentPage' => $page,
'recordsPerPage' => $recordsPerPage,
'totalContacts' => $totalContacts
]);
}
public function create() {
$request = new Request();
if ($request->isPost()) {
$data = $request->getPostData();
$validator = new Validator();
$validator->validate($data, [
'name' => ['required', 'max:255'],
'email' => ['required', 'email', 'max:255'],
'phone' => ['required', 'max:20'],
'title' => ['max:255']
]);
if ($validator->passes()) {
if ($this->contactModel->create($data)) {
$this->setFlashMessage('success', 'Contato criado com sucesso!');
$this->redirect('/contacts');
return;
}
$this->setFlashMessage('error', 'Erro ao criar contato.');
}
return $this->view('contacts/create', [
'errors' => $validator->getErrors(),
'oldInput' => $data
]);
}
return $this->view('contacts/create');
}
public function edit($id) {
$contact = $this->contactModel->getById($id);
if (!$contact) {
$this->setFlashMessage('error', 'Contato não encontrado.');
$this->redirect('/contacts');
return;
}
return $this->view('contacts/update', [
'contact' => $contact
]);
}
public function update($id) {
$contact = $this->contactModel->getById($id);
if (!$contact) {
$this->setFlashMessage('error', 'Contato não encontrado.');
$this->redirect('/contacts');
return;
}
$request = new Request();
if ($request->isPost()) {
$data = $request->getPostData();
$validator = new Validator();
$validator->validate($data, [
'name' => ['required', 'max:255'],
'email' => ['required', 'email', 'max:255'],
'phone' => ['required', 'max:20'],
'title' => ['max:255']
]);
if ($validator->passes()) {
if ($this->contactModel->update($id, $data)) {
$this->setFlashMessage('success', 'Contato atualizado com sucesso!');
$this->redirect('/contacts');
return;
}
$this->setFlashMessage('error', 'Erro ao atualizar contato.');
}
return $this->view('contacts/update', [
'contact' => $contact,
'errors' => $validator->getErrors(),
'oldInput' => $data
]);
}
}
public function delete($id) {
$contact = $this->contactModel->getById($id);
if (!$contact) {
$this->setFlashMessage('error', 'Contato não encontrado.');
$this->redirect('/contacts');
return;
}
$request = new Request();
if ($request->getQueryParam('confirm') === 'yes') {
if ($this->contactModel->delete($id)) {
$this->setFlashMessage('success', 'Contato excluído com sucesso!');
$this->redirect('/contacts');
return;
}
$this->setFlashMessage('error', 'Erro ao excluir contato.');
$this->redirect('/contacts');
return;
}
return $this->view('contacts/delete', [
'contact' => $contact
]);
}
}
```#### 4. Core: Database.php
```php
connection = new PDO(
"mysql:host={$host};dbname={$name};charset=utf8",
$user,
$pass,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
]
);
} catch (PDOException $e) {
// Log error and display friendly message
error_log("Database Connection Error: " . $e->getMessage());
die("Não foi possível conectar ao banco de dados. Por favor, tente novamente mais tarde.");
}
}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function getConnection() {
return $this->connection;
}
// Prevent cloning
private function __clone() {}
// Prevent unserialization
public function __wakeup() {
throw new \Exception("Cannot unserialize singleton");
}
}
```#### 5. View: contacts/index.php
```php
= $flashMessage['message'] ?>
#
Nome
Telefone
Cargo
Criado em
Ações
Nenhum contato encontrado
= htmlspecialchars($contact['id']) ?>
= htmlspecialchars($contact['name']) ?>
= htmlspecialchars($contact['email']) ?>
= htmlspecialchars($contact['phone']) ?>
= htmlspecialchars($contact['title']) ?>
= date('d/m/Y H:i', strtotime($contact['created'])) ?>
$recordsPerPage): ?>
```
#### 6. Public: index.php (Front Controller)
```php
load();// Initialize error handling
error_reporting(E_ALL);
ini_set('display_errors', $_ENV['APP_ENV'] === 'development' ? 1 : 0);// Initialize session
session_start();// Load router
$router = new \App\Core\Router();// Define routes
$router->get('/', 'HomeController@index');
$router->get('/contacts', 'ContactController@index');
$router->get('/contacts/create', 'ContactController@create');
$router->post('/contacts/create', 'ContactController@create');
$router->get('/contacts/edit/{id}', 'ContactController@edit');
$router->post('/contacts/update/{id}', 'ContactController@update');
$router->get('/contacts/delete/{id}', 'ContactController@delete');// Dispatch request
$router->dispatch();
```## Benefícios da Refatoração Proposta
1. **Segurança Aprimorada**
- Proteção contra SQL Injection
- Validação robusta de dados
- Credenciais seguras em variáveis de ambiente
- Proteção CSRF2. **Manutenibilidade**
- Separação clara de responsabilidades (MVC)
- Redução de código duplicado
- Organização lógica dos arquivos
- Documentação clara3. **Expansibilidade**
- Fácil adição de novas funcionalidades
- Framework customizado mas extensível
- Suporte a melhores práticas e padrões4. **Experiência do Usuário**
- Mensagens de erro amigáveis
- Feedback visual para ações
- Validação de formulários melhorada## Conclusão
O código original, embora funcional, apresenta vulnerabilidades de segurança e limitações de design que dificultam sua manutenção e expansão. A refatoração proposta transforma o projeto em uma aplicação moderna, seguindo padrões de desenvolvimento estabelecidos e boas práticas de segurança.
Esta implementação oferece uma base sólida para adicionar mais funcionalidades no futuro e serve como um bom exemplo de como aplicar princípios de desenvolvimento web modernos em PHP.