Ral
Ral

Reputation: 390

PHP Inheretence of methods through multiple layers of classes

I have code that when heavily boiled down, looks pretty much like this:

abstract class Car {
    public abstract static function parts();
    protected static function getParts() {
        $parts = [];
        $properties = (new ReflectionClass(static::class))->getProperties();
        foreach ($properties as $property) {
            $parts[] = $property->getName();
        }
        return $parts;
    }
}
class Ford extends car {
    public $wheels;
    public $body;
    public static function parts() {
        return self::getParts();
    }
}
class FordTruck extends Ford {
    public $truckBed;
}

function getBaseParts($cartype) {
    return $cartype::parts();
}

But when I call getBaseParts("FordTruck") I get back

array(3) {
  [0]=>
  string(8) "truckBed"
  [1]=>
  string(6) "wheels"
  [2]=>
  string(4) "body"
}

In reality, I only want back "wheels" and "body", not the 'extraneous' stuff that's added in classes past Ford. I thought that creating a parts() method in the class whose parts I care about, that then calls self::getParts(), which in turn gets the properties from static would mean that the class that 'static' is referring to would be the Ford class. But it's more like it's calling parts() from the FordTruck class.

Is there a way I can get the functionality I'm looking for with this setup? The 'easiest' way I can think of would be moving the 'getParts()' method into the Ford class and have it call a reflectionclass of self, but I have dozens of classes that extend Ford that would all just be copied code if I fixed it that way.

Upvotes: 0

Views: 52

Answers (3)

ybenhssaien
ybenhssaien

Reputation: 4105

I am not really fan of this solution, you rather change the mindset behind your solution, but here is a simple solution to you problem here

<?php

abstract class Car {
    public abstract static function parts();
    protected static function getParts($class = null) {
        return array_keys(get_class_vars($class ?? static::class));
    }
}
class Ford extends car {
    public $wheels;
    public $body;
    public static function parts() {
        return self::getParts(Ford::class);
    }
}
class FordTruck extends Ford {
    public $truckBed;
}

function getBaseParts($cartype) {
    return $cartype::parts();
}

var_dump(getBaseParts("FordTruck"));

Test : https://3v4l.org/s8Aar

Upvotes: 2

MahnQL
MahnQL

Reputation: 81

static::class is always going to return the 'lowest level' class that the object in question is instantiated as. As such, static::class is always going to return FordTruck even though it's defined in Car and the function itself is called in Ford

On the other hand, self::class returns the classname of the class it's defined in. So self::class in Ford is going to return Ford, even though the object is technically a FordTruck.

The easiest way to get the functionality you want is to pass the value of self::class from Ford to getParts() and use that in your Reflection.

So, for example:

<?php
     abstract class Car {
        public abstract static function parts();
        protected static function getParts(string $className) {
            $parts = [];
            $properties = (new ReflectionClass($className))->getProperties();
            foreach ($properties as $property) {
                $parts[] = $property->getName();
            }
            return $parts;
        }
    }
    class Ford extends Car {
        public $wheels;
        public $body;
        public static function parts() {
            return self::getParts(self::class);
        }
    }
    class FordTruck extends Ford {
        public $truckBed;
    }
    
    function getBaseParts($cartype) {
        return $cartype::parts();
    } 

Upvotes: 1

Ral
Ral

Reputation: 390

I managed to find this 'workaround' which works, but I'm not too sure it's the best solution.

abstract class Car {
    public static function getParts() {
        $carModel = array_reverse(array_values(class_parents(static::class)))[1];
        $parts = [];
        $properties = (new ReflectionClass(new $carModel))->getProperties();
        foreach ($properties as $property) {
            $parts[] = $property->getName();
        }
        return $parts;
    }
}
class Ford extends car {
    public $wheels;
    public $body;
}
class FordTruck extends Ford {
    public $truckBed;
}

function getBaseParts($cartype) {
    return $cartype::getParts();
}

getBaseParts("FordTruck");

Upvotes: 0

Related Questions