Automatisk dependency injection i PHP med hjälp av typhintning

Jag har skrivit om dependency injection förut (anonyma funktioner, dependency injection och php oop kurs del 1) och nu är det faktiskt dags igen. Denna gången med en liten twist. I en del ramverk, i tex Laravel, så har man möjlighet att injicera beroenden in i andra objekt enbart genom att typhinta klassnamnet. Jag vet inte säkert om det heter så på svenska, men på engelska heter det type hinting.

Type hinting använder du genom att skriva namnet på klassen du injicerar till ditt objekt. Vanligast är att man gör det i konstruktorn (constructor injection), och då kan det se ut som så här:

function __construct(Klassnamn $klass)

I detta exemplet, så måste objektet som används som argument vara av typen Klassnamn. Om du försöker injicera ett objekt av en annan typ, så kommer php att generera ett fel. Så varför är detta så bra? Jo, för att ditt objekt ska kunna fungera, så är det beroende av denna specifika typen av objekt. Utan type hinting skulle du kunna injicera vilket objekt som helst, och du kommer säkerligen att få massa saker som inte fungerar. På detta viset vet du att ditt objekt inte kan skapas upp utan att det har alla beroenden det behöver. Det är just det som är dependency injection och type hinting i ett nötskal. Kort sagt – Använd er av det när ni programmerar objektorienterat  php! :)

Men hursomhelst, så måste ett objekt som behöver injiceras senare fortfarande skapas. Många dependency injection containers som finns på nätet löser detta, men jag skulle vilja visa ett sätt som det kan göras på för att visa hur det hela kan fungera.

Observera/Disclaimer Detta är inte är någon kod jag vill ni ska använda i skarpt läge, men det kan kanske vara intressant att läsa igenom och kanske inspireras av?

Koden till en förenklad Service Container

Låt oss se hur den ser ut:

<?php
class ServiceContainer
{
    /* @var Lagrade objekt */
    private $instances;

    /**
     * Skapa upp servicecontainern och lagra sig själv.
     */
    public function __construct()
    {
        $this->add($this);
    }

    /**
     * Lägg till ett objekt.
     * @param object $instance
     */
    public function add($instance)
    {
        $this->instances[get_class($instance)] = $instance;
    }

    /**
     * Hämta ett objekt.
     * @param string $name Klassnamn.
     * @return object
     */
    public function get($name)
    {
        // Använd reflection för att undersöka klassen.
        $reflector = new \ReflectionClass($name);

        // Objekt av typen finns redan lagrad.
        if (isset($this->instances[$name])) {
            return $this->instances[$name];
        }

        // Skapa en ny instans.
        if ($reflector->getConstructor()) {
            $injections = $this->getInjections($reflector);
            $instance = $reflector->newInstanceArgs($injections);
        } else {
            $instance = $reflector->newInstance();
        }

        // Lagra och returnera objektet.
        $this->add($instance);

        return $instance;
    }

    /**
     * Hämta konstruktorns parametrar.
     * @param ReflectionClass $reflector
     * @return array
     */
    private function getInjections($reflector)
    {
        $parameters = $reflector->getConstructor()->getParameters();
        foreach ($parameters as $parameter) {
            $name = $parameter->getClass()->name;
            $injections[] = $this->get($name);
        }

        return $injections;
    }
}

Låt oss gå uppifrån och ner för att förklara hur det fungerar.

Den enda variabeln (instances) vi behöver är för att lagra objekt som redan finns uppskapade. Objekten är delade, d.v.s. att endast ett objekt av samma typ kan existera i samma container.

Konstruktorn (__construct) är enkel. Det enda den gör är att lägga till sig själv i containern.

Metoden add() är lika enkel den. Där kan du manuellt lägga till ett objekt till containern om du skulle behöva. Nyckeln som lagras internt blir klassnamnet.

Nu börjar det bli intressant. Metoden get() används för att skapa och returnera ett efterfrågat objekt från containern. Om en instans av rätt klass redan finns lagrad, så returneras den direkt. Vi använder oss av Reflections för att undersöka den efterfrågade klassen. Om klassen har en konstruktor, undersöks den och vi kan avgöra om den har några argument eller inte. Om konstruktorn tar argument, så kallar vi på metoden getInjections() för att hämta dessa. En ny instans skapas med hjälp av newInstanceArgs(), eller newInstance(). Slutligen lagras instansen i containern och så returneras den tillbaka.

Metoden getInjections() används slutligen för att skapa upp objekt som behöver injiceras till en konstruktor för att ett objekt ska kunna skapas upp. Metoden loopar igenom alla argument och skapar upp objekt med hjälp av get() och returnerar dem tillbaka i en array som kan användas när det beroende objektet sedan skapas upp.

Ett exempel:

// Exempel:
class Engine {}
class Tires {}
class Car {
    public function __construct(Engine $engine, Tires $tires) {}
}
class Owner {
    public function __construct(Car $car, ServiceContainer $sc) {}
}

