Vererbung und Sichtbarkeit von Variablen in Klassen

Über die Sichtbarkeit von Variablen innerhalb einer Klasse, gibt es in PHP.net ein paar Erklärung, die aber :

  1. nicht 100% erklären, was da passiert
  2. nicht 100% richtig sind
  3. nur einen kleinen Teil von dem erklären was da möglich ist.

Die Sichtbarkeit einer Eigenschaft oder Methode kann definiert werden, indem man der Deklaration eines der Schlüsselwörter public,protected oder private. voranstellt. Auf public deklarierte Elemente kann von überall her zugegriffen werden. Protected beschränkt den Zugang auf Elternklassen und abgeleitete Klassen (sowie die Klasse, die das Element definiert). Private grenzt die Sichtbarkeit einzig auf die Klasse ein, die das Element definiert.

Leider ist das, neben ein paar Quellcodes, auch das einzige was man so erfahren kann. Darum gibt es hier nun ein kleines Tutorial, was man mit Vererbung und Sichtbarkeit alles so machen kann.

Methoden und Variablen des Types "Public"

„Public“ ist die einfachste Deklaration. Alles was Public definiert wurde, kann von außerhalb der Klasse aufgerufen werden.

Als kleines Beispiel, ist hier die Klasse Red mit einer Methode und Variable, die jeweils Public definiert wurden. Nach dem Erzeugen der Klasse, kann ich auf die Variable und auf die Funktion außerhalb der Klasse zugreifen.

class Red
{
	public  $public = 'Red';
	public function getthisRedvars ()
	{
		echo $this->public;
	}
}

$red = new Red();
$red->getthisRedvars();
echo $red->public;
Ausgabe
Red
Red

Gleiches gilt, wenn die Klasse erweitere. Auch hier habe ich vollen Zugriff auf die Methoden und Variablen und kann sie nach belieben überschreiben.

class Blue extends Red
{
public $public = 'Blue';
}
$blue = new Blue();
$blue->getthisRedvars();
echo $blue->public;
Ausgabe
Blue
Blue

Methoden und Variablen des Types "Protected"

„Protected“ steht, wie der Name schon sagt, für geschützt. Anders als bei Public, lassen sich Methoden und Variablen nicht von außerhalb der Klasse aufrufen. Versucht man es trotzdem erhält man einen Fatal Error.

Diese Deklaration wird verwendet, um Variablen/Methoden außerhalb der deklarierenden Klasse ($this) erweiterbar/änderbar zu machen.

In meinem Beispiel wird die Klasse Red von Blue erweitert und im __construct() wird die Farbe definiert. Ohne __construct() in beiden Klassen, kann man die Farbe auch direkt in der Variable definieren.

Würde man nur in Blue auf __construct() verzichten und die Farbe in der Klasse Blue über protected $protected = ‚Blue‘; definiere, würde trotzdem Red rauskommen, da PHP die Klassen nach __construct() – Methoden durchsucht und diese ausführt, wenn eine Klasse initialisiert wird.

Für diese Fälle gibt es aber keine Beispiele, da es den Rahmen des Beitrags sprengen würde.

class Red
{
	protected $protected;
	public function __construct()
	{
		$this->protected = 'Red';
	}
	protected function getthisvars ()
	{
		echo $this->protected;
	}
	public function getVars ()
	{
		$this->getThisVars();
	}
}

$red = new Red();
/**
* $red->getthisvars();
* Fatal error:  Call to protected method Red::getthisvars() from context ''
*/
/**
* echo $red->protected;
* Fatal error:  Cannot access protected property Red::$protected
*/
$red->getVars();
class Blue extends Red
{
	public function __construct()
	{
		$this->protected = 'Blue';
	}

}
$blue = new Blue();
/**
* $blue->getthisvars();
* Fatal error:  Call to protected method Red::getthisvars() from context ''
*/
/**
* echo $blue->protected;
* Fatal error:  Cannot access protected property Red::$protected
*/
$blue->getVars();
Ausgabe
Red
Blue

Methoden und Variablen des Types "Private"

„Private“ ist „Protected“ auf hohem Niveau.  Methoden und Variablen lassen nicht von außerhalb der Klasse aufrufen. Versucht man es trotzdem erhält man einen Fatal Error.

Zusätzlich lassen sich die Methoden und Variablen auch nur in der Klasse aufrufen, in der Sie deklariert werden. Das bedeutet, würde ich in Blue versuchen auf eine Methode von Red zuzugreifen, die als Private deklariert ist, erhalte ich entsprechend einen „Fatal Error“.
Versuche ich jedoch auf eine Variable von Red zuzugreifen, erhalte ich ein „Notice: Undefined property“, da die Variable im eigentlichen Scope, nämlich Blue, nicht existiert.

Zusatz Nummer 2: Definiere ich in beiden Klassen die gleiche Variable als Private, aber mit unterschiedlichen Werten und versuche dann diese über eine Funktion in der Elternklasse (Red) aufzurufen, erhalte ich den Wert der Variable aus Red. Rufe ich hingegen die Variable über eine Funktion aus Blue heraus auf, erhalte ich den Wert aus dieser Klasse.

