Grunden

Från Wiki.linux.se
Hoppa till navigering Hoppa till sök

Grunderna

class

Grundläggande klassdefinitioner börjar med nyckelordet `class`, följt av ett klassnamn och därefter ett par klamrar `{}` som omsluter definitionerna av klassens egenskaper och metoder.

Klassnamnet kan vara vilken giltig etikett som helst, förutsatt att det inte är ett PHP-reserverat ord. Ett giltigt klassnamn börjar med en bokstav eller ett understreck, följt av ett valfritt antal bokstäver, siffror eller understreck. Som ett reguljärt uttryck skulle det uttryckas så här: `^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$`.

En klass kan innehålla sina egna konstanter, variabler (kallas "egenskaper") och funktioner (kallas "metoder").

Exempel #1 Enkel klassdefinition

<?php
class SimpleClass
{
    // Egenskapsdeklaration
    public $var = 'ett standardvärde';

    // Metoddeklaration
    public function displayVar() {
        echo $this->var;
    }
}
?>

Den pseudovariabeln `$this` är tillgänglig när en metod anropas inom ett objektkontext. `$this` är värdet på det anropande objektet.

Varning: Att anropa en icke-statisk metod statiskt utlöser ett fel. Före PHP 8.0.0 skulle detta generera ett deprecationsmeddelande och `$this` skulle vara odefinierad.

Exempel #2 Några exempel på pseudovariabeln $this

<?php
class A
{
    function foo()
    {
        if (isset($this)) {
            echo '$this är definierad (';
            echo get_class($this);
            echo ")\n";
        } else {
            echo "\$this är inte definierad.\n";
        }
    }
}

class B
{
    function bar()
    {
        A::foo();
    }
}

$a = new A();
$a->foo();

A::foo();

$b = new B();
$b->bar();

B::bar();
?>

Output av ovanstående exempel i PHP 7:

$this är definierad (A)

Deprecated: Icke-statisk metod A::foo() bör inte anropas statiskt i %s  på rad 27
$this är inte definierad.

Deprecated: Icke-statisk metod A::foo() bör inte anropas statiskt i %s  på rad 20
$this är inte definierad.

Deprecated: Icke-statisk metod B::bar() bör inte anropas statiskt i %s  på rad 32

Deprecated: Icke-statisk metod A::foo() bör inte anropas statiskt i %s  på rad 20
$this är inte definierad.

Output av ovanstående exempel i PHP 8:

$this är definierad (A)

Fatal error: Uncaught Error: Icke-statisk metod A::foo() kan inte anropas statiskt i %s :27
Stack trace:
#0 {main}
  thrown in %s  på rad 27

readonly klasser

Från och med PHP 8.2.0 kan en klass markeras med readonly-modifieraren. Att markera en klass som readonly lägger till readonly-modifieraren till varje deklarerad egenskap och förhindrar skapandet av dynamiska egenskaper. Dessutom är det omöjligt att lägga till stöd för dem genom att använda attributet `AllowDynamicProperties`. Ett försök att göra det utlöser ett kompileringstidfel.

<?php
#[\AllowDynamicProperties]
readonly class Foo {
}

// Fatalt fel: Kan inte tillämpa #[AllowDynamicProperties] på readonly-klass Foo
?>

Eftersom varken otypade eller statiska egenskaper kan markeras med readonly-modifieraren, kan readonly-klasser inte deklarera dem heller:

<?php
readonly class Foo
{
    public $bar;
}

// Fatalt fel: Readonly-egenskapen Foo::$bar måste ha typ
?>
<?php
readonly class Foo
{
    public static int $bar;
}

// Fatalt fel: Readonly-klassen Foo kan inte deklarera statiska egenskaper
?>

En readonly-klass kan utökas om, och endast om, den underordnade klassen också är en readonly-klass.

new