$c = new ServiceContainer();
$owner = $c->get('Owner');

var_dump($owner);
var_dump($c);

Först några klasser som vi kan testa att skapa instanser utav. Engine och Tires är enkla klasser som inte har någon konstruktor och således inte har några beroenden heller. Klassen Car däremot är beroende av både Engine och Tires för att fungera. Lägg märke till type hintingen av dessa två klasser i konstruktorn.

Klassen Owner vill ha en Car för att bli tillfredställt och bara för att vi ska se att det fungerar, så slänger vi in även containern som ett beroende. Ett varningens ord här dock – Det är generellt sett inte någon bra idé att skapa ett beroende av en container i en klass. Även om vi har type hintat containern, så vet vi inte om alla containern innehåller alla de objekt koden förväntar sig. Det är bättre att type hinta respektive klass som ett objekt är beroende av i konstruktorn, vilket ju är just det som hela detta inlägget faktiskt går ut på!

Så när vi anropar ServiceContainerns metod get(), så skapas samtliga objekt upp som Owner är beroende av. Genom att dumpa ut $owner och $c så ser vi just detta. Ownerobjektet är skapat och $c, dvs servicecontainern, innehåller alla de objekt som Owner är beroende av:

object(Owner)[5]
object(ServiceContainer)[1]
  private 'instances' => 
    array (size=5)
      'ServiceContainer' => 
        &object(ServiceContainer)[1]
      'Engine' => 
        object(Engine)[9]
      'Tires' => 
        object(Tires)[10]
      'Car' => 
        object(Car)[8]
      'Owner' => 
        object(Owner)[5]

Sammanfattning

Det är inte alla tillfällen som lämpar sig för en sådan här (enkel) lösning. Det finns några stora nackdelar. Till exempel så kan containern bara leverera objekt som har argument med type hintade klasser. Och dessa i sin tur måste också bestå av desamma. Det går inte i denna implementationen att använda sig av argument som tar strängar, arrays eller annat.

Den klarar inte heller av att injicera objekt där man använder interface som typhintning, vilket till viss del motverkar (subtype) polymorfism, vilket egentligen är önskvärt när man programmerar objektorienterat.

Den hanterar endast injektioner till konstruktorn. I vissa situationer så kan injektioner till enskilda metoder också vara praktiskt (s.k. method injection). Om det där tvistar de lärde, men jag föredrar att i största möjliga mån hålla mig till konstruktorn.

Containern lämpar sig helt enkelt bäst till ändamål där man automatiskt behöver skapa beroenden och eventuell input till respektive objekt sker först efter instansieringen. Alternativt beroende av objekt som kan skapas upp mer eller mindre manuellt innan den automatiska instansieringen. Tänk er en controller i MVC som väljs genom URL, eller någon annan routing, och som behöver en eller flera modeller för att kunna göra sitt jobb. I ett automatiserat flöde, så kan vi inte manuellt skapa upp dessa objektberoenden, utan vi måste förlita oss till någon form av objektfabrik, såsom denna service containern.

Hoppas ni förstår kraften med denna typen av funktionalitet och kanske söker er vidare till någon av de mer robusta och testade lösningar som finns där ute. Lösningar som många gånger tar upp de problem som jag nämnt ovan. Sök gärna mer information på er favoritsökmotor. Använd te.x. ”dependency injection container php” som sökord.

Må bäst, tills nästa gång! :)

Installera (och använd) php’s beroendehanterare composer i Windows 8

Traditionellt sett så har Microsoft Windowsanvändare de senaste åren varit ganska motvilliga att använda allt annat än det grafiska gränssnittet. Men som webb/php-utvecklare idag, så är det mycket vunnet att kunna använda kommandotolken åtminstone till det allra enklaste. En av anledningarna är composer.

Composer logoComposer är en programvara som hjälper dig att handskas med beroenden i din kod. Genom att definiera vilka beroenden, eller paket som du behöver i ditt projekt, så kan du enkelt ladda ner dem och hålla dem uppdaterade via enkla kommandon. Du får dessutom en autoloader på köpet, som laddar in klasser när du använder dem utan att du behöver använda include eller require i php.

De flesta stora php-ramverk använder sig idag av composer som standard. Och de som inte gör det, borde faktiskt ta sig en allvarlig funderare på att böra göra så.

Installationen av composer har tidigare varit lite bökig i windows-miljö för den ovane, men nu så har installationsprocessen blivit så enkel den bara kan bli.

Installera composer

Gå in på composers hemsida, https://getcomposer.org/ , klicka på download i menyn och ladda ner installationsfilen (Composer-Setup.exe) under överskriften Windows installer.

Kör filen och följ instruktionerna. Standardinställningar är okej att använda. Composer måste veta var php ligger installerat och kommer att fråga efter detta under installationen. På min maskin, med WAMP installerat som lokal server, ser det ut som nedan:

