PHP: How far can one extend classes? [duplicate] - php

I'm trying to work out exactly how deep inheritance goes.
For example if I start with class A
Class B extends class A
Class C extends class B
Class D extends class C
Class E extends class D
Class F extends class E
Class G extends class F
most of the lower levels are abstract classes with methods filled with code and abstract methods. its a rather large and complex structure. (doing my head in)
Will Class G still be able to access Class A methods and parameters, as well as still have access to the abstract methods from Class A ?
I'm asking because I have been having trouble with eclipse not adding the lower class methods into the auto complete. I would like to know if there is actually a limit to this or whether it's just eclipse reaching its maximum for code completion.
I'm far from a stage where I can test this with what I have done and don't want to find when I'm done that methods defined lower in the order are not accessible.

Sounds like a problem with your editor. For example, see the following quick-and-dirty demonstration:
php > class a { function foo() { echo "A::foo()\n"; } }
php > $letters = range('a', 'z');
php > for ($i = 0; $i < 25; $i++) { eval("class {$letters[$i+1]} extends {$letters[$i]} {}"); }
php > $z = new z;
php > $z->foo();
A::foo()
PHP doesn't impose any kind of restriction like this on you.

Using a canonical test like this:
<?php
abstract class A {
abstract function getValue();
}
abstract class B extends A{ }
abstract class C extends B{ }
abstract class D extends C{ }
abstract class E extends D{ }
abstract class F extends E{ }
class G extends F{
}
?>
one would expect a fatal error in that G does not in fact implement the abstract method defined in A. As you can see from the link above, this is in fact the case.
Thus, while this is a deep inheritance, it is beneath whatever limit (if any) PHP has. Rather, you're likely running into an indexer issue in the PDT.

The following php:
class A
{
public function FooA()
{
echo "A!!";
}
}
class B extends A
{
public function FooB()
{
echo "B!!";
}
}
class C extends B
{
public function FooC()
{
parent::FooB();
parent::FooA();
}
}
$test = new C();
$test->FooC();
prints:
B!!A!!
I tested this at a depth of 50 and it still functioned just fine, so you definitely can, sounds like your editor plug-in only looks so deep in the inheritance tree

Related

How do you keep track where an inherited method comes from

Considering this simple example of classes:
// father.php
class father {
public function wallet () {}
}
// mother.php
class mother extends father { }
// daughter.php
class daughter extends mother {
public function shop {
$this->wallet();
}
}
Is there a convention/coding practice that tells you quickly that wallet() comes from class father?
This isn't a big deal but when reviewing old code (without an IDE) it's nice to know quickly where inherited methods come from.
Though seemingly trivial, one has to loop over sequence of parent's classes:
$seekMethod = 'wallet';
$stack = [];
foreach(class_parents('daughter') as $parent){
foreach((new ReflectionClass($parent))->getMethods() as $method){
if($seekMethod == $method->name){
$stack[] = "{$method->class}::{$method->name}() in {$parent}";
echo "Found ".end($stack);
}
}
}
echo "{$seekMethod} is defined in ".end($stack);
Will output something like:
Found father::wallet() in mother
Found father::wallet() in father
wallet is defined in father::wallet() in father
Or simpler, without respecting inheritance:
foreach((new ReflectionClass('daughter'))->getMethods() as $method){
if($seekMethod == $method->name){
echo "Found {$method->class}::{$method->name}";
}
}
Will output e.g.
Found father::wallet
Inheritance isn't the easiest to follow without an IDE (or a lot of documentation).
PHPDoc does have the #see tag which you could use as:
/**
* #see father::wallet() For logic pertaining to wallet retrieval
*/
public function shop {
$this->wallet();
}
Beyond that, you want to make sure your inheritance makes sense. I'm sure this was just a quick example, but I don't see any reason why a daughter would ever extend a father.
A daughter could extend a Child, and in the Child, you could have the logic pertaining to pulling the wallet from a parent (or father).

PHP get_called_class() as variable for referencing static property

I'm trying every variation of the following to refer to a static property:
get_called_class()::$$prop
I've tried this:
${get_called_class()}::$$prop
I've tried many things, but can't seem to get it.
I know I can just do this:
$className = get_called_class();
$className::$$prop
BUT, that means an extra line of code. Surely there must be a way for the language to make this work on the same line. Anyone have a solution?
(By the way, the static property is protected, so it fails with ReflectionClass::getStaticPropertyValue.)
Without understanding any additional context here, you don't need to actually invoke get_called_class to poke at LSB-resolved static properties. Instead, use the static keyword to automagically resolve the currently called static class name.
class A {
static $foo = 'from a';
public static function test($property) {
echo static::$$property, "\n";
}
}
class B extends A { static $foo = 'from b'; }
class C extends A { static $foo = 'from c'; }
Example from the PHP interactive prompt:
php > include '/tmp/get_called_class.php';
php > A::test('foo');
from a
php > B::test('foo');
from b
php > C::test('foo');
from c
php >

