Vehicle Rental Service
Part of the Object Oriented Programming section of Coddy's PHP journey — lesson 91 of 91.
Challenge
EasyLet's build a Vehicle Rental Service system that manages a fleet of vehicles, tracks customer rentals, and calculates pricing based on vehicle type. This comprehensive challenge brings together inheritance, interfaces, encapsulation, and the State pattern to create a realistic rental business application.
You'll organize your code across seven files:
VehicleState.php— Define aVehicleStateinterface that represents the different states a vehicle can be in. Include two methods:getStatus(): stringandcanRent(): bool. Then create three classes implementing this interface:AvailableState— returns"Available"andtrueRentedState— returns"Rented"andfalseMaintenanceState— returns"Maintenance"andfalse
Vehicle.php— Include the VehicleState file. Create an abstractVehicleclass with constructor promotion forlicensePlate(string, public readonly),brand(string, public readonly), anddailyRate(float, protected). Store the current state in a private property, defaulting toAvailableState. Implement:getState(): VehicleState— returns current statesetState(VehicleState $state): void— changes the stategetStatus(): string— delegates to state'sgetStatus()canRent(): bool— delegates to state'scanRent()abstract calculateCost(int $days): float— each vehicle type calculates differentlygetType(): string— returns the class name usingstatic::class
Vehicles.php— Include the Vehicle file. Create three concrete vehicle classes:Car— extends Vehicle;calculateCost()returnsdailyRate * daysMotorcycle— extends Vehicle;calculateCost()returnsdailyRate * days * 0.8(20% discount)Van— extends Vehicle with an additional$mileageRate(float) parameter;calculateCost(int $days, int $miles = 0)returns(dailyRate * days) + (mileageRate * miles)
Customer.php— Create aCustomerclass with constructor promotion forid(int, public readonly) andname(string, public readonly). Track rental history in a private array. Implement:addRental(string $vehiclePlate, float $cost): void— adds to historygetRentalCount(): int— returns number of rentalsgetTotalSpent(): float— returns sum of all rental costs
Rental.php— Include the Vehicle and Customer files. Create aRentalclass that links a customer to a vehicle. Use constructor promotion forcustomer(Customer, public readonly),vehicle(Vehicle, public readonly),days(int, private), and optionallymiles(int, private, default 0). Implement:calculateTotal(): float— if vehicle is a Van, pass miles tocalculateCost(); otherwise just pass dayscomplete(): string— calculates total, adds to customer's history, sets vehicle to AvailableState, returns"Rental completed: $[total]"(2 decimal places)
RentalService.php— Include the Rental and Vehicles files. Create aRentalServiceclass that manages the fleet. Store vehicles in a private array indexed by license plate. Implement:addVehicle(Vehicle $vehicle): void— adds to fleetfindVehicle(string $plate): ?Vehicle— returns vehicle or nullgetAvailableVehicles(): array— returns vehicles wherecanRent()is truerentVehicle(Customer $customer, string $plate, int $days, int $miles = 0): string|Rental— if vehicle not found, return"Vehicle not found"; if not available, return"Vehicle is not available"; otherwise set vehicle to RentedState and return a new Rental objectsetMaintenance(string $plate): string— sets vehicle to MaintenanceState; return"[plate] set to maintenance"or"Vehicle not found"
main.php— Include the RentalService and Customer files. You'll receive three inputs: fleet data (JSON), customer data (JSON), and operations (JSON).Fleet JSON format:
[{"type": "car", "plate": "CAR-001", "brand": "Toyota", "rate": 50}, {"type": "van", "plate": "VAN-001", "brand": "Ford", "rate": 80, "mileage_rate": 0.25}]Customer JSON format:
{"id": 1, "name": "Alice"}Operations JSON format:
[{"op": "available"}, {"op": "rent", "plate": "CAR-001", "days": 3}, {"op": "complete", "plate": "CAR-001"}, {"op": "maintenance", "plate": "VAN-001"}, {"op": "customer_stats"}]Create the rental service, add all vehicles to the fleet, and create the customer. Keep track of active rentals by plate. Process each operation:
"available"— Print"Available vehicles: [count]""rent"— Rent the vehicle; if successful, store the Rental and print"Rented [plate] for [days] days"; if Van, include miles from the operation; otherwise print the error message"complete"— Complete the stored rental for that plate; print the result"maintenance"— Print the result ofsetMaintenance()"status"— Print"[plate]: [status]"for the given plate"customer_stats"— Print"[name]: [count] rentals, $[total] spent"(2 decimal places)
Print each result on a new line.
This system showcases how the State pattern elegantly handles vehicle availability without complex conditionals, how inheritance creates a clean vehicle hierarchy with polymorphic pricing, and how encapsulation protects rental data integrity throughout the workflow.
Try it yourself
<?php
require_once 'RentalService.php';
require_once 'Customer.php';
// Read inputs
$fleetJson = trim(fgets(STDIN));
$customerJson = trim(fgets(STDIN));
$operationsJson = trim(fgets(STDIN));
// Parse JSON data
$fleetData = (array)json_decode($fleetJson, true);
$customerData = (array)json_decode($customerJson, true);
$operations = (array)json_decode($operationsJson, true);
// TODO: Create RentalService instance
// TODO: Add all vehicles to the fleet based on type:
// - "car" -> new Car(plate, brand, rate)
// - "motorcycle" -> new Motorcycle(plate, brand, rate)
// - "van" -> new Van(plate, brand, rate, mileage_rate)
// TODO: Create Customer instance from customerData
// TODO: Create array to track active rentals by plate
$activeRentals = [];
// TODO: Process each operation and print results:
// - "available": Print "Available vehicles: [count]"
// - "rent": Rent vehicle, store Rental if successful, print result
// - "complete": Complete the rental, print result
// - "maintenance": Set maintenance, print result
// - "status": Print "[plate]: [status]"
// - "customer_stats": Print "[name]: [count] rentals, $[total] spent"
foreach ($operations as $op) {
$operation = (array)$op;
$opType = $operation['op'];
// TODO: Handle each operation type
switch ($opType) {
case 'available':
// TODO: Print available vehicle count
break;
case 'rent':
// TODO: Rent vehicle and print result
break;
case 'complete':
// TODO: Complete rental and print result
break;
case 'maintenance':
// TODO: Set maintenance and print result
break;
case 'status':
// TODO: Print vehicle status
break;
case 'customer_stats':
// TODO: Print customer statistics
break;
}
}
?>All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe $this KeywordMethodsPropertiesConstructor (__construct)Destructor (__destruct)Recap - Simple Calculator4Inheritance
Basic InheritanceThe parent:: KeywordMethod OverridingThe final KeywordAbstract ClassesRecap - Employee Hierarchy7Encapsulation
Public, Protected, PrivateAccess Modifiers in DepthGetters and SettersInformation HidingConstructor Promotion (8.0)Recap - Student Records System2Namespaces & Autoloading
Introduction to NamespacesThe use KeywordPSR-4 Autoloading StandardComposer AutoloaderRecap - Organized Project5Interfaces & Contracts
Introduction to InterfacesImplementing InterfacesMultiple Interface ImplementInterface vs Abstract ClassType Hinting with InterfacesRecap - Shape Calculator8Magic Methods
Magic Methods Introduction__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Object Cloning__serialize & __unserializeRecap - Custom Collection11Type System & Error Handling
Type DeclarationsNullable TypesUnion & Intersection TypesException ClassesCustom Exception HierarchyTry, Catch, FinallyRecap - Form Validator14Project: Library Management
Project OverviewBook and User Classes3Class Properties
Instance vs Static PropertiesConstants in ClassesStatic Methods & PropertiesPrivate & Protected PropertiesReadonly Properties (PHP 8.1)Recap - Bank Account Manager6Polymorphism
Method Overriding RevisitedPolymorphism via InterfacesType Hinting & Union TypesLate Static BindingRecap - Payment Processor9Traits
Introduction to TraitsUsing Multiple TraitsTrait Conflict ResolutionAbstract Methods in TraitsTraits vs Inheritance12Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy Pattern