Sökväg till php i composerinstallation

Fortsätt att följa instruktionerna, så ska det snart vara klart!

När installationen är klar kan du kontrollera att allt fungerar genom att öppna en kommandotolk (tryck på windows-tangenten, skriv ”cmd” följt av enter). Om allt fungerar så ska du kunna köra kommandot ”composer” var som helst i mappstrukturen, och resultatet kommer att se ut ungefär som det nedan:

composer i kommandotolken

Där ser du nu vilka argument du kan använda för composer. Läs mer på deras hemsida om alternativen.

Använda composer i ett nytt projekt

Att installera composer är en sak, men att använda den är en annan. Även om dokumentationen är ganska tydlig, så känner jag att ett snabbt exempel på användning måste finnas med i mitt inlägg.

Packagist, är den hemsida som innehåller de officiella paket som composer använder sig av. Där kan du enkelt söka dig fram till ett intressant paket som du skulle vilja testa. Varför inte monolog, ett paket för att logga händelser till fil?

Skapa en ny mapp/projekt på sin webbserver och lägg en ny fil, composer.json i den, med följande innehåll:

{
   "require": {
      "monolog/monolog": "1.12.*@dev"
   }
}

Gå nu in i din nya mapp i kommandotolken och kör ”composer install” (utan citattecken), så kommer monolog att laddas ner och läggas till i ditt projekt. Composer löser dessutom själv monologs beroenden, så att alla paket den är beroende av också laddas ner.

(Har du inte git installerat på din maskin så kommer du att få lite varningar, men det kommer att fungera ändå).

Nu har en ny mapp, ”vendor”, lagts in där alla paket finns lagrade. En autoload-fil har automatiskt skapats som håller reda på vart filerna ligger, så att de kan hittas av composer när du anropar dem. Detta är alltså den enda fil du behöver ladda in för att sedan använda vilket paket som helt som du lagt in i composer.json-filen. Skapa en index.php i roten av ditt projekt för att testa:

<?php
require 'vendor/autoload.php';

$log = new Monolog\Logger('name');
var_dump($log);

När du kör koden, så kommer du att se din nyskapade instans av Monolog-klassen, färdig att användas.

För att lägga till fler paket till ditt projekt, så lägger du till ett nytt beroende i composer.json och kör ”composer update”, så läggs de nya filerna in i projektet!

Gör valet att följa standarder i PHP

PHP Framework Interop Group är en  sammansatt grupp av medlemmar ur många av de allra största ramverken i PHP. Tanken med gruppen är att de ska besluta om gemensamma kodstandarder i PHP-världen. Och med så många både stora och lite mindre aktörer som medlemmar så har (faktiskt) php-fig och deras standarder PSR (Proposed Standards Recommendation) blivit mer än väl accepterat.

Fördelen med att följa en standard är många. Det kan t.ex. handla om allt från att det blir lättare att läsa kod eftersom du är van vid utseendet, till att en standardiserad pakethantering gör det lättare att använda 3’e-parts kod i ditt egna projekt.

Fördelarna är kanske inte så stora om du bara sitter hemma i kammaren och programmerar endast själv, och för dina egna ändamål. Men om du har minsta intresse av att så småningom blanda in flera programmerare, eller kanske publicera ditt projekt för att andra ska kunna använda det, då blir det genast mycket viktigare.

För att inte tala om den dagen du blir anställd programmerare och kan nämna att du följer standarder! Då är det bra att var förberedd och inte så smart att säga ”jag har alltid gjort på detta viset, och kommer att fortsätta med det” ;)

Nedan följer en länkar och en kort beskrivning av några standarder:

PSR-0 beskriver standarder för autoloading av klasser, och därmed även filnamn. Det finns även exempel på hur en autoloader kan se ut med vanlig autoload och med SplClassLoader, som är att föredra framför de två.

PSR-1 beskriver enklare kodstandarder såsom php-taggar, teckenkodning, klass- och metodnamn, konstanter och namespaces.

PSR-2 tar över där PSR-1 slutar och beskriver detaljer kring kodstandarden. Bl.a. beskrivs när radbrytningar och mellanslag ska ske eller inte ske,  var klamrar och paranteser ska läggas, hur indentering i koden ska vara och mycket mer. Detta är den mest utförliga standarden när det gäller ”utseendet” på din kod.

PSR-3 beskriver hur ett interface för loggning av varningar och fel bör se ut. Exempelkod finns även här.

PSR-4 fortsätter att beskriva och komplettera PSR-0, dvs hur autoloadern ska fungera. Nämner även Composer och pakethantering.

 PSR-5 är ännu inte en accepterad standard, men mycket tyder på att den inte kommer att ändra sig så väldigt mycket innan den accepteras. PSR-5 tar upp det som kallas för docblocks, en standard för att kommentera din kod. Docblocks används bland annat av phpDocumentor för att automatiskt generera dokumentation.

