Gépelt tömbök létrehozásának megközelítései a PHP-ben – CloudSavvy IT

Avatar admin | 2021.03.11. 7 Views 0 Likes 0 Ratings

7 Views 0 Ratings Rate it

PHP logó

A PHP nem engedi definiálni a tipizált tömböket. Bármely tömb bármilyen értéket tartalmazhat, ami bonyolulttá teszi a kodabázis konzisztenciájának kikényszerítését. Íme néhány megoldás, amelyek segítségével a meglévő PHP-szolgáltatások segítségével gépelhet objektumokat.

A probléma azonosítása

A PHP tömbök nagyon rugalmas adatstruktúra. Bármelyet hozzáadhat egy tömbhöz, a skaláris értékektől az összetett objektumokig:

$arr = [
    "foobar",
    123,
    new DateTimeImmutable()
];

A gyakorlatban ritka, hogy valóban egy ilyen változatos értéktartományú tömböt szeretne. Valószínűbb, hogy a tömbök több, azonos típusú értékű példányt tartalmaznak.

$times = [
    new DateTimeImmutable(),
    new DateTimeImmutable(),
    new DateTimeImmutable()
];

Ezután létrehozhat egy metódust, amely a tömb összes értékére hat:

final class Stopwatch {
 
    protected array $laps = [];
 
    public function recordLaps(array $times) : void {
        foreach ($times as $time) {
            $this -> laps[] = $time -> getTimestamp();
        }
    }
 
}

Ez a kód a DateTimeInterface példányok ban ben $times. Ezután az idő Unix időbélyeg-ábrázolása (egész számban mért másodperc) tárolásra kerül $laps.

A baj ezzel a kóddal az, hogy egy feltevés hogy $times teljes egészében a következőkből áll: DateTimeInterface példányok. Semmi sem garantálja, hogy ez a helyzet, így a hívó továbbra is átadhat egy vegyes értékű tömböt. Ha az egyik érték nem valósult meg DateTimeInterface, a hívás getTimestamp() törvénytelen lenne, és futásidejű hiba lépne fel.

$stopwatch = new Stopwatch();
 
// OK
$stopwatch -> recordLaps([
    new DateTimeImmutable(),
    new DateTimeImmutable()
]);
 
// Crash!
$stopwatch -> recordLaps([
    new DateTimeImmutable(),
    123     // can't call `getTimestamp()` on an integer!
]);

Típuskonzisztencia hozzáadása a változatos argumentumokkal

Ideális esetben a kérdést úgy oldanák meg, hogy meghatározzák, hogy a $times tömb csak tartalmazhat DateTimeInterface példányok. Mivel a PHP nem támogatja a tipizált tömböket, helyette alternatív nyelvi jellemzőket kell keresnünk.

Az első lehetőség a variadikus argumentumok használata és a $times tömb, mielőtt átadnák neki recordLaps(). A Variadic argumentumok lehetővé teszik a függvény számára, hogy ismeretlen számú argumentumot fogadjon el, amelyeket aztán egyetlen tömbként tesznek elérhetővé. Ami a felhasználási esetünket illeti, a szokásos módon tipizálhat variadikus argumentumokat. Minden átadott argumentumnak a megadott típusúnak kell lennie.

Variadikus argumentumokat szoktak használni a matematikai függvényeknél. Íme egy egyszerű példa, amely összefoglalja az összes argumentumot:

function sumAll(int ...$numbers) {
    return array_sum($numbers);
}
 
echo sumAll(1, 2, 3, 4, 5);     // emits 15

sumAll() nem ad át tömböt. Ehelyett több argumentumot kap, amelyeket a PHP egyesít a $numbers sor. A int A typehint azt jelenti, hogy minden értéknek egész számnak kell lennie. Ez garanciát jelent arra $numbers csak egész számokból áll. Ezt most alkalmazhatjuk a stopperóra példájára:

final class Stopwatch {
 
    protected array $laps = [];
 
    public function recordLaps(DateTimeInterface ...$times) : void {
        foreach ($times as $time) {
            $this -> laps[] = $time -> getTimestamp();
        }
    }
 
}
 
$stopwatch = new Stopwatch();
 
$stopwatch -> recordLaps(
    new DateTimeImmutable(),
    new DateTimeImmutable()
);

Nem támogatott típusokat továbbítani már nem lehet recordLaps(). Az erre irányuló próbálkozások jóval korábban, a getTimestamp() megkísérli a hívást.

Ha már van egy sor alkalom, hogy átmehessen recordLaps(), ki kell csomagolnia a splat operátor (...), amikor meghívja a módszert. Ha megpróbálja átadni közvetlenül, akkor nem sikerül – a változatok egyikének kell tekinteni, amelyeknek kötelezőnek kell lenniük int és nem egy array.

$times = [
    new DateTimeImmutable(),
    new DateTimeImmutable()
];
 
$stopwatch -> recordLaps(...$times);

A változatos érvek korlátai

A Variadic argumentumok nagy segítséget jelenthetnek, ha egy elemtömböt át kell adnia egy függvénynek. Van azonban néhány korlátozás a felhasználásuk módjára vonatkozóan.

A legfontosabb korlátozás az, hogy függvényenként csak egy variadikus argumentumkészlet használható. Ez azt jelenti, hogy minden funkció csak egy „tipizált” tömböt fogadhat el. Ezenkívül a variadikus argumentumot minden szabályos argumentum után utoljára kell meghatározni.

function variadic(string $something, DateTimeInterface ...$times);