passing variable to a class

This is the scenario:
There are 3 classes: class A, class B , class C
//A.php
class A {
public $something1;
public $something2;
//long list....
// i am the base class
}
//B.php
class B extends A {
// i am a child of A
}
//C.php
class C extends A{
// i do the side work
}
The program makes an object and sets values for class B like this:
$b = new B();
$b->something1 = "XYZ";
$b->something2 = "ABC";
...... long list.....
Now what I want is:
I want to call some functions of class C in class B.
I know I can do that by making an object in class B like this $c = new C();.
But I also want to pass all the variables of B i.e. (something1, something2 ....)
passed by the program to class C
I know I can do this like this:
$c->something1 = $this->something1;
$c->something2 = $this->something2 .....
But is it possible to do that without the above code because
$c->somethingN will just take too many lines and i do not like repetitive code.
I just want to know if there is any other way.
I know i can do that by making an object in class B like this $c = new C();.
If this are instance members (not static), this is the way to go.
Another way would be to use static members, like this:
class A {
public static $foo;
}
class B extends A {}
class C extends A {}
# usage:
B::$foo = 'bar';
echo C::$foo; // will output 'bar'

How do you properly extend PHP classes that use other extended classes without strict warnings

Imagine i have 2 classes (i've simplified the logic here):
class Table {
public function addRow (Row $row){
$this->row = $row;
}
// lots of code
}
class Row {
// lots of code
}
And i want to extend the table class to do something similar so i create 2 new classes:
class SpecialTable extends Table{
public function addRow (SpecialRow $row){
parent::addRow($row);
}
// lots of code
}
class SpecialRow extends Row{
// lots of code
}
When i try to add a SpecialRow to a SpecialTable object i get a warning similar to:
PHP Strict standards: Declaration of SpecialTable::addRow() should be compatible with that of Table::addRow() in /SpecialTable.php on line XX
Can someone help me here? Is this bad practice and i should code it differently? Or is it just a warning that i should ignore?
Many thanks for any suggestions / guidance.
You should probably use a name like SpecialTable::addSpecialRow() if you're changing the argument type - otherwise SpecialTable doesn't actually extend Table, it overloads it (which isn't supported in PHP).
Based on your simplified example, it should be public function addRow(Row $row) since you just call the parent. Depending on what you're actually doing to $row in that function, you could type-hint Row and check whether it's a SpecialRow in code, or just use Row if you don't need its Specialness.
Make sure SpecialRow extends from Row:
class SpecialRow extends Row {
// lots of code
}
And that SpecialTable extends from Table:
class SpecialTable extends Table {
// lots of code
}
If Row and SpecialRow aren't directly related (where SpecialRow is a specialized Row and therefore should extend Row), you could use an interface. PHP doesn't support method overloading without some crazy ugliness, so an alternative might be something like this:
Interface:
interface IRow
{
// interface body
}
Row classes:
class Row implements IRow
{
}
class SpecialRow implements IRow
{
}
Table classes:
class Table
{
public function addRow(IRow $row)
{
$this->row = $row;
}
}
class SpecialTable
{
public function addRow(IRow $row)
{
$this->row = $row;
}
}
Implementation:
$t = new Table();
$st = new SpecialTable();
$r = new Row();
$sr = new SpecialRow();
$t->addRow($r);
$st->addRow($sr);
http://ideone.com/TsVw5
Since it doesn't follow PHP's standards I'd call it bad practice. The point is that a derivative class should always work like its ancestor. So classes expecting a Row argument can handle a SpecialRow the same way. You're extending the class, not overloading it. I'd suggest adding another method for this purpose.

Get class name from extended class

Is it possible to get the name of the top level class from an extended class, without setting it from the top level class. See example below, I would like to get 'Foo' from Base. I know I could set a variable from Foo, but hoping to skip the extra step.
Thanks.
class Base {
function __construct() {
echo '<p>get_class: '.get_class().'</p>';
echo '<p>__CLASS__: '.__CLASS__.'</p>';
}
}
class Foo extends Base {
}
$test = new Foo();
(PHP 5.2.4+)
Use:
get_class($this);
get_called_class() for static classes or get_class($this) for instantiated.
get_called_class(), as Jason said, was introduced in PHP 5.3
You can simply use:
get_class($this);

Categories