Semantic Versioning 2.0.0 är inte en del av php-fig, men beskriver hur du bör tänka när du sätter versioner på ditt projekt.

 

Dependency Injection Container med anonyma funktioner/closures i PHP 5.3

Tänkte visa hur man kan använda anonyma funktioner/closures (en funktion som infördes i PHP 5.3) för att skapa en enkel Dependency Injection Container.

En DI-container är ett objekt som håller reda på dina objekts beroenden av andra objekt eller variabler. Du konfigurerar den en gång (i en config-fil eller likande) så att du slipper att fundera över vad som krävs för att skapa objekt senare. Du använder helt enkelt containern för att skapa objekt istället för att använda new.

Den lagrar även den skapade instansen, så att du alltid hämtar samma instans varje gång du anropar den. Du kombinerar lämpligen med en autoloader. På det viset får du både lazy initialization och lazy loading på en gång.

**
* Dependency Injection Container / Registry
* För PHP >= 5.3
*
* @author Ola Waljefors
*/
class container
{
    /**
    * $var array Lagrad data
    * @access private
    */
    private $data;

    /**
    * Registrera en egenskap
    *
    * @param string $key
    * @param mixed $value
    */
    public function __set ($key, $value)
    {
        $this->data[$key] = $value;
    }

    /**
    * Returnera en egenskap
    *
    * Om den är en closure anropas den och lagrar
    * ny data innan den returneras
    *
    * @param string $key
    * @return mixed
    */
    public function __get ($key)
    {
        if (is_a ($this->data[$key], 'Closure')) {
            $this->data[$key] = $this->data[$key] ();
        }
        return $this->data[$key];
    }
}

__set() lagrar den variabeln/objektet/whatever i containern.

Om __get() stöter på en closure så kör den funktionen och returnerar svaret, annars returnerar den egenskapen som den är.

Så hur använda den?

För att lagra och hämta tex strängar gör du så här:

$c = new container ();

$c->db_engine = 'mysql';
$c->db_host = 'localhost';
$c->db_name = 'wordpress';
$c->db_user = 'root';
$c->db_password = '';

// Skriv ut en sträng lagrad i containern
echo $c->db_engine;

Lite exempel på hur du konfigurerar objekt:

// Ett enkelt objekt utan argument
$c->mittObjekt = function () use ($c) {
    return new mittObjekt ();
};

// Ett andra objekt som använder det
// första som argument
$c->mittAndraObjekt = function () use ($c) {
    return new mittAndraObjekt ($c->mittObjekt);
};

// Ett PDO-objekt som använder containerns
// egenskaper som argument
$c->pdo = function () use ($c) {
    $dsn = sprintf ('
        %s:dbname=%s;host=%s',
        $c->db_engine,
        $c->db_name,
        $c->db_host
    );
    return new PDO(
        $dsn,
        $c->db_user,
        $c->db_password
    );
};

När du sedan ska hämta objekten så kommer den anonyma funktionen att köras, det skapade objektet lagras i containern och returneras så att du kan börja använda det.

Alla beroenden hämtas rekursivt så att alla objekt skapas upp automatiskt (i detta fallet mittAndraObjekt och mittObjekt). Om något av de ingående objekten redan har skapats upp så används dem.

// Hämta ett objekt från containern
$c->mittObjekt;

// Om du samtidigt vill köra en av objektets
// metoder (utan att behöva köra raden ovan)
$c->mittObjekt->metod();

Fabien Potencier (skaparen av ramverket Symfony) har skrivit ett blogginlägg som förklarar enkelt varför en DI-container kan vara en bra idé.

Objektorientering i PHP (del 1)

(Denna artikeln har jag tidigare publicerat på phpportalen.net. Tanken är att jag så småningom ska fortsätta att skriva om ämnet).

Inledning

PHP är ett väldigt enkelt programmeringsspråk att lära sig, nästan lite för enkelt att lära sig. Det finns så många sätt att nå ett och samma mål, några är bra och andra mindre bra. Det är också lätt att lära sig grunderna i objektorientering i PHP, men det är en konst att bemästra den fullt ut. När jag började skriva på denna lilla artikelserie/kurs blev jag också varse om att det är väldigt svårt att lära ut. Jag kände mig lika frustrerad som jag gjorde när jag skulle börja lära mig om OOP (Object Oriented Programming)

Sanningen är den att jag har mycket att lära fortfarande. Jag har inte kommit så långt som jag skulle vilja önska. Det är bara att inse, OOP är besvärligt att lära sig för de allra flesta. Jag kommer att nämna en hel del nya och ibland krångliga termer som ni förmodligen inte känner igen från vanlig traditionell PHP-programmering. Men ta er tid att lägga dem på minnet och försök att förstå vad de kan spela för roll för dig och hur du kan nyttja dem.

