An open API service indexing awesome lists of open source software.

https://github.com/rubenslyra/aula-crud-php


https://github.com/rubenslyra/aula-crud-php

Last synced: 5 months ago
JSON representation

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



Gerenciar Contatos


Criar Contato




= $flashMessage['message'] ?>






#
Nome
Email
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 CSRF

2. **Manutenibilidade**
- Separação clara de responsabilidades (MVC)
- Redução de código duplicado
- Organização lógica dos arquivos
- Documentação clara

3. **Expansibilidade**
- Fácil adição de novas funcionalidades
- Framework customizado mas extensível
- Suporte a melhores práticas e padrões

4. **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.