För att skapa en instans av en klass måste nyckelordet `new` användas. Ett objekt kommer alltid att skapas om inte objektet har en konstruktor som definieras som kastar ett undantag vid fel. Klasser bör definieras före instansiering (och i vissa fall är detta ett krav).

Om en variabel som innehåller en sträng med namnet på en klass används med `new`, kommer en ny instans av den klassen att skapas. Om klassen finns i ett namnrymd, måste dess fullt kvalificerade namn användas vid detta.

Observera: Om det inte finns några argument att skicka till klassens konstruktor, kan parenteserna efter klassnamnet utelämnas.

Exempel #3 Skapa en instans

<?php
$instance = new SimpleClass();

// Detta kan också göras med en variabel:
$className = 'SimpleClass';
$instance = new $className(); // new SimpleClass()
?>

Från och med PHP 8.0.0 stöds användning av `new` med godtyckliga uttryck. Detta möjliggör mer komplex instansiering om uttrycket genererar en sträng. Uttrycken måste omslutas i parenteser.

Exempel #4 Skapa en instans med ett godtyckligt uttryck

<?php

class ClassA extends \stdClass {}
class ClassB extends \stdClass {}
class ClassC extends ClassB {}
class ClassD extends ClassA {}

function getSomeClass(): string
{
    return 'ClassA';
}

var_dump(new (getSomeClass()));
var_dump(new ('Class' . 'B'));
var_dump(new ('Class' . 'C'));
var_dump(new (ClassD::class));
?>

Output av ovanstående exempel i PHP 8:

object(ClassA)#1 (0) {
}
object(ClassB)#1 (0) {
}
object(ClassC)#1 (0) {
}
object(ClassD)#1 (0) {
}

I klasskontexten är det möjligt att skapa ett nytt objekt med `new self` och `new parent`.

När man tilldelar en redan skapad instans av en klass till en ny variabel, kommer den nya variabeln att få tillgång till samma instans som objektet som tilldelades. Detta beteende är detsamma när man skickar instanser till en funktion. En kopia av ett redan skapat objekt kan göras genom att klona det.

Exempel #5 Objektstilldelning

<?php

$instance = new SimpleClass();

$assigned   =  $instance;
$reference  =& $instance;

$instance->var = '$assigned kommer att ha detta värde';

$instance = null; // $instance och $reference blir null

var_dump($instance);
var_dump($reference);
var_dump($assigned);
?>

Output av ovanstående exempel:

NULL
NULL
object(SimpleClass)#1 (1) {
   ["var"]=>
     string(30) "$assigned kommer att ha detta värde"
}

Det är möjligt att skapa instanser av ett objekt på ett par sätt:

Exempel #6 Skapa nya objekt

<?php

class Test
{
    public static function getNew()
    {
        return new static();
    }
}

class Child extends Test {}

$obj1 = new Test(); // Med klassnamnet
$obj2 = new $obj1(); // Genom variabeln som innehåller ett objekt
var_dump($obj1 !== $obj2);

$obj3 = Test::getNew(); // Med klassmetoden
var_dump($obj3 instanceof Test);

$obj4 = Child::getNew(); // Genom en underklassmetod
var_dump($obj4 instanceof Child);

?>

Output av ovanstående exempel:

bool(true)
bool(true)
bool(true)

Det är möjligt att komma åt en medlem av ett nyligen skapat objekt i ett enda uttryck:

Exempel #7 Komma åt medlem av nyligen skapat objekt

<?php
echo (new DateTime())->format('Y');
?>

Output av ovanstående exempel:

2016

Observera: Före PHP 7.1 utvärderades inte argumenten om det inte fanns någon konstruktörsmetod definierad.

Egenskaper och metoder

Klassens egenskaper och metoder lever i separata "namnutrymmen", så det är möjligt att ha en egenskap och en metod med samma namn. Hänvisning till både en egenskap och en metod har samma notation, och om en egenskap kommer att nås eller en metod kommer att anropas beror enbart på sammanhanget, det vill säga om användningen är en variabelåtkomst eller ett funktionsanrop.

