Programmeren in PHP/Klassen
Basis
[bewerken]Introductie
[bewerken]Voorbeeld
[bewerken]Variabelen
[bewerken]Encapsulatie
[bewerken]Soms kan het voorkomen dat je niet wilt dat anderen zien wat jij aan het doen bent (of het is niet belangrijk), kortom je wilt je gegevens (methoden en variabelen) afschermen tegen ‘misbruik van buitenaf’. In PHP4 had je hier geen of bijna geen mogelijkheden voor. Met de komst van PHP5 is hier verandering in gekomen; er zijn wat nieuwe keywords ingevoerd. Het encapselen van data houdt in dat jij aangeeft welke variabele/methoden de gebruikers van de klasse wel of niet mogen aanroepen.
Public
[bewerken]Het public keyword zorgt er voor dat iedereen de methode of variabele kan aanroepen, het maakt niet uit of dat van binnen of buiten de class is, dit was het standaard gedrag van PHP4. Als je geen ‘scope’ opgeeft, is dit ook het standaard gedrag van PHP5; ik raad je echter aan om wel altijd een scope op te geven, ook als die public is omdat de code dan zelf de documentatie is. Een voorbeeldje van het public keyword;
PHP-code:
<?php
class foo
{
public $bar;
public function getBar ()
{
echo $this->bar;
}
}
$foo = new foo();
$foo->bar = "foobar";
$foo->getBar();
?>
Private
[bewerken]Als je wilt dat een variabele of methode alleen toegankelijk is binnen de class zelf gebruik je het private keyword. Het kan namelijk wel eens voorkomen dat je wilt bijhouden wat de status is van de class, zonder dat de gebruiker daar iets mee te maken heeft of zonder dat de gebruiker daar iets aan mag veranderen (een toegangsniveau op een bestandssysteem bijvoorbeeld, het zou fataal voor de beveiliging zijn als de gebruikers zomaar hun toegangsniveau aan konden passen). Het wordt aangeraden om al je class variabelen als private te declareren en er ‘toegangsfuncties’ voor te schrijven, deze functies zijn gekenmerkt door de prefix ‘get’ en ‘set’. Het maakt, bij het gebruik van dat soort functies namelijk niet uit of je een keer besluit om de naam van de variable te veranderen. Een voorbeeld volgt;
PHP-code:
<?php
class foo
{
private $bar;
public function getBar ()
{
echo $this->bar;
}
public function setBar ($bar)
{
$this->bar = $bar;
}
}
$foo = new foo();
//echo $foo->bar;
$foo->setBar ("foobar");
$foo->getBar ();
?>
Protected
[bewerken]Als een variable of method protected is, kan deze alleen gebruikt worden door classes die van de parent erven, of de class zelf.
PHP-code:
<?php
class foo
{
protected $bar;
}
class foobar extends foo
{
public function setBar ($bar)
{
$this->bar = $bar;
}
public function getBar ()
{
echo $this->bar;
}
}
$foobar = new foobar();
$foobar->setBar ("foobar");
$foobar->getBar ();
?>
Klasse constanten
[bewerken]Een constante is zoals de naam zegt, voor iedere klasse instantie hetzelfde. Het zijn dan ook geen variabelen want die kun je aanpassen en die waren te herkennen aan hun dollarteken. Voor de klasseconstanten heeft men een nieuwe operator toegevoegd om de waarde uit de klasse op te halen en een een nieuw keyword om een referentie naar zichzelf te krijgen (vanuit de klasse). Namelijk de '::' operator en het 'self' keyword. Het is een normale conventie om constanten met alleen maar hoofdletters te schrijven, maar anders mag natuurlijk ook. Je kunt maar een keer aan een constante een waarde toekennen, en dat is op het moment dat je deze definieert. Daarna blijft een constante zijn waarde behouden.
PHP-code:
<?php
class foo
{
const BAR = 12;
}
echo foo::BAR;
?>
Zoals je ziet wordt er geen instantie gemaakt van de klasse foo, maar wordt de klassenaam gebruikt, omdat het toch een constante is.
PHP-code:
<?php
class foo
{
const BAR = 13;
public function __construct()
{
echo self::BAR;
}
}
$foo = new foo();
?>
Om dus in de klasse zelf aan een referentie naar BAR te komen gebruiken we niet $this-> maar self:: omdat $this een variabele zou impliceren, net als ->. Terwijl het hier gaat om constanten.
Statische klasse variabelen
[bewerken]In PHP4 had je al statische variabelen, maar die golden alleen voor het bereik van de functie waarin ze gebruikt werden. In PHP5 kun je ook statische variabelen gebruiken die voor de klasse gedefinieerd zijn. Statische variabelen zijn eigenlijk een kruising tussen variabelen en klasse constanten, ze zijn voor iedere instantie gelijk maar je kunt er wel een nieuwe waarde aangeven (die waarde wordt dan overgenomen door alle andere instanties). Het wordt over het algemeen gezien als een slecht gebruik om statische variabelen te gebruiken. Een handige toepassing van statische variabelen is het singleton design pattern. (Wees hier echter voorzichtig mee!)
PHP-code:
<?php
class foo
{
public static $bar = 1;
}
$een = new foo();
$twee = new foo();
$een::bar++;
echo $twee::bar;
//Outputs: Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM
?>
Speciale functies
[bewerken]Constructor
[bewerken]Waar we in PHP4 de naam van de klasse gebruiken als constructor, is dat in PHP5 de speciale functie __construct geworden, deze verschilt verder niet van de constructor in PHP4 en wordt op de volgende manier gebruikt.
PHP-code:
<?php
class foo
{
function __construct ()
{
echo "Constructor aangeroepen";
}
}
$foo = new foo();
?>
Destructor
[bewerken]PHP5 heeft ook ondersteuning voor destructors gekregen, deze worden (in tegenstelling tot constructors) aangeroepen als een object van de class wordt ‘vernield’, meestal is dit aan het eind van de pagina, maar het kan ook na een unset () gebeuren. De naam van de destructor is __destruct.
PHP-code:
<?php
class foo
{
function __construct ()
{
echo "Constructor aangeroepen!";
}
function __destruct ()
{
echo "Destructor aangeroepen";
}
}
$foo = new foo();
?>
__get
[bewerken]Het komt steeds vaker voor dat je eigenschappen wel public wilt maken, maar niet wilt dat ze aangepast worden. Of dat de variabele eigenlijk in een array thuis hoort, maar je die implementatie voor de gebruiker van de class verborgen wilt houden Daarvoor is __get in het leven geroepen. In OOP heet zoiets een property en functioneel zijn ze hetzelfde als die in C#. Echter is de implementatie in PHP anders.
PHP-code:
<?php
class Foo
{
private $myBar;
public function __get ($Var)
{
$Value = "";
switch ($Var)
{
case 'Bar':
$Value = $this->myBar;
break;
}
return $Value;
}
public function __construct ()
{
$this->myBar = 'BarFoo';
}
}
$F = new Foo();
echo $F->Bar; //Uitvoer: 'BarFoo'
// $F->Bar = 'Bar'; // Will error!
?>
Het voordeel van __get (); is dat het voor de gebruiker duidelijk is dat hij een variabele gebruikt, omdat dat ook zo is. In tegenstelling tot Java, waar nogal eens methoden voorkomen als getBar() die totaal tegen de OOP principes ingaan. Zeker als je bar dan private maakt en het enige wat zo'n functie dan doet is `return bar;` tenzij je hetzelfde wilt gebruiken als hierboven. Anders is het een verkapte manier om variabelen publiek te maken.
__set
[bewerken]Als je een functie hebt om op te vangen wanneer een variabele wordt opgevraagd, heb je er natuurlijk ook een voor wanneer een variabele van waarde wijzigt. Daar heb je __set voor. Set heeft twee parameters, een met de naam van de variabele en een met de waarde. Een voordeel van het gebruik van __set is dat je invoer kunt valideren voordat je daadwerkelijk de variabele van waarde laat veranderen.
PHP-code: PHP.index
<?php
class Foo
{
private $myBar;
public function __set ($Var, $Value)
{
switch ($Var)
{
case 'Bar':
if (!$this->ValidateBar ($Value))
{
trigger_error ('Wrong value for Foo::$Bar', E_USER_ERROR);
}
else
{
$this->myBar = $Value;
}
break;
}
}
private function ValidateBar ($Int)
{
return ctype_digit ((string)$Int);
}
}
$F = new Foo();
$F->Bar = 89;//Gaat goed
$F->Bar = 'String';//Gaat fout, Foo::Bar moet een integer zijn
?>
__call
[bewerken]__call Werkt hetzelfde als __get maar dan voor methoden aanroepen. Je kunt hiermee method overloading (meerdere methoden met dezelfde naam maar andere argumenten in dezelfde class) simuleren, maar het wordt wel een gigantische troep als je dat gaat doen.
PHP-code:
<?php
class Foo
{
public function __call ($Method, $Arguments = array())
{
switch ($Method)
{
case 'Bar':
switch (count ($Arguments))
{
case 1:
$cMethod = 'Bar1';
break;
case 3:
$cMethod = 'Bar2';
break;
default:
trigger_error ("Call to undefined method
Foo::{$Method}() with " . count ($Arguments) . " arguments",E_USER_ERROR);
return false;
}
call_user_func_array (array($this, $cMethod), $Arguments);
break;
default:
trigger_error ("Call to undefined method
Foo::{$Method}()",E_USER_ERROR);
return false;
}
}
private function Bar1 ($Str)
{
echo "Foo::Bar1, argument: {$Str}<br/>";
}
private function Bar2 ($Str, $Int, $Double)
{
echo "Foo::Bar2, arguments: {$Str}, {$Int}, {$Double}<br/>";
}
}
$F = new Foo();
$F->Bar ('FooBar');
$F->Bar ('BarFoo', 12, 45.16);
$F->Bar (); //Geeft een foutmelding
?>
Let wel dat dit een extreem omslachtige manier is om te werken!
__toString
[bewerken]Omdat klassen eigenlijk uitgebreide gegevensstructuren zijn, net als bijvoorbeeld een integer maar dan uitgebreid, is het natuurlijk handig om een manier te hebben om de interne waarde van je klasse weer te geven, bij bijvoorbeeld een echo. Net als dat
PHP-code:
<?php
$myInt = 12;
echo $myInt;
?>
Gewoon `12` naar de uitvoer schrijft. Waarom zou een klasse dat niet mogen? Of kunnen? Daarvoor heeft PHP de methode __toString voor bedacht. Deze neemt geen argumenten, en moet een string returnen. Stel je hebt een klasse die de getallen 1 tot en met 10 moet uitbeelden. Dan krijg je zoiets;
PHP-code:
<?php
class SmallNumber
{
private $myNumber;
public function __set ($Number, $Value)
{
if ($Number == 'Number' && $this->ValidateNumber ($Value))
{
$this->myNumber = $Value;
}
}
public function __get ($Number)
{
if ($Number == 'Number')
{
return $this->myNumber;
}
}
private function ValidateNumber ($Number)
{
return ctype_digit ((string)$Number) && ($Number >= 1 && $Number <= 10);
}
public function __toString ()
{
switch ($this->myNumber)
{
case 1:
return 'een';
case 2:
return 'twee';
case 3:
return 'drie';
case 4:
return 'vier';
case 5:
return 'vijf';
case 6:
return 'zes';
case 7:
return 'zeven';
case 8:
return 'acht';
case 9:
return 'negen';
case 10:
return 'tien';
}
}
}
$Tien = new SmallNumber();
$Tien->Number = 10;
echo $Tien;
?>
Interfaces
[bewerken]Een interface legt de functies die gebruikt worden in de class vast, om zo polymorfie toe te passen. Polymorfie wil letterlijk zeggen dat een object in meerdere vormen voor kan komen. In een interface kan je geen variabele definiëren, omdat dat helemaal niet van belang is voor de eindgebruiker van de class. Net als dat de interface van een website alleen belangrijk is voor de eindgebruiker (wat maakt het hem/haar nou uit wat er achter de schermen gebeurt, als het maar werkt). Je kunt ook geen instance van een interface definiëren en als een class niet alle functies uit die interface implementeert wordt deze class een abstracte class. Doordat iedere class die de interface implementeert, de functies heeft die in de interface beschreven zijn, mag je er blind op vertrouwen dat die functies ook bestaan, wat soms (bij gegevensvalidatie bijvoorbeeld) enorm handig kan zijn. Een voorbeeld van 'polymorfie' met behulp van een interface.
PHP-code:
<?php
interface bar
{
function foo ();
}
class foobar implements bar
{
function foo ()
{
echo "foobar::foo aangeroepen!";
}
}
class barfoo implements bar
{
function foo()
{
echo "barfoo::foo aangeroepen!";
}
}
$_FOOS = array();
$_FOOS[‘foobar’] = new foobar();
$_FOOS[‘barfoo’] = new barfoo();
foreach ($_FOOS as $foo)
{
$foo->foo();
}
// Outputs: foobar::foo aangeroepen!barfoo::foo aangeroepen!
?>
Zoals je hier kan zien, bevat de array $_FOOS instantie van de classes foobar en barfoo, welke beide de interface bar implementeren (die de functie foo dicteert.) Je kunt er dus blind op vertrouwen dat ze allebei de functie foo bevatten.
Meerdere interfaces implementeren
[bewerken]Het is mogelijk om een class meerdere interfaces te laten implementeren. Het verschil met abstracte classes (hieronder besproken) is dat je wel meerdere interfaces kan implementeren in een class, maar dat je een class maar 1 abstracte class kan laten 'extenden'. Om meerdere interfaces te implementeren doe je zoiets:
PHP-code:
<?php
interface Validateable {
function validate();
}
interface Runnable {
function run();
}
class Foo implements Validateable, Runnable {
function run() {
echo "Runned!";
}
function validate() {
echo "Validated!";
}
}
?>
Abstracte classes
[bewerken]Hoewel interfaces je meer vrijheid geven in de implementatie daarvan, kan het wel eens voorkomen dat je ook wil dat er private functions worden gedeclareerd, die voldoen aan een eigen invulling, of dat je variabelen mee wil geven in je interface. Abstracte classes kunnen ook gewone methoden hebben, net als iedere andere klasse. Bij interfaces is dit allemaal niet zo. Een abstracte klasse bevat sowieso een abstracte method. Deze abstracte method geef je aan door ‘abstract’ voor de functie definitie te zetten. Je kan geen instantie maken van een abstracte klasse, of een klasse die niet alle abstracte methods van een klasse invult.
Kort samengevat is een abstract class dus een class waarvan een of meerdere functies niet zijn geïmplementeerd (ze hebben geen code), maar wel zijn gedeclareerd (de functie staat er wel). Een abstract class kun je daardoor niet los instantiëren, maar je kunt wel een andere class ervan 'afleiden' door de abstracte class te extenden.
PHP-code:
<?php
abstract class voertuig {
abstract public function rijden();
public function foo() {
echo "Foo";
}
}
class auto extends voertuig {
public function rijden() {
echo "over de snelweg";
}
}
/* Dit werkt wel */
$auto = new auto();
$auto->rijden();
$auto->foo();
/* Dit werkt niet! Je kunt geen abstracte class instantiëren! */
$voertuig = new voertuig();
$voertuig->rijden();
$voertuig->foo();
?>
Type hints
[bewerken]Het kan natuurlijk wel eens voorkomen dat je wilt dat je functie alleen een soort class of alleen classes met dezelfde interface in een functie wilt gebruiken, om bijvoorbeeld polymorfie toe te passen. Dit kan in PHP5 met type hints. Deze hints geven aan welk type class er gegeven moet worden als argument.
PHP-code:
<?php
interface bar
{
public function bar();
}
class foo implements bar
{
public function bar ()
{
echo "Bar";
}
}
function FooBar (bar $foo) //Neemt dus alleen classes van het type 'bar'
{
$foo->bar();
}
$foo = new foo();
FooBar ($foo);
?>
Je kunt als hint zowel de naam van een class zelf als de naam van een interface opgeven.