A variadikus argumentumok természetüknél fogva csak függvényekkel használhatók. Ez azt jelenti, hogy nem tudnak segíteni, amikor szükség van rá bolt egy tömb tulajdonságként, vagy adja vissza egy függvényből. Láthatjuk ezt a stopper kódban – a Stopwatch osztálynak van egy laps tömb, amely csak egész időbélyegek tárolására szolgál. Jelenleg nem tudjuk ezt érvényesíteni.

Gyűjtemény osztályok

Ilyen körülmények között más megközelítést kell választani. Az egyik módja annak, hogy valami hasonlót hozzon létre egy „tipizált tömbhöz” a userland PHP-ben, egy dedikált gyűjtemény osztály írása:

final class User {
 
    protected string $Email;
 
    public function getEmail() : string {
        return $this -> Email;
    }
 
}
 
final class UserCollection implements IteratorAggregate {
 
    private array $Users;
 
 
    public function __construct(User ...$Users) {
        $this -> Users = $Users;
    }
 
    public function getIterator() : ArrayIterator {
        return new ArrayIterator($this -> Users);
    }
 
}

A UserCollection osztály mostantól bárhol használható, amire általában egy tömbre számíthat User példányok. UserCollection variadikus argumentumokat használ a sorok elfogadásához User példányai a konstruktorában. Habár a $Users tulajdonságot általánosként kell gépírni array, garantáltan teljes egészében felhasználói példányokból áll, mivel csak a konstruktorban írják.

Csábítónak tűnhet a get() : array módszer, amely a gyűjtemény összes elemét feltárja. Ezt el kell kerülni, mivel ez visszavezet minket a homályba array typepint probléma. Ehelyett a gyűjtemény iterálhatóvá tette hogy a fogyasztók a foreach hurok. Ily módon sikerült létrehoznunk egy tipet-képes „tömböt”, amelyről kódunk biztonságosan feltételezheti, hogy csak felhasználókat tartalmaz.

function sendMailToUsers(UserCollection $Users) : void {
    foreach ($Users as $User) {
        mail($user -> getEmail(), "Test Email", "Hello World!");
    }
}
 
$users = new UserCollection(new User(), new User());
sendMailToUsers($users);

A gyűjtemények tömbszerűbbé tétele

A gyűjtési osztályok megoldják a gépírás problémáját, de azt jelentik, hogy elveszíti a hasznos tömbök funkcionalitása. Beépített PHP funkciók, mint count() és isset() nem fog működni az egyéni gyűjtemény osztályával.

Ezen funkciók támogatása további beépített interfészek megvalósításával adható hozzá. Ha megvalósítja Countable, az osztályod használható lesz count():

final class UserCollection implements Countable, IteratorAggregate {
 
    private array $Users;
 
 
    public function __construct(User ...$Users) {
        $this -> Users = $Users;
    }
 
    public function count() : int {
        return count($this -> Users);
    }
 
    public function getIterator() : ArrayIterator {
        return new ArrayIterator($this -> Users);
    }
 
}
 
$users = new UserCollection(new User(), new User());
echo count($users);     // 2

Végrehajtás ArrayAccess lehetővé teszi a tömbszintaxis segítségével a gyűjteménye elemeinek elérését. Ez lehetővé teszi a isset() és unset() funkciókat. Négy módszert kell alkalmaznia, hogy a PHP kölcsönhatásba léphessen az elemeivel.

final class UserCollection implements ArrayAccess, IteratorAggregate {
 
    private array $Users;
 
 
    public function __construct(User ...$Users) {
        $this -> Users = $Users;
    }
 
    public function offsetExists(mixed $offset) : bool {
        return isset($this -> Users[$offset]);
    }
 
    public function offsetGet(mixed $offset) : User {
        return $this -> Users[$offset];
    }
 
    public function offsetSet(mixed $offset, mixed $value) : void {
        if ($value instanceof User) {
            $this -> Users[$offset] = $value;
        }
        else throw new TypeError("Not a user!");
    }
 
    public function offsetUnset(mixed $offset) : void {
        unset($this -> Users[$offset]);
    }
 
    public function getIterator() : ArrayIterator {
        return new ArrayIterator($this -> Users);
    }
 
}
 
$users = new UserCollection(
    new User("example@example.com"),
    new User("hello@world.com")
);
 
echo $users[1] -> getEmail();   // hello@world.com
var_dump(isset($users[2]));     // false

Most van egy osztálya, amely csak tartalmazhat User példányok, és amely szintén tömbnek tűnik és érződik. Egy megjegyzendő pont ArrayAccess az a offsetSet megvalósítás – mint $value kell, hogy legyen mixed, ez lehetővé teheti inkompatibilis értékek hozzáadását a gyűjteményéhez. Kifejezetten ellenőrizzük az átadott típusát $value ennek megakadályozására.

Következtetés

Friss PHP kiadások a nyelvet erősebb gépelés és nagyobb konzisztencia felé fejlesztették. Ez még nem terjed ki a tömb elemekre. Gépírás ellen array gyakran túl nyugodt, de megkerülheti a korlátozásokat saját gyűjtőosztályainak felépítésével.

Variadikus argumentumokkal kombinálva a gyűjteményminta életképes módszer az összesített értékek típusainak kikényszerítésére a kódban. Gépelheti géppel a gyűjteményeit, és megismételheti azokat, tudván, hogy csak egy típusú érték lesz jelen.


Source link


7 Views 0 Ratings Rate it

  • Minden jog fenntartva 2019-2020. Hogyankészítsek.Hu Impresszum Adatkezelési szabályok