Denna artikelserie förutsätter att du är väl insatt i grunderna i PHP och funktioner. Du kommer också att behöva PHP5+.

Vad är egentligen objekt?

Objektorienterad programmering utvecklades redan på 60-talet till det norska programspråket Simula 67. Som namnet antyder ett programspråk för simulering, som användes till att simulera avancerade och komplexa fysiska processer. Idén med språket var att man skulle dela upp delar av koden till olika objekt, där alla objekt hade sina egna egenskaper och funktioner. Man skapade en modell av den verklighet man behövde simulera.

Olika objekt skapades som modeller för att efterlikna egenskaperna hos vanliga fysiska objekt – Saker helt enkelt! Dessa objekt kombinerades sedan med varandra i programmen på samma sätt som de flesta saker i verkligheten består av många mindre delar.

Så egentligen skulle frågan i överskriften vara: Vad i ett program behöver modelleras som ett objekt? Men det får nog saken att låta mer komplicerad än vad den egentligen är.

Exempel:

Låt oss säga att vi skapar ett bilspel. Man ska kunna välja mellan olika bilar och även kunna modifiera sin bil genom att byta ut delar som påverkar bilens prestanda. En starkare motor ger en bättre topphastighet, men också en större risk att hjulen spinner loss i starten. En ny hjulupphängning gör att bilen kränger mindre i kurvorna och andra däck ger bättre väggrepp. Alla saker som man kan byta ut påverkar bilens prestanda på något sätt, vingar, kardanaxlar, bromsar m.m.

Alla dessa delar i ett objektorienterat program skulle vara ett objekt. Om vi skulle se på det väldigt enkelt så är alla ”delar” eller alla substantiv i den verkligheten du simulerar objekt i ditt program. Bil, motor, hjulupphängning, däck, vingar, kardanaxlar och bromsar modelleras alla i ditt program till olika objekt.

I spelet ger vi alla delar olika egenskaper som sedan påverkar den samlade egenskapen hos hela bilen: Topphastighet, acceleration, vindmotstånd, väggrepp och bromssträcka t.ex.

Om vi återkopplar tanken på vårt bilspel till språket Simula och varför det utvecklades, så hoppas jag att ni ser kopplingen? Vi måste helt enkelt i vårt spel kunna beräkna/simulera hur bilen ska uppföra sig med givna parametrar. I ett avancerat bilspel skulle vi inte nöja oss med att modellera bilen och alla delarna som objekt. I verkligheten finns det fler ”objekt” som borde påverka bilen i spelet: Vägunderlag, väder och vind m.m.

Objektorienterad programmering visade tydligt sig ha många fördelar när man sysslade med annat än simuleringar också, som exemplet med bilspelet visar.

Fördelarna med objektorientering?

Vad är det som gör objektorientering så intressant? Många skulle säga att OOP ökar din produktivitet, och de är till viss del sant eftersom OOP uppmuntrar till återanvändbar kod. Men det inte alls så besvärligt att skapa vanliga funktioner som du kan återanvända i flera projekt. Om du använder ett av alla ramverk som finns därute så kan du definitivt snabba upp ditt utvecklingsarbete ordentligt. Men frågan är om det är OOP’s förtjänst eller ramverkets.

De allra största fördelarna med OOP är dock möjligheten att skapa en extremt flexibel kod som har ett lättanvänt gränssnitt, minimerar buggar, är enkelt att anpassa, bygga ut och att utöka funktionaliteten på.

Fördelarna är svåra att förklara, men det finns ett gäng verktyg i OOP som tillsammans skapar möjligheten till mycket flexibla lösningar. De är bland annat:

  • Meddelanden (message passing)
  • Arv (inheritance)
  • Polymorfism (polymorphism)
  • Synlighet (visibility)
  • Abstraktion och inkapsling (abstraction, encapsulation)
  • Designmönster (design patterns)

Jag kommer att förklara dem alla närmare nedan.

Grundläggande om objekt

Ett objekt är en enhet, en programdel, som kombinerar och paketerar funktionalitet och data på ett och samma ställe. I sin enklaste form så består ett objekt helt enkelt av ett antal funktioner och variabler. Ett helt objekt kan lätt lagras i en variabel, vilket gör att du kommer åt både funktioner och variabler från ett och samma variabelnamn.

ett objekt

För att skilja fristående funktioner från de som finns i ett objekt så kallar man dem i objektsammanhang för metoder. En metod används ofta för att påverka eller hämta objektets variabler. De döps därför lika ofta till ett verb följt av ett substantiv ex. ”getColour”, eftersom de gör saker med objektet, eller hämtar saker ur det. Alla metoder kan ta ett eller flera argument när den kallas och returnera ett svar tillbaka till den som kallar den, precis som du är van vid ifrån vanliga funktioner.

