What's the best way to cast a custom Class in PHP
casting, class, php, typeof
Solution
Short answer to your question, no you cannot recast an object to another object.
However, Dan Lee's response is a good one and close to what I am suggesting. Why not make the `fillTank` an attribute of a vehicle object, this way all objects extending the vehicle class will know how to fill their own tanks. Something like this:
abstract class Vehicle
{
protected $tank1;
protected $tank2;
// Declaring an abstract function in parent class forces all child class to
// implement same class
abstract public function fillGas() {}
}
class Car extends Vehicle
{
public function fillGas()
{
$this->tank1 = 'full';
}
}
class SportsCar extends Vehicle
{
public function fillGas()
{
$this->tank1 = 'full';
$this->tank2 = 'full';
}
}
class Skateboard extends Vehicle
{
// Skateboards don't have gastanks, just here to sastify parent abstract definition
public function fillGas() {}
}
Of course the big fallacy with your OP is that you are assuming that ALL sportscars have two gas tanks, when in fact this not the case. Only certain sportscars have multiple gas tanks.
Another approach is to take a look at `traits` (available as of PHP 5.4). It appears you can enforce interface as well as implementation across objects which do not extend the same class.
-- Update --
Update (Based on responses): In my 'real' case... imagine I have to check various Vehicle types, each with different paint, body, interior, gas_type, cleaner_type, color, etc...
All these attributes you mention are Vehicle attributes, not gastation, fillingstation, parkinglot etc attributes, as such I would add all these attributes to the vehicle class, then you can pass a vehicle to a `GasStation::cleanVehicle()` factory method to manipulate the vehicle attributes.
The code snippet below is DEMONSTRATIVE only, demonstrating how aforementioned attributes should be attached to the vehicle class, and how the `GasStation` class can manipulate vehicle attributes based on the class of vehicle. I wrote the following in 5 minutes, but it's obvious that it will take more thought to properly handle the factory method and whether to pass off to other objects etc. Consider the following:
abstract class Vehicle
{
// Setting these to public for demonstration only, otherwise you should set these
// to protected and write public accessors
public $paintType;
public $bodyType;
public $interior;
}
class Car extends Vehicle
{
}
class Suv extends Vehicle
{
}
class Truck extends Vehicle
{
}
class GasStation
{
public static function cleanVehicle(Vehicle $vehicle)
{
switch (get_class($vehicle)) {
case 'Car':
// Car specific cleaning
break;
case 'Truck':
// Truck specific cleaning
break;
default:
throw new Exception(sprintf('Invalid $vehicle: %s', serialize($vehicle)));
}
// We've gone through our vehicle specific cleaning, now we can do generic
if ('Leather' === $vehicle->getInterior()) {
// Leather specific cleaning
}
if ('Sedan' === $vehicle->getBodyType()) {
// Sedan specific cleaning
}
}
}
$car = new Car();
$car->setPaintType = 'Glossy';
$car->setBodyType = 'Sedan';
$car->setInterior = 'Cloth';
$suv = new Suv();
$suv->setPaintType = 'Glossy';
$suv->setBodyType = 'Crossover';
$suv->setInterior = 'Leather';
$truck = new Truck();
$truck->setPaintType = 'Flat';
$truck->setBodyType = 'ClubCab';
$truck->setInterior = 'Cloth';
$vehicles = array($car, $suv, $truck);
foreach ($vehicles as $vehicle) {
GasStation::cleanVehicle($vehicle);
}
Problem
I know off the bat some of you will assume Interface or Abstract, but that only handles SOME of the situations. Here's an example where they break. Assume we have classes that implement the same interface and extend the same base ``` class car extends fourwheeler implements ipaygas{ protected $tank1; //interface public function payGas($amount){} } class sportscar extends fourwheeler implements ipaygas{ protected $tank1; protected $tank2; //interface public function payGas($amount){} } interface ipaygas{ function payGas($amount); } ``` In some situations an interface is all you need as you may only want to execute 'payGas()'. But what do you do when you have conditions to be met. Example, what if - before paying gas you need to (1) check the car type, (2) use premium gas for the sports car, and (3) fill the second tank of the sports car. THIS IS WHAT I WANT TO DO BUT CANNOT ``` function pumpAndPay(iPayGas $car){ if(gettype($car) == "car"){ fillTank($car,(car) $car->tank1); }else{ fillTank($car,(sportscar) $car->tank1); fillTank($car,(sportscar) $car->tank2); } } ``` How can I do this with real type casting? Is it possible in PHP? Update (Based on responses): In my 'real' case... imagine I have to check various Vehicle types, each with different paint, body, interior, gas_type, cleaner_type, color, etc... ``` abstract class AVechicle{} abstract class ACar extends AVechicle{} abstract class ATruckOrSUV extends AVechicle{} abstract class ABike extends AVechicle{} class Car extends ACar{} class SportsCar extends ACar{} class SUV extends ATruckOrSUV{} class Truck extends ATruckOrSUV{} class Bike extends ABike{} class Scooter extends ABike{} class GasStation{ public function cleanVehicle(AVehicle $car){ //assume we need to check the car type to know //what type of cleaner to use and how to clean the car //if the car has leather or bucket seats //imagine we have to add an extra $2/h for sports cars //imagine a truck needs special treatment tires //or needs inspection } public function pumpAndPay(AVehicle $car){ //need to know vehicle type to get gas type //maybe we have a special for scooters only, Green Air campaign etc. } public function fullService(AVehicle $car){ //need to know if its a truck to do inspection FIRST $this->cleanVehicle($car); $this->pumpAndPay($car); //bikes get 10% off //cars get free carwash } } ``` Interfaces and abstracts alone will only go so far...