Objekt Orienteret Programming (OOP) i PHP










OOP står for Objekt Orienteret Programmering, og navnet er nogenlunde selvsigende; det er programmering, der gør brug af objekter. I denne tutorial lærer du om de forskellige udtryk, samt hvordan de bruges.
#1
Introduktion, instanser/objekter
OOP står for Objekt Orienteret Programmering, og navnet er nogenlunde selvsigende; det er programmering, der gør brug af objekter. Et objekt er en instans af en klasse. Du kan se det som om, at instansen af en klasse er ligesom en elev i en klasse. En elev i en klasse har alt, hvad klassen indeholder, til sin rådighed. Det kan være blyanter, linealer, viskelædre, og det kan være funktioner, variabler, konstanter mm.. Siden PHP4 har man kunnet oprette en klasse ved brug af nøgleordet class. Syntaksen ved oprettelsen af en klasse er lig syntaksen ved oprettelsen af en funktion, bortset fra parametrene.
De fire linjer opretter en tom klasse ved navn Klasse1. I PHP kan en klasse groft sagt indeholde 4 ting: Funktioner, variabler, konstanter og en særlig type funktioner, der kaldes når en instans af klassen oprettes og når en instans af klassen ’dræbes’ (hvilket sker automatisk når instansen af klassen ikke længere bruges eller PHP når slutningen af koden.) Vi opretter en instans af klassen således:
Man vil nu kunne gøre brug af alle Klasse1’s funktioner, variabler, konstanter og særlige funktioner gennem $instans, som er en ganske normal variabel. Det vil sige, det kan vi faktisk ikke, da vi ikke har puttet noget i Klasse1.
Nu indeholder Klasse1 funktionen write, og vi kan benytte den gennem $instans således:
OOP står for Objekt Orienteret Programmering, og navnet er nogenlunde selvsigende; det er programmering, der gør brug af objekter. Et objekt er en instans af en klasse. Du kan se det som om, at instansen af en klasse er ligesom en elev i en klasse. En elev i en klasse har alt, hvad klassen indeholder, til sin rådighed. Det kan være blyanter, linealer, viskelædre, og det kan være funktioner, variabler, konstanter mm.. Siden PHP4 har man kunnet oprette en klasse ved brug af nøgleordet class. Syntaksen ved oprettelsen af en klasse er lig syntaksen ved oprettelsen af en funktion, bortset fra parametrene.
class Klasse1
{
}
{
}
De fire linjer opretter en tom klasse ved navn Klasse1. I PHP kan en klasse groft sagt indeholde 4 ting: Funktioner, variabler, konstanter og en særlig type funktioner, der kaldes når en instans af klassen oprettes og når en instans af klassen ’dræbes’ (hvilket sker automatisk når instansen af klassen ikke længere bruges eller PHP når slutningen af koden.) Vi opretter en instans af klassen således:
$instans = new Klasse1;
Man vil nu kunne gøre brug af alle Klasse1’s funktioner, variabler, konstanter og særlige funktioner gennem $instans, som er en ganske normal variabel. Det vil sige, det kan vi faktisk ikke, da vi ikke har puttet noget i Klasse1.
class Klasse1
{
function write($text)
{
echo $text;
}
}
{
function write($text)
{
echo $text;
}
}
Nu indeholder Klasse1 funktionen write, og vi kan benytte den gennem $instans således:
$instans->write(”Hello World!”);
(klik for zoom)
#1
#2
Public & Private medlemmer
En klasse kan forhindre brugen af funktioner udefra. Det kan eksempelvis være, at klassen skal indeholde en ’skjult’ funktion, der kun kan bruges af klassen selv. Det er her, ordene public, private og protected kommer ind i billedet. Som standard er et medlem af en klasse (en funktion, en variable etc.) public, hvilket betyder, at den kan bruges inde i klassen, udenfor klassen ($instans->medlem()) og i en anden klasse (dette kommer jeg ind på senere, det er et relativt stort emne). Hvis et medlem er private, kan medlemmet kun bruges af medlemmer af klassen selv. Det vil for eksempel sige, at hvis klassen indeholde en private variabel, vil kun en funktion fra samme klasse kunne bruge variablen. Man kan vælge et medlems ’protection level’ (hvorvidt den er tilgængelig for andre) således:
Udover private og public blev et nyt OOP-element introduceret, nemlig $this. $this er en instans – ligesom $instans1 – men dog lidt anderledes. Den er nemlig altid en instans af den klasse, man opererer i. I ovenstående eksempel vil $this altså være en instans af Klasse1. Hvis vi oprettede en ny klasse, ville $this være en instans af den klasse. Det er måske lidt svært at forstå, men det kan følgende eksempel måske hjælpe på:
En klasse kan forhindre brugen af funktioner udefra. Det kan eksempelvis være, at klassen skal indeholde en ’skjult’ funktion, der kun kan bruges af klassen selv. Det er her, ordene public, private og protected kommer ind i billedet. Som standard er et medlem af en klasse (en funktion, en variable etc.) public, hvilket betyder, at den kan bruges inde i klassen, udenfor klassen ($instans->medlem()) og i en anden klasse (dette kommer jeg ind på senere, det er et relativt stort emne). Hvis et medlem er private, kan medlemmet kun bruges af medlemmer af klassen selv. Det vil for eksempel sige, at hvis klassen indeholde en private variabel, vil kun en funktion fra samme klasse kunne bruge variablen. Man kan vælge et medlems ’protection level’ (hvorvidt den er tilgængelig for andre) således:
class Klasse1
{
private $variabel1 = ”Hello World!”;
public function write()
{
echo $this->variabel1;
}
}
$instans1 = new Klasse1;
// Forkert:
echo $instans1->variabel1;
// Rigtigt:
$instans1->write();
{
private $variabel1 = ”Hello World!”;
public function write()
{
echo $this->variabel1;
}
}
$instans1 = new Klasse1;
// Forkert:
echo $instans1->variabel1;
// Rigtigt:
$instans1->write();
Udover private og public blev et nyt OOP-element introduceret, nemlig $this. $this er en instans – ligesom $instans1 – men dog lidt anderledes. Den er nemlig altid en instans af den klasse, man opererer i. I ovenstående eksempel vil $this altså være en instans af Klasse1. Hvis vi oprettede en ny klasse, ville $this være en instans af den klasse. Det er måske lidt svært at forstå, men det kan følgende eksempel måske hjælpe på:
class Klasse1
{
public function add10($number)
{
return $number + 10;
}
}
class Klasse2
{
public function add20($number)
{
$klasse1_instans = new Klasse1;
// Kald Klasse1’s add10 metode og læg yderligere 10 til
$number = $klasse1_instans->add10($number) + 10;
return $number;
}
}
$klasse2_instans = new Klasse2;
echo $klasse2_instans->add20(50); // Resultat: 70
{
public function add10($number)
{
return $number + 10;
}
}
class Klasse2
{
public function add20($number)
{
$klasse1_instans = new Klasse1;
// Kald Klasse1’s add10 metode og læg yderligere 10 til
$number = $klasse1_instans->add10($number) + 10;
return $number;
}
}
$klasse2_instans = new Klasse2;
echo $klasse2_instans->add20(50); // Resultat: 70
(klik for zoom)
#2
#3
Statiske medlemmer
En funktion i en klasse kaldes en metode. Metoder kan være statiske og ... ustatiske? En statisk metode er en funktion, der kan benyttes uden en instans af en klasse. Problemet ved dette er, at en statisk metode kun kan gøre brug af andre statiske medlemmer. Eksempel:
Vi kan altså konkludere, at...
- Statiske medlemmer tilgås via operatoren :: (scope operator)
- Ikke-statiske medlemmer tilgås via operatoren -> (pointer-to-object operator)
- self og $this er de henholdsvis statiske og ikke statiske instanser af den klasse, vi befinder os i.
Når man bruger $this til at fange en instans af den klasse, man befinder sig i, fjernes dollartegnet ($) fra den variabel, man vil bruge. Man kan sige, at dollartegnet bliver skubbet hen foran this, og at kommandoen skal læses således: $(this->)variabel1. Men i virkeligheden skyldes det, at hvis man skrev $this->$variabel1, ville den bruge indholdet af $variabel1 til at finde en variabel. Lidt forvirrende, men kig på dette eksempel:
En funktion i en klasse kaldes en metode. Metoder kan være statiske og ... ustatiske? En statisk metode er en funktion, der kan benyttes uden en instans af en klasse. Problemet ved dette er, at en statisk metode kun kan gøre brug af andre statiske medlemmer. Eksempel:
class Klasse1
{
public static $variabel1 = “Hello World!”;
public $variabel2 = “Hello Moon!”;
public static function write($text)
{
echo $text;
}
}
$instans1 = new Klasse1;
// Forkert
echo $instans1->variabel1; // variabel1 er statisk
echo Klasse1::write(Klasse1::variabel2); // variabel2 er ikke statisk
// Rigtigt
echo $instans1->variabel2;
Klasse1::write(Klasse1::$variabel2);
// Forkert (inde i write metoden)
echo self::variabel2; // variabel2 er ikke statisk
echo $this->variabel1; // variabel1 er statisk
// Rigtigt (stadig i write metoden)
echo self::variabel1; // variabel1 er statisk
echo $this->variabel2; // variabel2 er ikke statisk
{
public static $variabel1 = “Hello World!”;
public $variabel2 = “Hello Moon!”;
public static function write($text)
{
echo $text;
}
}
$instans1 = new Klasse1;
// Forkert
echo $instans1->variabel1; // variabel1 er statisk
echo Klasse1::write(Klasse1::variabel2); // variabel2 er ikke statisk
// Rigtigt
echo $instans1->variabel2;
Klasse1::write(Klasse1::$variabel2);
// Forkert (inde i write metoden)
echo self::variabel2; // variabel2 er ikke statisk
echo $this->variabel1; // variabel1 er statisk
// Rigtigt (stadig i write metoden)
echo self::variabel1; // variabel1 er statisk
echo $this->variabel2; // variabel2 er ikke statisk
Vi kan altså konkludere, at...
- Statiske medlemmer tilgås via operatoren :: (scope operator)
- Ikke-statiske medlemmer tilgås via operatoren -> (pointer-to-object operator)
- self og $this er de henholdsvis statiske og ikke statiske instanser af den klasse, vi befinder os i.
Når man bruger $this til at fange en instans af den klasse, man befinder sig i, fjernes dollartegnet ($) fra den variabel, man vil bruge. Man kan sige, at dollartegnet bliver skubbet hen foran this, og at kommandoen skal læses således: $(this->)variabel1. Men i virkeligheden skyldes det, at hvis man skrev $this->$variabel1, ville den bruge indholdet af $variabel1 til at finde en variabel. Lidt forvirrende, men kig på dette eksempel:
class Klasse1
{
private $hw = ”Hello World!”;
public function ShowVariableContent($variable)
{
echo $this->$variable;
}
}
$instans1 = new Klasse1;
$instans1->ShowVariableContent(“variabel1”); // Ugyldigt, Klasse1 har ingen variabel ved navn variabel1.
$instans1->ShowVariableContent(”hw”); // Gyldigt, vil udskrive ”Hello World!”, da det er hvad $hw indeholder.
{
private $hw = ”Hello World!”;
public function ShowVariableContent($variable)
{
echo $this->$variable;
}
}
$instans1 = new Klasse1;
$instans1->ShowVariableContent(“variabel1”); // Ugyldigt, Klasse1 har ingen variabel ved navn variabel1.
$instans1->ShowVariableContent(”hw”); // Gyldigt, vil udskrive ”Hello World!”, da det er hvad $hw indeholder.
(klik for zoom)
#3
#4
Abstrakte klasser
En abstrakt klasse er en klasse, der udelukkende indeholder statiske medlemmer, det vil sige alle funktioner og variabler skal skrives med nøgleordet ’static’. For at sikre sig at dette overholdes, kan man skrive ’abstract’ foran ’class’, altså:
Det betyder altså også, at man aldrig bruger -> operatoren i en abstrakt klasse, men kun :: til at tilgå medlemmer.
En abstrakt klasse er en klasse, der udelukkende indeholder statiske medlemmer, det vil sige alle funktioner og variabler skal skrives med nøgleordet ’static’. For at sikre sig at dette overholdes, kan man skrive ’abstract’ foran ’class’, altså:
abstract class Klasse1
{
public static $variabel1; // Rigtigt, variabel1 er statisk
public $variabel2; // Forkert, abstrakte klasser må kun indeholde statiske medlemmer
}
{
public static $variabel1; // Rigtigt, variabel1 er statisk
public $variabel2; // Forkert, abstrakte klasser må kun indeholde statiske medlemmer
}
Det betyder altså også, at man aldrig bruger -> operatoren i en abstrakt klasse, men kun :: til at tilgå medlemmer.
#4
#5
Interfaces
Et interface er en prototype af en klasse, der indeholder alle klassens medlemmer, men ikke koden bag medlemmerne. Det vil sige, at interfacet indeholder navnet på metoden, men ikke de linjer, metoden udfører. Det kan bruges til at sikre sig, at en klasse indeholder visse forudbestemte medlemmer, og det kan bruges som en letoverskuelig liste, der viser klassens indhold. Når en klasse optager 400 linjer, kan det være svært at holde rede i, hvilke medlemmer, klassen indeholder. Et eksempel:
Der er to dumme regler ved interfaces:
- Ingen variabler eller konstanter i selve interfacet, men gerne i klassen
- Medlemmets protection level (fx public eller protected) udelades i interfacet.
Når vi har opretter interfacet, giver vi det til en klasse ved at skrive implements efterfulgt af interfacets navn.
Et interface er en prototype af en klasse, der indeholder alle klassens medlemmer, men ikke koden bag medlemmerne. Det vil sige, at interfacet indeholder navnet på metoden, men ikke de linjer, metoden udfører. Det kan bruges til at sikre sig, at en klasse indeholder visse forudbestemte medlemmer, og det kan bruges som en letoverskuelig liste, der viser klassens indhold. Når en klasse optager 400 linjer, kan det være svært at holde rede i, hvilke medlemmer, klassen indeholder. Et eksempel:
interface Interface1
{
function metode1($text);
}
class Klasse1 implements Interface1
{
public static $variabel1;
public function metode1($text)
{
echo $text;
}
}
$instans1 = new Klasse1;
$instans1->metode1("Hello World!");
{
function metode1($text);
}
class Klasse1 implements Interface1
{
public static $variabel1;
public function metode1($text)
{
echo $text;
}
}
$instans1 = new Klasse1;
$instans1->metode1("Hello World!");
Der er to dumme regler ved interfaces:
- Ingen variabler eller konstanter i selve interfacet, men gerne i klassen
- Medlemmets protection level (fx public eller protected) udelades i interfacet.
Når vi har opretter interfacet, giver vi det til en klasse ved at skrive implements efterfulgt af interfacets navn.
(klik for zoom)
#5
#6
Nedarvning & protected medlemmer
Indtil videre har vi kun set på to af de tre protection levels. Det 3. protection level hedder protected. Et medlem med nøgleordet protected, kan tilgås to steder: Indeni selve klassen og i en anden klasse, der nedarver den første klasses medlemmer. Når en klasse nedarver en anden klasser, får den præcis de samme egenskaber, som den første klasse. Hvis en klasse for eksempel nedarver fra en klasse, der har en variabel $variabel1, vil den nedarvende klasse også have den variabel. Eksempel:
En klasse nedarver med nøgleordet extends efterfulgt af den klasse, den skal nedarve fra.
En nedarvet klasse arver også medlemmer med nøgleordet public. Kun private nedarver den ikke.
Indtil videre har vi kun set på to af de tre protection levels. Det 3. protection level hedder protected. Et medlem med nøgleordet protected, kan tilgås to steder: Indeni selve klassen og i en anden klasse, der nedarver den første klasses medlemmer. Når en klasse nedarver en anden klasser, får den præcis de samme egenskaber, som den første klasse. Hvis en klasse for eksempel nedarver fra en klasse, der har en variabel $variabel1, vil den nedarvende klasse også have den variabel. Eksempel:
class Klasse1
{
private $variabel1;
protected $variabel2;
}
class Klasse2 extends Klasse1
{
}
$instans2 = new Klasse2;
echo $instans2->variabel1; // Forkert, $variabel1 er private og Klasse2 nedarver den derfor ikke
echo $instans2->variabel2; // Rigtigt, $variabel2 er protected og findes i Klasse2.
{
private $variabel1;
protected $variabel2;
}
class Klasse2 extends Klasse1
{
}
$instans2 = new Klasse2;
echo $instans2->variabel1; // Forkert, $variabel1 er private og Klasse2 nedarver den derfor ikke
echo $instans2->variabel2; // Rigtigt, $variabel2 er protected og findes i Klasse2.
En klasse nedarver med nøgleordet extends efterfulgt af den klasse, den skal nedarve fra.
En nedarvet klasse arver også medlemmer med nøgleordet public. Kun private nedarver den ikke.
#6
#7
Konstruktører og destruktører
Jeg nævnte på et tidspunkt nogle særlige metoder, der blev kaldt når et objekt (også kendt som en instans) blev oprettet, altså når der blev skrevet noget lignende $instans1 = new Klasse1, og når PHP har parset scriptet færdigt. Disse særlige metoder hedder helholdsvis konstruktører og destruktører. Disse skrives ligesom en normal metode, om end de skal være skrevet med nøgleordet public eller helt uden en ’protection modifier’ (public, private, protected). De skal også hedde noget bestemt. Konstruktøren kan enten hedde det samme som klassen, fx. Klasse1, eller den kan hedde __construct. Destruktøren skal hedde __destruct. Konstruktøren kan – modsat destruktoren - godt tage parametre. Et eksempel:
Uden nogle videre metodekald (brug af funktioner i en klasse) vil scriptet nu udskrive ’Spangsberg’. Dette vil ske, når $instans1 bliver destrueret, hvilket sker når scriptet er kørt færdigt. Som nævnt kunne vi have skrevet public function __construct($newValue) som kontruktør i stedet. Det er en smagssag, men hvis man vil holde stilen med to underscore’s og et forudbestemt, engelsk navn (som destruktøren har det), kan man med fordel bruge __construct.
Jeg nævnte på et tidspunkt nogle særlige metoder, der blev kaldt når et objekt (også kendt som en instans) blev oprettet, altså når der blev skrevet noget lignende $instans1 = new Klasse1, og når PHP har parset scriptet færdigt. Disse særlige metoder hedder helholdsvis konstruktører og destruktører. Disse skrives ligesom en normal metode, om end de skal være skrevet med nøgleordet public eller helt uden en ’protection modifier’ (public, private, protected). De skal også hedde noget bestemt. Konstruktøren kan enten hedde det samme som klassen, fx. Klasse1, eller den kan hedde __construct. Destruktøren skal hedde __destruct. Konstruktøren kan – modsat destruktoren - godt tage parametre. Et eksempel:
class Klasse1
{
private $value;
public function Klasse1($newValue)
{
$this->value = $newValue;
}
public function __destruct()
{
echo $this->value;
}
}
$instans1 = new Klasse1("Test");
{
private $value;
public function Klasse1($newValue)
{
$this->value = $newValue;
}
public function __destruct()
{
echo $this->value;
}
}
$instans1 = new Klasse1("Test");
Uden nogle videre metodekald (brug af funktioner i en klasse) vil scriptet nu udskrive ’Spangsberg’. Dette vil ske, når $instans1 bliver destrueret, hvilket sker når scriptet er kørt færdigt. Som nævnt kunne vi have skrevet public function __construct($newValue) som kontruktør i stedet. Det er en smagssag, men hvis man vil holde stilen med to underscore’s og et forudbestemt, engelsk navn (som destruktøren har det), kan man med fordel bruge __construct.
#7
#8
Konstanter
Flere gange i denne guide har jeg nævnt konstanter som en del af en gruppe af tilladte medlemmer. En konstant er det modsatte af en variabel. Kort forklaret er det en variabel, hvis værdi ikke kan ændres. En konstant oprettes med nøgleordet ’const’, og den kan hverken være statisk, public, private eller protected. Det er blandt andet her, PHP’s OOP-del forekommer mig lidt snæver. Et eksempel:
Flere gange i denne guide har jeg nævnt konstanter som en del af en gruppe af tilladte medlemmer. En konstant er det modsatte af en variabel. Kort forklaret er det en variabel, hvis værdi ikke kan ændres. En konstant oprettes med nøgleordet ’const’, og den kan hverken være statisk, public, private eller protected. Det er blandt andet her, PHP’s OOP-del forekommer mig lidt snæver. Et eksempel:
class Klasse1
{
const Konstant1 = ”Hello World!”;
public $Variabel1 = ”Hello Moon!”;
}
$instans1 = new Klasse1;
$instans1->Konstant1 = ”Test”; // Forkert, en konstant kan ikke ændre værdi
$instans1->Variabel1 = ”Mappel”; // Rigtigt, en variabel kan godt ændre værdi
echo $instans1::Konstant1; // Rigtigt, i PHP er en konstant både statisk og ustatisk, hvilket er dybt uregelmæssigt og forvirrende.
{
const Konstant1 = ”Hello World!”;
public $Variabel1 = ”Hello Moon!”;
}
$instans1 = new Klasse1;
$instans1->Konstant1 = ”Test”; // Forkert, en konstant kan ikke ændre værdi
$instans1->Variabel1 = ”Mappel”; // Rigtigt, en variabel kan godt ændre værdi
echo $instans1::Konstant1; // Rigtigt, i PHP er en konstant både statisk og ustatisk, hvilket er dybt uregelmæssigt og forvirrende.
#8









