Bank System Using SOLID Principles in OOP PHP
Introduction
Creating a bank system using Object-Oriented Programming (OOP) in PHP and applying SOLID principles ensures a scalable, maintainable, and robust application. This article will guide you through designing a bank system that includes different banks, adding customers, managing accounts, and performing basic operations while adhering to the SOLID principles.
Understanding SOLID Principles
SOLID is an acronym for five design principles intended to make software designs more understandable, flexible, and maintainable:
- S - Single Responsibility Principle: A class should have only one reason to change.
- O - Open/Closed Principle: Software entities should be open for extension but closed for modification.
- L - Liskov Substitution Principle: Objects of a superclass should be replaceable with objects of subclasses without affecting the correctness of the program.
- I - Interface Segregation Principle: No client should be forced to depend on methods it does not use.
- D - Dependency Inversion Principle: High-level modules should not depend on low-level modules but on abstractions.
System Design
The system design involves defining interfaces, abstract classes, and concrete classes to simulate a real-world bank system. We will adhere to the SOLID principles throughout the design. Below is an outline of the components we will create:
- AccountInterface: Defines the essential methods for a bank account.
- AbstractAccount: Abstract class that implements AccountInterface and provides base functionality for accounts.
- BankAccount: Concrete class extending AbstractAccount.
- Customer: Class representing a customer.
- Bank: Class representing the bank.
Code Implementation
AccountInterface
// Single Responsibility Principle: This interface has a single responsibility - defining account operations.
interface AccountInterface {
public function deposit($amount);
public function withdraw($amount);
public function getBalance();
public function getAccountNumber();
}
AbstractAccount
// Open/Closed Principle: This abstract class is open for extension (by creating subclasses) but closed for modification.
abstract class AbstractAccount implements AccountInterface {
protected $accountNumber;
protected $balance;
public function __construct($accountNumber, $initialBalance = 0) {
$this->accountNumber = $accountNumber;
$this->balance = $initialBalance;
}
public function deposit($amount) {
if ($amount > 0) {
$this->balance += $amount;
return true;
}
return false;
}
public function withdraw($amount) {
if ($amount > 0 && $amount <= $this->balance) {
$this->balance -= $amount;
return true;
}
return false;
}
public function getBalance() {
return $this->balance;
}
public function getAccountNumber() {
return $this->accountNumber;
}
}
BankAccount
// Liskov Substitution Principle: BankAccount can be used wherever AbstractAccount is expected.
class BankAccount extends AbstractAccount {
// Additional functionalities can be added here if needed
}
Customer Class
// Single Responsibility Principle: This class has a single responsibility - managing customer details and accounts.
class Customer {
private $name;
private $customerId;
private $accounts = [];
public function __construct($name, $customerId) {
$this->name = $name;
$this->customerId = $customerId;
}
public function addAccount(AccountInterface $account) {
$this->accounts[] = $account;
}
public function getAccounts() {
return $this->accounts;
}
public function getName() {
return $this->name;
}
public function getCustomerId() {
return $this->customerId;
}
}
Bank Class
// Single Responsibility Principle: This class has a single responsibility - managing the bank's customers.
class Bank {
private $name;
private $customers = [];
public function __construct($name) {
$this->name = $name;
}
public function addCustomer(Customer $customer) {
$this->customers[] = $customer;
}
public function getCustomers() {
return $this->customers;
}
public function findCustomerById($customerId) {
foreach ($this->customers as $customer) {
if ($customer->getCustomerId() == $customerId) {
return $customer;
}
}
return null;
}
}
Example Usage
Creating Different Banks and Performing Operations
require 'AccountInterface.php';
require 'AbstractAccount.php';
require 'BankAccount.php';
require 'Customer.php';
require 'Bank.php';
// Create different banks
$sbi = new Bank('SBI');
$icici = new Bank('ICICI');
// Create customers
$customer1 = new Customer('John Doe', 'C001');
$customer2 = new Customer('Jane Smith', 'C002');
// Create bank accounts and add them to customers
$account1 = new BankAccount('A12345', 1000);
$account2 = new BankAccount('A54321', 2000);
$customer1->addAccount($account1);
$customer2->addAccount($account2);
// Add customers to banks
$sbi->addCustomer($customer1);
$icici->addCustomer($customer2);
// Perform operations
$account1->deposit(500);
$account1->withdraw(200);
// Display account balance
echo "Account Balance for {$account1->getAccountNumber()}: " . $account1->getBalance() . PHP_EOL;
// Find a customer by ID in SBI and display their information
$foundCustomer = $sbi->findCustomerById('C001');
if ($foundCustomer) {
echo "Customer Name: " . $foundCustomer->getName() . PHP_EOL;
foreach ($foundCustomer->getAccounts() as $acc) {
echo "Account Number: " . $acc->getAccountNumber() . ", Balance: " . $acc->getBalance() . PHP_EOL;
}
}
Basic Security Measures
Security is a crucial aspect of any banking system. Here are a few basic measures:
- Validation: Validate all inputs to prevent SQL injection and other attacks.
- Sanitization: Sanitize user inputs to prevent cross-site scripting (XSS).
- Encryption: Encrypt sensitive data, such as passwords and account numbers.
- Authentication and Authorization: Implement robust authentication and authorization mechanisms to control access to resources.
Database Operations
Database Connection and Operations
class Database {
private $pdo;
public function __construct($host, $dbname, $username, $password) {
$dsn = "mysql:host=$host;dbname=$dbname";
$this->pdo = new PDO($dsn, $username, $password);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
public function addCustomer(Customer $customer) {
$sql = "INSERT INTO customers (customer_id, name) VALUES (:customer_id, :name)";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([
':customer_id' => $customer->getCustomerId(),
':name' => $customer->getName()
]);
}
public function getCustomerById($customerId) {
$sql = "SELECT * FROM customers WHERE customer_id = :customer_id";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([':customer_id' => $customerId]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
}
Example Usage with Database
// Database connection
$db = new Database('localhost', 'bank_db', 'root', 'password');
// Adding a customer to the database
$customer = new Customer('John Doe', 'C001');
$db->addCustomer($customer);
// Retrieving a customer from the database
$retrievedCustomer = $db->getCustomerById('C001');
echo 'Customer ID: ' . $retrievedCustomer['customer_id'] . ', Name: ' . $retrievedCustomer['name'];
Conclusion
Building a bank system using SOLID principles in OOP PHP involves understanding and applying core principles such as single responsibility, open/closed, Liskov substitution, interface segregation, and dependency inversion. By defining interfaces, abstract classes, and concrete classes, we can create a robust and flexible system that simulates real-world banking operations. Additionally, incorporating security measures and simulating database operations further enhances the system's functionality and reliability.