Variabler kallas i regel för attribut eller egenskaper när de existerar i ett objekt. De kan precis som i vanlig PHP-programmering innehålla strängar, vektorer, siffror, vektorer och resurser t.ex. Som jag nämnde innan så kan även objekt lagras i variabler, så det är fullt möjligt att lagra ett objekt som en egenskap i ett annat objekt.

ett objekt lagrat i ett annat objekt

Du kommer att stöta på många exempel på det längre fram.

Klasser (classes) och objekt (objects)

För att ett objekt ska kunna skapas, så behöver den en mall som definierar hur objektet ska skapas. De mallarna kallas för klasser. En klass kan sägas vara en ritning, ett recept, en mall på hur ett objekt ska fungera. Det är alltså i klassen du definierar alla metoder och egenskaper som du vill att ditt objekt ska ha.

När du skapar ett objekt av en klass så säger man att objektet är en instans av klassen. Du har möjlighet att skapa oändligt många objekt från en och samma klass och alla kommer att se likadana ut och fungera på exakt samma sätt. Jämför det med en fabrik där du tillverkar badankor t.ex. Klassen är gjutformen som bestämmer hur ankorna ska se ut. I fabriken kan du sedan tillverka hur många ankor (objekt) som helst av samma gjutform. Formen slits aldrig ut och nästa anka kommer alltid att lämna maskinen exakt likadan som den förra så länge du använder samma form.

klasser och objekt

När ankorna sedan lämnat gjutformen så kan du välja att måla dem alla i olika färger genom att ge den olika egenskaper. Alla ankor är sina egna objekt med olika egenskaper av samma attribut, men de är i grunden alla tillverkade ur samma form.

olika objekt med olika egenskaper från samma klass

Skapa objekt och använda meddelanden (message passing)

Meddelande i objektorientering är den teknik du använder för att kommunicera med ditt objekt. Meddelanden skickar du till ditt objekt varje gång du anropar en metod i objektet, så i princip så är metoden namnet på ditt meddelande och argumenten är själva meddelandet. Detta borde du känna igen från traditionell PHP-programmering, eftersom funktioner har många likheter med objektmetoder. Men det är bara i OO som det kallas för meddelanden.

Exempel:

Om vi återigen tittar på våra badankor som vi pratade om innan, så behöver vi en klass som är själva gjutformen för våra kommande ankor. Vi kan kalla den klassen kort och gott för ”Duck”. Ankan ska kunna målas och få en unik färg, så en egenskap i klassen måste vara färgen. I klassen ”Duck” lägger vi alltså till en egenskap ”colour”.

Vi vill också ha en metod för att sätta en färg på ankorna som lämnar maskinen. Vi kallar den metoden för ”setColour”. setColour behöver ta ett enda argument – Färgen på vår anka. Vi kan också passa på att införa även ett sätt att hämta färgen på en färdigmålad badanka, ”getColour”. Detta ger oss följande klass:

klassen duck

För att skapa en instans, ett objekt, av en klass i PHP använder man nyckelordet new. Så skapa och lagra tre identiska objekt av samma typ är väldigt enkelt:

$anka1 = new Duck();
$anka2 = new Duck();
$anka3 = new Duck();

Vi har nu tre helt identiska omålade ankor lagrade med varsitt variabelnamn $anka1, $anka2 och $anka 3. Enklare kan det inte vara, vi bara säger till PHP att skapa ett nytt (new) objekt av typen Duck och samtidigt lagrar vi den i en variabel.

För att sätta en färg på våra badankor så måste vi nu skicka ett meddelande till dem, med färger som argument. Vi skickar ett meddelande till var och en av ankorna:

$anka1->setColour(”red”);
$anka2->setColour(”green”);
$anka3->setColour(”blue”);

Om vi har satt upp metoden setColour ordentligt i Duck-klassen, så kommer nu metoden att lagra alla färgerna i egenskapen colour i var och en av badankorna. För att sedan hämta och skriva ut färgerna på ankorna kan vi skicka nya meddelanden till objekten. Vi använder oss av getColour som returnerar färgen:

$anka1->getColour();

En bild och dataflöde över vårt objekt och meddelanden som går till och från objektet ser nu ut så här:

objekt av typen duck

Det kan vara värt att observera att vi måste sätta upp metoderna setColour() och getColour() själva. PHP sätter och hämtar inte egenskapen colour automatiskt bara för att jag döpt metoderna till just set- och getColour. Metoder kan vara just så här enkla, men det är också troligt att du kommer att skapa mer avancerade metoder som behandlar data innan de returnerar ett svar till användaren.

Arv (inheritance)

Ett av objektorienteringens främsta verktyg är arv. Ett objekt kan ärva egenskaper från ett annat objekt och på det viset skapa en ny mer specialiserad variant av objektet den ärver av. Objektet du ärver ifrån kallas för förälder (parent/super class) och den som ärver för barn (child/sub class). Barnet kan komma åt alla metoder och egenskaper som föräldern har, vilket gör att du kan definiera gemensamma metoder och egenskaper i föräldern som sedan alla barn delar.