class Red
{
	private $private = 'Red';
	private $privateRed = 'Red';
	private function getthisvars ()
	{
		echo $this->private;
		echo $this->privateRed;
	}
	private function getthisvars2 ()
	{
		echo $this->private;
		echo $this->privateRed;
	}
	public function getVars ()
	{
		$this->getThisVars();
	}
	public function callgetthisvarsViaRed ()
	{
		$this->getThisVars();
	}
}

$red = new Red();
$red->getVars();
/**
* $red->getThisVars();
* Fatal error: Call to private method Red::getThisVars() from context ''
*/
/**
* $red->getThisVars2();
* Fatal error: Call to private method Red::getThisVars2() from context ''
*/

class Blue extends Red
{
	private $private = 'Blue';
	private $privateBlue = 'Blue';
	private function getthisvars ()
	{
		echo $this->private;
		echo $this->privateBlue;
		/**
		* echo $this->privateRed;
		* Notice: Undefined property: Blue::$privateRed
		*/

	}
	public function getVars ()
	{
		$this->getThisVars();
	}
	public function getVars2 ()
	{
		// only defined in RED
		$this->getThisVars2();
	}
	public function callParentgetthisvars ()
	{
		parent::getThisVars();
	}

}
$blue = new Blue();
$blue->getVars();
/**
* $blue->getVars2(); 
* Fatal error: Call to private method Red::getThisVars2() from context 'Blue'
*/
$blue->callgetthisvarsViaRed();
/**
* $blue->callParentgetthisvars();
* Fatal error: Call to private method Red::getThisVars() from context 'Blue'
*/
Ausgabe
Red
Red
Blue
Blue
Red
Red

Was bedeutet das nun?

Nun, grundsätzlich muss man sich die Frage stellen, was will ich mit meinem Klassen-Aufbau erreichen.

Will man eine Klasse bauen, die eine InfoBox aufbaut und deren Erweiterung durch eine Zusatzklasse lediglich den Type und das Icon ändert, so darf die Variable $type und $icon maximal protected sein, wenn man nur über eine Funktion der Infobox auf diese Variablen zugreift.

Hierzu habe ich einmal drei Beispiele erzeugt, wo jedoch lediglich die „Protect“-Methode wirklich brauchbar ist, da ich hier vermeide, dass die Variablen überschrieben werden können! Dies ist besonders dann wichtig, wenn man mit Singleton-Klassen arbeitet und fremde Entwickler „dirty“ arbeiten.

Auf die „Private“ – Methode kann man verzichten, da man hier Code mehrfach schreiben müsste. Das passt natürlich nicht zu „Write less, do more“.

class infoBox
{
	public $type = 'info';
	public $icon = 'info.gif';

	public function getIcon()
	{
		return $this->icon;
	}
	public function getType()
	{
		return $this->type;
	}
}

class errorBox extends infoBox
{
	public $icon = 'error.gif';
	public $type = 'error';
}
class successBox extends infoBox
{
	public $icon = 'success.gif';
	public $type = 'success';
}
$box = new errorBox();
echo $box->getType(); // 'error';
$box->type = 'schmarrn';
echo $box->getType(); // 'schmarrn';
$box = new successBox();
echo $box->getType(); // 'success';
$box->type = 'schmarrn';
echo $box->getType(); // 'schmarrn';
class infoBox
{
	private $type = 'info';
	private $icon = 'info.gif';

	public function getIcon()
	{
		return $this->icon;
	}
	public function getType()
	{
		return $this->type;
	}
}

class errorBox extends infoBox
{
	private $icon = 'error.gif';
	private $type = 'error';
}
class successBox extends infoBox
{
	private $icon = 'success.gif';
	private $type = 'success';
	public function getIcon()
	{
		return $this->icon;
	}
	public function getType()
	{
		return $this->type;
	}
}

$box = new errorBox();
echo $box->getType(); // 'info';
$box = new successBox();
echo $box->getType(); // 'success';
class infoBox
{
	protected $type = 'info';
	protected $icon = 'info.gif';

	public function getIcon()
	{
		return $this->icon;
	}
	public function getType()
	{
		return $this->type;
	}
}

class errorBox extends infoBox
{
	protected $icon = 'error.gif';
	protected $type = 'error';
}
class successBox extends infoBox
{
	protected $icon = 'success.gif';
	protected $type = 'success';
}

$box = new errorBox();
echo $box->getType(); // 'error';
$box = new successBox();
echo $box->getType(); // 'success';

Wer nun noch etwas tiefer in das Thema eintauchen will, kann sich in meinem Git-Repo die Datei unter VererbungUndSichtbarkeit anschauen. Darin befindet sich eine PHP-Datei, die einige Vorfälle simuliert und farbig darstellt.

Gleichzeitig wird das verhalten von get_called_class und get_class(), sowie get_class($this) dargestellt.