Exempel #8 Egenskapsåtkomst vs. metodanrop

<?php
class Foo
{
    public $bar = 'egenskap';
    
    public function bar() {
        return 'metod';
    }
}

$obj = new Foo();
echo $obj->bar, PHP_EOL, $obj->bar(), PHP_EOL;
?>

Output av ovanstående exempel:

egenskap
metod

Det betyder att det inte är direkt möjligt att anropa en anonym funktion som har tilldelats en egenskap. Istället måste egenskapen tilldelas en variabel först, till exempel. Det är möjligt att anropa en sådan egenskap direkt genom att omsluta den med parenteser.

Exempel #9 Anropa en anonym funktion lagrad i en egenskap

<?php
class Foo
{
    public $bar;
    
    public function __construct() {
        $this->bar = function() {
            return 42;
        };
    }
}

$obj = new Foo();

echo ($obj->bar)(), PHP_EOL;
?>

Output av ovanstående exempel:

42

extends

En klass kan ärva konstanter, metoder och egenskaper från en annan klass genom att använda nyckelordet `extends` i klassdeklarationen. Det är inte möjligt att utöka flera klasser; en klass kan bara ärva från en bas-klass.

De ärvda konstanterna, metoderna och egenskaperna kan åsidosättas genom att återdeklarera dem med samma namn som definierats i basklassen. Men om basklassen har definierat en metod eller konstant som final, får de inte åsidosättas. Det är möjligt att komma åt de åsidosatta metoderna eller statiska egenskaperna genom att referera till dem med `parent::`.

Observera: Från och med PHP 8.1.0 kan konstanter deklareras som final.

Exempel #10 Enkel klassarv

<?php
class ExtendClass extends SimpleClass
{
    // Omdefiniera basklassens metod
    function displayVar()
    {
        echo "Utökad klass\n";
        parent::displayVar();
    }
}

$extended = new ExtendClass();
$extended->displayVar();
?>

Output av ovanstående exempel:

Utökad klass
ett standardvärde

Regler för signaturkompatibilitet

När man åsidosätter en metod måste dess signatur vara kompatibel med basklassens metod. Annars genereras ett fatalt fel, eller före PHP 8.0.0, ett fel på E_WARNING-nivå. En signatur är kompatibel om den respekterar variationsreglerna, gör en obligatorisk parameter valfri, lägger endast till valfria nya parametrar och inte begränsar utan endast slappnar av synligheten. Detta är känt som Liskov Substitution Principle, eller LSP för korthetens skull. Konstruktorn och privata metoder är undantagna från dessa regler för signaturkompatibilitet och kommer därför inte att utlösa ett fatalt fel vid en signaturmismatch.

Exempel #11 Kompatibla barnmetoder

<?php

class Base
{
    public function foo(int $a) {
        echo "Giltig\n";
    }
}

class Extend1 extends Base
{
    function foo(int $a = 5)
    {
        parent::foo($a);
    }
}

class Extend2 extends Base
{
    function foo(int $a, $b = 5)
    {
        parent::foo($a);
    }
}

$extended1 = new Extend1();
$extended1->foo();
$extended2 = new Extend2();
$extended2->foo(1);
?>

Output av ovanstående exempel:

Giltig
Giltig

Följande exempel visar att en barnmetod som tar bort en parameter eller gör en valfri parameter obligatorisk inte är kompatibel med basklassens metod.

Exempel #12 Fatalt fel när en barnmetod tar bort en parameter

<?php

class Base
{
    public function foo(int $a = 5) {
        echo "Giltig\n";
    }
}

class Extend extends Base
{
    function foo()
    {
        parent::foo(1);
    }
}
?>

Output av ovanstående exempel i PHP 8:

Fatalt fel: Deklaration av Extend::foo() måste vara kompatibel med Base::foo(int $a = 5) i /in/evtlq på rad 13