arv

Flera olika objekt kan ärva från samma förälder, men bara från en (i OOP behövs det inte två kön för att skapa ett barn). En förälder kan däremot ha hur många barn som helst.

I ett barn kan du också välja att ersätta en eller flera av förälderns metoder helt och hållet. Definiera den på nytt med samma namn i barnet och du har ersatt funktionaliteten i föräldern. Det sättet som du skriver över förälderns metoder i barnet kallas för metodöverlagring (method overriding).

Exempel:

Tänk dig en webbshop där olika artiklar till viss del delar egenskaper med varandra. Alla produkter har ett artikelnummer, namn, pris, momssats och vikt t.ex. Men det finns ju andra egenskaper som beror på vilken typ av produkt det rör sig om. En CD har en speltid och artist/grupp och en bok har istället sidnummer och författare. Hur gör du om du vill göra en utskrift där de olika delarna skrivs ut olika (utan att ställa villkor på vilken typ av objekt det är)?

Du kan med OO skapa ett objekt med alla de gemensamma egenskaperna. Sedan låta två specialobjekt ärva de gemensamma metoderna från föräldern där du hanterar olikheterna. Gränssnittet kan vara detsamma i båda barnen, och du använder dem likadant, men du får olika resultat när du till exempel ska göra en snygg utskrift med en beskrivning av produkten.

Polymorfism (polymorphism)

Olika objekttyper, t.ex. olika barn som jag nämnde ovan, kan innehålla samma metodnamn. Varje unikt objekt kan alltså ha olika implementation med samma metod. OOP ger oss med andra ord möjligheten att olika objekt kan ge olika svar på samma metodanrop. Det är det som kallas för polymorfism.

Användaren behöver inte veta hur det aktuella objektet hanterar informationen eller hur metoden fungerar, han behöver bara veta hur han anropar den. Men varje objekt har ett unikt svar på samma metod.

I vanlig programmering har inte den möjligheten. Flera funktioner kan inte ha samma namn utan måste döpas olika. Det gör att du har mindre flexibilitet och gör koden svårare att underhålla.

Exempel:

I din webbshop behöver du ha olika fraktalternativ. Ett alternativ kan vara ett fast pris, ett annat kan beräkna frakten med hjälp av totalvikten på varukorgen. Här kan du välja att kombinera olika fraktobjekt med ditt varukorgsobjekt beroende på vilken typ användaren har valt. De olika fraktalternativen ger olika frakter, de beräknas olika i de olika objekten, men sättet du anropar dem på är desamma.

polymorfism

Varukorgsobjektet behöver alltså inte veta vilket fraktalternativ som har valts, eftersom sättet den använder fraktalternativen på är precis desamma. Det som avgör är vilket fraktobjekt du väljer att kombinera med varukorgen.

Synlighet (visibility)

Även om det är möjligt i PHP så uppmuntras man att inte direkt hämta och ändra ett objekts egenskaper, utan man påverkar istället alltid dem genom objektets metoder. För att användaren inte ska lockas att lagra ett nytt innehåll i en av dina egenskaper utan att gå via metoderna så har erbjuder objektorienteringen ett verktyg som kallas för synlighet.

Publika (public) metoder/egenskaper kan nås i alla sammanhang och är därför helt öppna både inifrån objektet självt och utifrån. Som jag nämnde ovan så sätter man därför sällan egenskaper till publika, medan metoder oftast är det.

Privata (private) metoder/egenskaper kan bara nås inifrån det egna objektet, inte ens barn/subklasser har åtkomst till dessa. Av samma anledning som ovan så sätts alltså oftast egenskaper som privata.

Skyddade (protected) metoder/egenskaper kan nås inifrån objektet och alla barn/subklasser, men inte utifrån objektet.

Detta är viktigt eftersom de bestämmer vilka egenskaper och metoder som användaren ska nå. De skyddar de känsliga interna delarna av objekten och lämnar bara ut information om objektet genom ett öppet och väl definierat gränssnitt av publika metoder. I vanlig PHP har du inte den möjligheten, där är alla funktioner och variabler fritt åtkomliga att påverkas i princip när som helst. Det kan innebära att användaren påverkar en variabel vid fel tillfälle, eller i tron att de gör något annat, med rejäla buggar som följd.

Synligheten är alltså ett säkerhetsverktyg du använder för att styra vilken åtkomst användaren (och andra objekt) ska ha till de metoder och egenskaper som du definierat i ditt objekt.

Abstraktion och inkapsling (abstraktion, encapsulation)

OOP tillåter oss alltså att kombinera funktionalitet och data i ett objekt. Vi väljer att tillhandahålla bara de metoder vi behöver för att styra och kontrollera objekten. Allt som händer inne i objekten, alla komplicerade detaljer, behöver inte användaren bry sig om eftersom koden är inkapslad i objekten genom att dölja både beteende och funktion.

