Zoeken…


Wat is Type Jongleren?

PHP is een los getypte taal. Dit betekent dat het standaard geen operanden vereist in een expressie van hetzelfde (of compatibele) type. U kunt bijvoorbeeld een nummer toevoegen aan een string en verwachten dat het werkt.

var_dump ("This is example number " . 1);

De output zal zijn:

string (24) "Dit is voorbeeldnummer 1"

PHP bereikt dit door automatisch incompatibele variabeletypen in types te gieten waarmee de gevraagde bewerking kan plaatsvinden. In het bovenstaande geval wordt het gehele getal 1 in een string gegoten, wat betekent dat het kan worden samengevoegd op de voorgaande letterlijke string. Dit wordt type jongleren genoemd. Dit is een zeer krachtige functie van PHP, maar het is ook een functie die je tot veel haartrekken kan leiden als je je er niet van bewust bent, en zelfs tot beveiligingsproblemen kan leiden.

Stel je de volgende situatie voor:

if (1 == $variable) {
    // do something
}

De bedoeling lijkt te zijn dat de programmeur controleert of een variabele een waarde van 1 heeft. Maar wat gebeurt er als $ variabele in plaats daarvan een "anderhalf" heeft? Het antwoord zal je misschien verbazen.

$variable = "1 and a half";
var_dump (1 == $variable);

Het resultaat is:

bool (true)

Waarom is dit gebeurd? Het is omdat PHP zich realiseerde dat de tekenreeks "1 en een half" geen geheel getal is, maar dit moet het zijn om het te vergelijken met geheel getal 1. In plaats van te falen initieert PHP het type jongleren en probeert het de variabele om te zetten in een geheel getal. Dit wordt gedaan door alle tekens aan het begin van de tekenreeks die naar het gehele getal kunnen worden gegoten, te nemen en ze te casten. Het stopt zodra het een personage tegenkomt dat niet als een getal kan worden behandeld. Daarom wordt "1 en een half" gegoten naar geheel getal 1.

Toegegeven, dit is een zeer gekunsteld voorbeeld, maar het dient om het probleem aan te tonen. De volgende voorbeelden zullen enkele gevallen behandelen waarin ik fouten tegenkwam die werden veroorzaakt door jongleren met typen die in echte software gebeurde.

Uit een bestand lezen

Bij het lezen van een bestand willen we weten wanneer we het einde van dat bestand hebben bereikt. Wetende dat fgets() false retourneert aan het einde van het bestand, kunnen we dit gebruiken als voorwaarde voor een lus. Als de gegevens die bij de laatste read zijn geretourneerd echter toevallig iets zijn dat als boolean false wordt geëvalueerd, kan dit ervoor zorgen dat onze leeslus voor bestanden voortijdig wordt beëindigd.

$handle = fopen ("/path/to/my/file", "r");

if ($handle === false) {
    throw new Exception ("Failed to open file for reading");
}

while ($data = fgets($handle)) {
    echo ("Current file line is $data\n");
}

fclose ($handle);

Als het te lezen bestand een lege regel bevat, wordt de while lus op dat punt beëindigd, omdat de lege tekenreeks als boolean false geëvalueerd.

In plaats daarvan kunnen we expliciet controleren op de booleaanse false waarde met behulp van strikte gelijkheidsexploitanten :

while (($data = fgets($handle)) !== false) {
    echo ("Current file line is $data\n");
}

Merk op dat dit een gekunsteld voorbeeld is; in het echte leven zouden we de volgende lus gebruiken:

while (!feof($handle)) {
    $data = fgets($handle);
    echo ("Current file line is $data\n");
}

Of vervang het hele ding door:

$filedata = file("/path/to/my/file");
foreach ($filedata as $data) {
    echo ("Current file line is $data\n");
}

Verander verrassingen

Switch-verklaringen gebruiken een niet-strikte vergelijking om overeenkomsten te bepalen. Dit kan tot vervelende verrassingen leiden . Overweeg bijvoorbeeld de volgende verklaring:

switch ($name) {
    case 'input 1':
        $mode = 'output_1';
        break;
    case 'input 2':
        $mode = 'output_2';
        break;
    default:
        $mode = 'unknown';
        break;
}

Dit is een zeer eenvoudige verklaring en werkt zoals verwacht wanneer $name een string is, maar kan anders problemen veroorzaken. Als $name bijvoorbeeld geheel getal 0 , gebeurt er tijdens het vergelijken een type-jongleren. Het is echter de letterlijke waarde in de case-instructie die jongleert, niet de voorwaarde in de switch-instructie. De string "input 1" wordt geconverteerd naar geheel getal 0 dat overeenkomt met de invoerwaarde van geheel getal 0 . Het resultaat hiervan is dat als u een waarde van geheel getal 0 opgeeft, het eerste geval altijd wordt uitgevoerd.

Er zijn een paar oplossingen voor dit probleem:

Expliciete casting

De waarde kan voorafgaand aan vergelijking worden typecast naar een string:

switch ((string)$name) {
...
}

Of een functie waarvan bekend is dat deze een string retourneert, kan ook worden gebruikt:

switch (strval($name)) {
...
}

Beide methoden zorgen ervoor dat de waarde van hetzelfde type is als de waarde in de case statements.

Vermijd switch

Het gebruik van een if instructie geeft ons controle over hoe de vergelijking wordt uitgevoerd, waardoor we strikte vergelijkingsoperatoren kunnen gebruiken :

if ($name === "input 1") {
    $mode = "output_1";
} elseif ($name === "input 2") {
    $mode = "output_2";
} else {
    $mode = "unknown";
}

Strikt typen

Sinds PHP 7.0 kunnen sommige van de schadelijke effecten van jongleren met typen worden beperkt door strikt te typen . Door deze declare instructie op te nemen als de eerste regel van het bestand, zal PHP parametertype-verklaringen afdwingen en type-declaraties retourneren door een TypeError uitzondering te TypeError .

declare(strict_types=1);

Deze code zal bijvoorbeeld, met gebruik van parametertype-definities, een vangbare uitzondering van het type TypeError bij uitvoering:

<?php
declare(strict_types=1);

function sum(int $a, int $b) {
    return $a + $b;
}

echo sum("1", 2);

Evenzo gebruikt deze code een aangifte van het retourtype; het zal ook een uitzondering genereren als het iets anders dan een geheel getal probeert te retourneren:

<?php
declare(strict_types=1);

function returner($a): int {
    return $a;
}

returner("this is a string");


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow