The previous solutions like using an interface, restricting "doors" to a subclasss, or a composite class are ok.
Sometimes, when defining a class hierarchy, you may find a feature (method or property) that maybe implemented, or may not be implemented, in descendant classes. My proposed solution is, add that feature in the base class as an "abstract or virtual feature" and let each class to decide to override or not.
// check that in base classes,
// is common to have most stuff private or protected, not public
class Vehicle {
...
// using a protected variable field for properties
protected $_doors = array();
protected $_wings = array();
// you may want to use the methods or the "__call" way,
// Important, these are intentionally "protected", not "public"
protected /* array */ getDoors()
{
return $this->_doors;
} // /* array */ getDoors(...)
protected /* array */ setDoors(/* array */ p_doors)
{
$this->_doors = p_doors;
} // /* array */ SetDoors(...)
protected /* void */ function addDoor(/* array */ $args)
{
array_push($this->doors, $args[0]);
} // /* void */ function addDoor(...)
// you may want to use the methods or the "__call" way,
// Important, these are intentionally "protected", not "public"
protected /* array */ getWings()
{
return $this->_wings;
} // /* array */ getWings(...)
protected /* array */ setWings(/* array */ p_wings)
{
$this->_wings = p_wings;
} // /* array */ SetWings(...)
protected /* void */ function addWing(/* array */ $args)
{
array_push($this->wings, $args[0]);
} // /* void */ function addWing(...)
// these one is always public in all classes
public /* bool */ function supportsDoors()
{
return false;
}
// these one is always public in all classes
public /* bool */ function supportsWings()
{
return false;
}
} // class Vehicle
class Car extends Vehicle {
// these one is always public in all classes
public /* bool */ function supportsDoors()
{
return true;
}
public /* array */ getDoors()
{
return $this->_doors;
} // /* array */ getDoors(...)
// promoted from "protected" to "public"
public /* array */ setDoors(/* array */ p_doors)
{
$this->_doors = p_doors;
} // /* array */ SetDoors(...)
// promoted from "protected" to "public"
public /* void */ function addDoor(/* array */ $args)
{
array_push($this->doors, $args[0]);
} // /* void */ function addDoor(...)
} // class Car
class JetPack extends Vehicle {
// these one is always public in all classes
public /* bool */ function supportsWings()
{
return true;
}
// promoted from "protected" to "public"
public /* array */ getWings()
{
return $this->_wings;
} // /* array */ getWings(...)
// promoted from "protected" to "public"
public /* array */ setWings(/* array */ p_wings)
{
$this->_wings = p_wings;
} // /* array */ SetWings(...)
public /* void */ function addWing(/* array */ $args)
{
array_push($this->wings, $args[0]);
} // /* void */ function addWing(...)
} // class JetPack
class Boeing extends Vehicle {
// these one is always public in all classes
public /* bool */ function supportsDoors()
{
return true;
}
// these one is always public in all classes
public /* bool */ function supportsWings()
{
return true;
}
public /* array */ getDoors()
{
return $this->_doors;
} // /* array */ getDoors(...)
// promoted from "protected" to "public"
public /* array */ setDoors(/* array */ p_doors)
{
$this->_doors = p_doors;
} // /* array */ SetDoors(...)
// promoted from "protected" to "public"
public /* void */ function addDoor(/* array */ $args)
{
array_push($this->doors, $args[0]);
} // /* void */ function addDoor(...)
// promoted from "protected" to "public"
public /* array */ getWings()
{
return $this->_wings;
} // /* array */ getWings(...)
// promoted from "protected" to "public"
public /* array */ setWings(/* array */ p_wings)
{
$this->_wings = p_wings;
} // /* array */ SetWings(...)
public /* void */ function addWing(/* array */ $args)
{
array_push($this->wings, $args[0]);
} // /* void */ function addWing(...)
} // class JetPack
Resume: "Doors" & "Wings" are declared as a "protected virtual" in the base class, and hence, all descendant classes have it as protected, but only some classes implement that feature, and promote to public the features, wheter methods, or properties.
As an additional remark, I personally dislike using the "quick & dirty virtual" properties and methods, way of PHP, and instead, using explicit "getMyProperty" & "setMyProperty" methods, or "myMethod()", because are a best practice. I suggest to avoid using these common "_call" feature._