Exempel #13 Fatalt fel när en barnmetod gör en valfri parameter obligatorisk

<?php

class Base
{
    public function foo(int $a = 5) {
        echo "Giltig\n";
    }
}

class Extend extends Base
{
    function foo(int $a)
    {
        parent::foo($a);
    }
}
?>

Output av ovanstående exempel i PHP 8:

Fatalt fel: Deklaration av Extend::foo(int $a) måste vara kompatibel med Base::foo(int $a = 5) i /in/qJXVC på rad 13

Varning: Att byta namn på en metods parameter i en barnklass är inte en signaturkompatibilitet. Men detta avråds eftersom det kommer att resultera i ett runtime-fel om namngivna argument används.

Exempel #14 Fel vid användning av namngivna argument och parametrar har bytt namn i en barnklass

<?php

class A {
    public function test($foo, $bar) {}
}

class B extends A {
    public function test($a, $b) {}
}

$obj = new B;

// Skicka parametrar enligt A::test() kontrakt
$obj->test(foo: "foo", bar: "bar"); // FEL!
?>

Output av ovanstående exempel:

Fatalt fel: Uncaught Error: Okänt namngivet argument $foo i /in/XaaeN:14
Stack trace:
#0 {main}
  thrown in /in/XaaeN på rad 14

::class

Nyckelordet `class` används också för klassnamnsupplösning. För att erhålla det fullt kvalificerade namnet på en klass används `ClassName::class`. Detta är särskilt användbart med namnrymdsklasser.

Exempel #15 Klassnamnsupplösning

<?php
namespace NS {
    class ClassName {
    }
    
    echo ClassName::class;
}
?>

Output av ovanstående exempel:

NS\ClassName

Observera: Klassnamnsupplösning med `::class` är en kompileringstidsomvandling. Det betyder att när klassnamnsträngen skapas har ingen autoloading skett ännu. Som en konsekvens utökas klassnamn även om klassen inte existerar. Inget fel ges i det fallet.

Exempel #16 Saknad klassnamnsupplösning

<?php
print Does\Not\Exist::class;
?>

Output av ovanstående exempel:

Does\Not\Exist

Från och med PHP 8.0.0 kan `::class` även användas på objekt. Denna upplösning sker vid runtime, inte vid kompileringstid. Dess effekt är densamma som att anropa `get_class()` på objektet.

Exempel #17 Objektupplösning

<?php
namespace NS {
    class ClassName {
    }
}
$c = new ClassName();
print $c::class;
?>

Output av ovanstående exempel:

NS\ClassName

Nullsäkra metoder och egenskaper

Från och med PHP 8.0.0 kan egenskaper och metoder även nås med den "nullsäkra" operatorn `?->`. Den nullsäkra operatorn fungerar på samma sätt som egenskaps- eller metodåtkomst som ovan, förutom att om objektet som ska avrefereras är `null`, kommer `null` att returneras istället för att ett undantag kastas. Om avreferensen är en del av en kedja, hoppas resten av kedjan över.

Effekten är liknande att omsluta varje åtkomst med en `is_null()`-kontroll först, men mer kompakt.

Exempel #18 Nullsäkra operatorn

<?php

// Från och med PHP 8.0.0, är denna rad:
$result = $repository?->getUser(5)?->name;

// Likvärdig med följande kodblock:
if (is_null($repository)) {
    $result = null;
} else {
    $user = $repository->getUser(5);
    if (is_null($user)) {
        $result = null;
    } else {
        $result = $user->name;
    }
}
?>

Observera: Den nullsäkra operatorn används bäst när null anses vara ett giltigt och förväntat möjligt värde för en egenskap eller metods returvärde. För att indikera ett fel är det att föredra att kasta ett undantag.

Sidslut

Orginalhemsidan på Engelska :
PHP
Språkreferens
Språkreferens#Klasser_och_Objekt