Statische klassen uitbreiden in PHP - Vermijd het delen van variabelen voor meerdere klassen in de uitgebreide klas

Ik schijn problemen te hebben met het uitbreiden van statische klassen in PHP.

PHP-code:

<?php
    class InstanceModule {
        public static $className = 'None';
        public static function PrintClassName() {
            echo self::$className . ' (' . __CLASS__ . ')
'; } } class A extends InstanceModule { public static function Construct() { self::$className = "A"; } } class B extends InstanceModule { public static function Construct() { self::$className = "B"; } } ?>

Mijn belcode en wat ik zou verwachten:

<?php
    //PHP Version 5.3.14

    A::PrintClassName(); //Expected 'None' - actual result: 'None'
    B::PrintClassName(); //Expected 'None' - actual result: 'None'

    A::Construct();

    A::PrintClassName(); //Expected 'A' - actual result: 'A'
    B::PrintClassName(); //Expected 'None' - actual result: 'A'

    B::Construct();

    A::PrintClassName(); //Expected 'A' - actual result: 'B'
    B::PrintClassName(); //Expected 'B' - actual result: 'B'

    A::Construct();

    A::PrintClassName(); //Expected 'A' - actual result: 'A'
    B::PrintClassName(); //Expected 'B' - actual result: 'A'
?>

Werkelijke volledige output:

None (InstanceModule)
None (InstanceModule)
A (InstanceModule)
A (InstanceModule)
B (InstanceModule)
B (InstanceModule)
A (InstanceModule)
A (InstanceModule)

Dus wat hier gebeurt (van wat het lijkt) is dat zodra ik self :: $ className op een van de verlengende klassen instelt, deze de variabele uit de andere klasse overschrijft. Ik neem aan dat dit komt omdat ik statische klassen gebruik en er kan slechts één InstanceModule -klasse zijn in plaats van deze te kopiëren naar zowel A als B , net als mijn eerdere begrip van breidt . Ik heb in plaats daarvan het trefwoord static :: $ className geprobeerd, maar het lijkt geen verschil te maken.

Het zou mooi zijn als iemand me kon begeleiden in de goede richting van wat ik hier verkeerd doe en wat ik moet doen om dit probleem op te lossen.

Edit: To clarify, this code does what I want, but is obviously a horrible workaround since it would ruin the whole idea of extending and reusing functions:

<?php
    class A {
        public static $className = 'None';
        public static function PrintClassName() {
            echo self::$className . ' (' . __CLASS__ . ')
'; } public static function Construct() { self::$className = "A"; } } class B { public static $className = 'None'; public static function PrintClassName() { echo self::$className . ' (' . __CLASS__ . ')
'; } public static function Construct() { self::$className = "B"; } } ?>
0

3 antwoord

Omdat $ className statisch is en binnen de bovenliggende klasse, wanneer u className in A of B instelt, wordt de variabele binnen het bovenliggende element gewijzigd en hetzelfde wordt gedaan wanneer de variabele wordt gelezen. Tenzij u className in uw uitgebreide klassen overschrijft, slaat u informatie op van dezelfde geheugenlocatie en wordt deze opgehaald, oorspronkelijk gedefinieerd in ExemplaarModule.

Als u className opnieuw definieert in A/B, hebt u toegang tot className met parent :: of self :: van respectievelijk ExemplaarModule of A/B. Afhankelijk van wat u probeert te doen, kunnen abstracte klassen ook een belangrijke rol spelen.

Zie Static Keyword of Class Abstraction in de PHP5-handleiding.

2
toegevoegd
Klasse-abstractie lijkt voor mij geen verschil te maken (hoewel ik het misschien verkeerd doe). Zoals vermeld in het OP, heb ik al geprobeerd het sleutelwoord static te gebruiken, en ten slotte resulteerde het opnieuw definiëren van className in beide verlengende klassen ( public static $ className = 'None') feitelijk in PrintClassName() `om de hele tijd Geen uit te voeren, ook al wordt zelf :: $ className nog steeds gedefinieerd.
toegevoegd de auteur h2ooooooo, de bron

Ik denk dat het beste antwoord op jouw situatie is om de te gebruiken get_called_class() functie in plaats van je huidige $ className variabele, die de late static binding class naam retourneert in plaats van __ CLASS __ of get_class() die alleen de huidige klassenaam retourneert.

Als u de functie PrintClassName() hebt gewijzigd om alleen uit te voeren wat get_called_class() retourneerde, zou uw uitvoer het volgende zijn. Nu zou je gewoon een standaardwaarde moeten opnemen, die natuurlijk over de klassen wordt gedeeld, dus je zou die vlag in beide klassen moeten hebben als je door gaat met het gebruik van statische methoden.

A
B
A
B
A
B
A
B
0
toegevoegd
Natuurlijk zijn de A en B slechts een vorm van laten zien wat er aan de hand was. In mijn huidige scenario behandel ik verschillende uitbreidingen Module , maar deze hebben verschillende functies (en hun algemene functies bevinden zich in ExemplaarModule ). Dit betekent dat wanneer ik klasse B construeer, en probeer toegang te krijgen tot A :: FunctionInClass ik een fout krijg, aangezien deze functie niet bestaat (omdat het eigenlijk alleen maar is A :: FunctionInBClass op dit moment, ook al werd die functie gedeclareerd in B en niet A (maar A werd overschreven door B net zoals in mijn OP-voorbeeld).
toegevoegd de auteur h2ooooooo, de bron
Feit is dat het eigenlijk mijn poging is om een ​​meer globale manier te vinden om lessen (en instanties) aan te roepen. Natuurlijk zou ik $ db = DB :: GetInstance() kunnen aanroepen; $ db-> Connect (); , maar wat ik heb geprobeerd te doen in mijn code is een omslag die eenvoudig de huidige instantie gebruikt, en wanneer u vervolgens DB :: Connect , het doet dit spul op de achtergrond. Vandaar dit probleem komt. Ik denk dat ik moet besluiten om $ GLOBALS (bah.) Te gebruiken of de instantie te moeten krijgen wanneer ik de klas moet bellen.
toegevoegd de auteur h2ooooooo, de bron
Het ziet er echt naar uit dat u deze klassen beter kunt wijzigen in statisch, zodat u objecten kunt instantiëren, waardoor u de overerving krijgt waarnaar u op zoek bent.
toegevoegd de auteur nickb, de bron
U moet zowel de afhankelijkheidsinjectie als de fabrieksontwerppatronen bekijken. Ofwel zou je kunnen helpen met de laatste (instanties krijgen wanneer je de klas moet bellen).
toegevoegd de auteur nickb, de bron

Ik zou willen dat de basisklasse een repository van subklasse-instanties houdt, zodat u gegevens die tot elke klasse behoren correct kunt scheiden in plaats van die gegevens uit een statische basisklassevariabele te halen.

U kunt de magische methode __ callStatic() in de basisklasse gebruiken om belmethoden op niet-bestaande subklassen te gebruiken, zoals hieronder wordt getoond. Helaas moet de statische repositoryvariabele openbaar worden verklaard vanwege de zichtbaarheid van die magische methode.

abstract class Base
{
    public static $repo = array();

    public static function __callStatic($name, $args)
    {
        $class = get_called_class();

        if (!isset(self::$repo[$class])) {
                echo "Creating instance of $class\n";
                self::$repo[$class] = new $class();
        }

        return call_user_func_array(array(self::$repo[$class], $name), $args);
    }

        protected function PrintClassName()
        {
                echo __CLASS__, " (", get_called_class(), ")\n";
        }

        protected abstract function Construct($a);
}

class A extends Base
{
        protected function Construct($a)
        {
                echo __CLASS__, ": setting x := $a\n";
        }
}

class B extends Base
{
        protected function Construct($a)
        {
                echo __CLASS__, ": setting y := $a\n";
        }
}

A::PrintClassName();
B::PrintClassName();

A::Construct('X');
B::Construct('Y');

Output:

Creating instance of A
Base (A)
Creating instance of B
Base (B)
A: setting x := X
B: setting y := Y
0
toegevoegd