Genom abstraktion reducerar vi alltså alla detaljer till ett enkelt gränssnitt. Ett objekt kan innehålla en rejäl data- och metodstruktur i sitt inre, men användaren ser bara just det som behövs och bakom kulisserna sker magin.

Abstraktion hjälper på detta viset också till att skydda användaren från att behöva repetera samma tråkiga kodstycken gång på gång genom att abstrahera flera rutiner bakom en metod. Ett välutvecklat objektorienterat system låter dig göra komplicerade saker på ett väldigt enkelt sätt, genom att bara skicka meddelanden (via metoder) i olika objekt. Det är det som är abstraktion i ett nötskal.

Designmönster (design patterns)

Designmönster är ingen del av PHP, inga funktioner, utan en teknik och ett hjälpmedel för att förstå hur du bäst kombinerar olika objekt med varandra.

Ett designmönster är en etablerad och väldokumenterad lösning på ett känt problem. Så att säga ett ”best practice” för olika givna problem, färdiga att välja och vraka mellan. Det är ett färdigt koncept för ett framgångsrikt, effektivt och flexibelt kodanvändande. Ett designmönster kan vara litet och enkelt eller stort och avancerat, men gemensamt är att de beskriver hur du använder objekt och hur de interagerar med varandra.

Per definition så ska en beskrivning av ett design mönster ha ett namn så att andra kan referera till den enkelt när du diskuterar med andra eller söker på nätet. Den ska ange ett eller flera liknande problem som exempel på vad du kan använda den. Den ska (givetvis) ha en lösning på problemet, själva mönstret/receptet på lösningen. Den bör också nämna konsekvenserna av att använda det beskrivna mönstret, dess fördelar och eventuella nackdelar.

Exempel:

Håll i hatten, för nu kommer det kanske att bli mycket att ta in. Känner ni att det inte är dags för mer avancerade förklaringar av designmönster i PHP, hoppa då över resten av kapitlet.

Men jag vill ge er en liten aptitretare på vad som komma skall och förhoppningsvis ger det ytterligare en bild av vad designmönster är och förhoppningsvis lite fler ledtrådar om objektorienteringens mysterier. Bilden må se lite avancerad ut, men det ser nog värre ut än vad det är.

Om ni tittar på bilden nedan så beskriver den ett exempel på hur ett antal olika objekt kan relatera till varandra. I detta fall delar av en webbshop (klicka för att se bilden större):

del av objektorienterad webbshop

Det allra enklaste designmönstret måste nog sägas vara ”Dependency Injection”. Några skulle vilja säga att det är så enkelt att det inte ens räknas som ett mönster. Det kan kanske diskuteras, men det är helt klart ett viktigt verktyg i OOP.

Item-objektet är en produkt/artikeln i webbshoppen med tre olika egenskaper; name, price och weight. Dessa Item-objekt ska man kunna placera ett antal av i ShoppingCart-objektet. För det ändamålet så använder vi en metod, addItem(), som tar ett Item-objekt som argument. addItem() tar det objektet du skickar med i meddelandet och lagrar det i en vektor (array) för säkert förvar i ShoppingCart-objektet. Det här sättet att ”skjuta” in ett eller flera nödvändiga objekt in i ett annat är det som kallas för Dependency Injection.

De olika fraktobjekten till höger i bild har vi varit inne och nosat på förut i kapitlet. Via ytterligare en Dependency Injection så kan vi skicka ett meddelande till ShoppingCart-objektet där vi anger vilken typ av fraktberäkning som ska användas. De två fraktberäkningarna ligger i varsitt objekt; FixedShipping och PricedShipping. De har båda en varsin metod, getShipping(), som beräknar fraktkostnaden på olika sätt = Polymorfism, om ni minns. Den ena med fast pris och den andra med ett pris beräknat på totalsumman på de produkter (items) som ligger i varukorgen.

Sättet som vi flyttar ut fraktberäkningarna från ShoppingCart-objektet in till nya olika objekt som lätt är utbytbara, är ett exempel på det mönstret som kallas för ”Strategy Pattern”. Vill vi lägga till ytterligare en fraktberäkning att välja mellan så kan vi göra det genom att skapa ett ”WeightShipping” t.ex. som beräknar frakten utifrån vikten på varorna i varukorgen.

Koden till exemplet som beskrivs här kan du hitta på php-portalen: http://www.phpportalen.net/viewtopic.php?p=675983#675983

Det svåra med designmönster är att veta vilken man ska använda sig av var. Allt eftersom objektorienteringens principer sitter bättre och bättre, så blir designmönster mer och mer intressant att lära sig mer om. En applikation innehåller i regel många mönster som arbetar med varandra i olika kombinationer.

Sammanfattning

Förhoppningsvis har det jag beskrivit gett dig lite insikt om vad objektorientering innebär och vad den kan göra för dig och vad som skiljer den från vanlig traditionell PHP-programmering.