IMPRESSUM dotted_line KONTAKT dotted_line search dotted_line Contact dotted_line sitemap
80 PHP-Resource Mitglieder online


php-resource



Zurück   PHP-Scripte PHP-Tutorials PHP-Jobs und vieles mehr > Blogs > AmicaNoctis
 

Login

 
eingeloggt bleiben
star Jetzt registrieren   star Passwort vergessen
 

Bewertung: 7 Stimmen mit einer durchschnittlichen Bewertung von 5,00.

Vererbung mit eingebauten Klassen

"Vererbung mit eingebauten Klassen" bei Mister Wong speichern "Vererbung mit eingebauten Klassen" bei YiGG.de speichern "Vererbung mit eingebauten Klassen" bei Google speichern "Vererbung mit eingebauten Klassen" bei del.icio.us speichern "Vererbung mit eingebauten Klassen" bei Digg speichern "Vererbung mit eingebauten Klassen" bei icio.de speichern "Vererbung mit eingebauten Klassen" bei My Yahoo speichern
Veröffentlicht: 28-04-2011 um 07:54 von AmicaNoctis
Aktualisiert: 28-04-2011 um 14:11 von AmicaNoctis

Um das hier halbwegs vernünftig lesen zu können, kopiere diesen Code in die Adressleiste und drücke Enter:

javascript:void((function(){for(i=0,d=document.getElementsByTagName("div");i<d.length;i++)d[i].style.width=="410px"&&(d[i].style.width="810px")})())

Um in den Code-Blöcken Zeilennummern zu sehen, mach dasselbe Spiel mit diesem Code:

javascript:void((function(){var a=1,b="#888",c="br",d="code",e="span";function g(c,d){c.insertBefore(document.createElement(e).appendChild(document.createTextNode((""+(1000+a++)). substr(1)+"| ")).parentNode,d).style.color=b;}function h(a,b){return a.nodeName.toLowerCase()==b;}function i(a){if(h(a,c))g(a.parentNode,a.nextSibling);else if(a.nodeType==1)for(var b=a.firstChild;b;b=b.nextSibling)i(b);}for(var j=0,f=document.getElementsByTagName(d);j<f.length;j++){var k=f[j];a=1;if(h(k.parentNode,d)){g(k,k.firstChild);i(k);}}})());



Die in PHP eingebauten Klassen sind schon oft hilfreich. Ja, sogar meistens. Aber manchmal reichen sie nicht aus und man braucht zusätzliche oder leicht veränderte Funktionalität.

Als OOP-Fans bedienen wir uns der Vererbung und fügen die gewünschten Features in der abgeleiteten Klasse hinzu. Das klappt oft prima. Ja, sogar meistens. Aber manchmal, nur manchmal, ist PHP ein Drecksack und verhält sich total unlogisch, weil irgendwelche verkorksten Interna anders funktionieren als man denkt, ja sogar anders als sie sollten.

Nein, ich will nicht nur Frust ablassen, sondern auch eine mögliche Lösung vorstellen, aber dazu später mehr. Erstmal kommt ein Beispiel:

Für ein bestimmtes Projekt brauchen wir präzise Zeitangaben mit Millisekunden. Die entsprechende Ableitung von DateTime namens PreciseDateTime haben wir bereits fertig und klappt soweit. Beim Addieren und Subtrahieren von Zeiten sind wir aber auf DateInterval beschränkt und diese Klasse arbeitet wiederum ohne Millisekunden-Anteil. Da das bisher alles so schön geklappt hat, schreiben wir auch hier eine abgeleitete Klasse PreciseDateInterval.

Wie die Elternklasse DateInterval selbst nimmt der Konstruktor einen auf den ersten Blick merkwürdig anmutenden String entgegen, in dem die Zeitspanne definiert wird: P8Y7M6DT5H43M21S
Das steht für Period: 8 Years 7 Months 6 Days Time part: 5 Hours 43 Minutes 21 Seconds. Die Reihenfolge ist zwar festgelegt, aber alle Bestandteile außer dem P sind optional. Wenn aber eine Zeitskomponente angegeben wird, muss das T davor stehen. Nur so kann man 3 Monate (P3M) von 3 Minuten (PT3M) unterscheiden.

Da wir ja mit Millisekunden arbeiten wollen, fügen wir noch das X ein, um z. B. 4 Minuten 3 Sekunden und 210 Millisekunden angeben zu können: PT4M3S210X
Also ran an die Arbeit und schnell was zusammengehackt, was schon recht gut aussieht. (Bitte die Quellcode-Kommentare lesen!)

PHP-Code:
class PreciseDateInterval extends DateInterval {
    public 
$ms 0;
    public function 
__construct ($pIntervalSpec) {
        
// pIntervalSpec fängt mit einem P an, dann kommen die optionalen Bestandteile
        // in genau dieser Reihenfolge:
        // ..Y Anzahl Jahre
        // ..M Anzahl Monate
        // ..W Anzahl Wochen
        // ..D Anzahl Tage (wenn W angegeben ist wird D einfach ignoriert)
        // T   Trennzeichen - ist Pflicht, wenn eine Zeitkomponente vorhanden ist
        // ..H Anzahl Stunden
        // ..M Anzahl Minuten
        // ..S Anzahl Sekunden
        // ..X Anzahl Millisekunden (haben wir hinzuerfunden)
        
        // Das ganze muss jetzt aufgedröselt werden, denn der Elternkonstruktor würde über
        // den X-Teil meckern aber wir brauchen ihn, um die zusätzliche eigene Eigenschaft $ms
        // zu setzen.
        //                                                _______________________________
        //                                               /5                       ______ \
        //               _____   _____   _____   _____  / _____   _____   _____  |9____ \ \
        // Subpatterns: /1    \ /2    \ /3    \ /4    \/ /6    \ /7    \ /8    \ |/10  \ \ \
        
$pattern =    "P(\\d+Y)?(\\d+M)?(\\d+W)?(\\d+D)(T(\\d+H)?(\\d+M)?(\\d+S)?((\\d+)X)?)?";

        
// neue intervalSpec für den Elternkonstruktor zusammenbauen
        
$spec "P";
        if (
preg_match("<^" $pattern "$>"$pIntervalSpec$matches)) {
            foreach (
$matches as $i => $match) {
                if (
$i == 5) {
                    
// die 5 umfasst alle folgenden, also auch den X-Teil,
                    // daher nehmen wir nur das T mit und grasen 6 bis 8 einzeln ab
                    
$spec .= "T";
                }
                else if (
$i && $i 9) {
                    
// den gesamten Ausdruck (0) und den X-Teil (9 und 10) ausschließen
                    // die anderen (außer der 5) werden rüberkopiert
                    
$spec .= $match;
                }
                else if (
$i == 10) {
                    
// wir greifen uns den X-Teil, aber dazu kommen wir später
                    // $this->ms = (int) $match;
                
}
            }
        }
        else {
            
// Regex hat nicht gematcht => pIntervalSpec ungültig
            // soll sich der Elternkonstruktor damit rumschlagen
            
$spec $pIntervalSpec;
        }
        
        
parent::__construct($spec);
    }
    public function 
format ($pFormat) {
        
// hier passiert die Magie, dass neben den eingebauten Tokens für die Ausgabe
        // %Y, %M, %D, %H, %I, %S, %R, 
        // %y, %m, %d, %h, %i, %s, %r und %a
        // jetzt auch %X unterstützt wird, um an die Millisekunden ranzukommen
        
$result "";
        
$isLiteral false;
        foreach (
explode("X"$pFormat) as $k => $v) {
            
$vt rtrim($v"%");
            
$percents strlen($v) - strlen($vt);
            if (
$k) {
                
$result .= $isLiteral "X" str_pad($this->ms30STR_PAD_LEFT);
            }
            
$result .= parent::format($vt) . str_repeat("%"floor($percents 2));
            
$isLiteral = !($percents 2);
        }
        return 
$result;
    }

Wir legen los mit einem kleinen Testlauf

PHP-Code:
header("Content-Type: text/plain; charset=utf-8");
print_r(new PreciseDateInterval("P8Y7M6DT5H43M21S987X")); 
und erhalten schonmal das hier:
Code:
PreciseDateInterval Object
(
    [ms] => 0
    [y] => 8
    [m] => 7
    [d] => 6
    [h] => 5
    [i] => 43
    [s] => 21
    [invert] => 0
    [days] => 
)
Da wir Zeile 41 noch auskommentiert haben, ist es kein Wunder, dass der Millisekunden-Anteil noch auf 0 steht. Also den Kommentar schnell wegnehmen und...

Zitat:
Fatal error: PreciseDateInterval::__construct(): Unknown property (ms) in ... on line 41
Was soll das denn? Die ist sogar public! Wieso können wir nicht darauf zugreifen?

DateInterval ist eine von diesen Klassen, mit denen man keine anständige Vererbung hinbekommt, weil intern das OOP nur trickreich vorgegaukelt wird, die Klasse aber gar kein OOP kann. Der gleiche Betrug wie beim Zitronenfalter.





Wie lösen wir das Problem? Mit Delegation. Dabei speichern wir eine Instanz in einer privaten Eigenschaft und reichen Methodenaufrufe an diese durch. Das kann man durch die magische __call-Methode natürlich stark vereinfachen, anstatt für jede Methode eine eigene zu schreiben, die diese aufruft. Für Eigenschaften gilt dasselbe: Mittels __get und __set delegieren wir das an die gespeicherte Instanz.

PHP-Code:
class PreciseDateInterval {
    private 
$interval null;
    public  
$ms 0;
    public function 
__construct ($pIntervalSpec) {
        
$pattern =    "P(\\d+Y)?(\\d+M)?(\\d+W)?(\\d+D)(T(\\d+H)?(\\d+M)?(\\d+S)?((\\d+)X)?)?";
        
$spec "P";
        if (
preg_match("<^" $pattern "$>"$pIntervalSpec$matches)) {
            foreach (
$matches as $i => $match) {
                if (
$i == 5) {
                    
$spec .= "T";
                }
                else if (
$i && $i 9) {
                    
$spec .= $match;
                }
                else if (
$i == 10) {
                    
$this->ms = (int) $match;
                }
            }
        }
        else {
            
$spec $pIntervalSpec;
        }
        
// bis hierher alles wie gehabt, aber statt des Elternkonstruktors erzeugen und
        // speichern wir eine Instanz
        
$this->interval = new DateInterval($spec);
    }
    
    
// mit magischen Gettern und Settern kann man dann alle abgerufenen Eigenschaften an die
    // gespeicherte Instanz durchreichen. __call brauchen wir nicht, da die einzige Methode
    // format() ist und wir die selbst umimplementieren müssen.
    
public function __get ($pName) {
        return 
$this->interval->$pName;
    }
    public function 
__set ($pName$pValue) {
        
$this->interval->$pName $pValue;
    }
    
    public function 
format ($pFormat) {
        
$result "";
        
$isLiteral false;
        foreach (
explode("X"$pFormat) as $k => $v) {
            
$vt rtrim($v"%");
            
$percents strlen($v) - strlen($vt);
            if (
$k$result .= $isLiteral "X" str_pad($this->ms30STR_PAD_LEFT);
            
$result .= $this->interval->format($vt) . str_repeat("%"floor($percents 2));
            
$isLiteral = !($percents 2);
        }
        return 
$result;
    }

Also dann, ein neuer Versuch:

PHP-Code:
header("Content-Type: text/plain; charset=utf-8");
$pdi = new PreciseDateInterval("P8Y7M6DT5H43M21S987X");
$pdi->1;
echo 
$pdi->s","$pdi->ms"\n";
echo 
$pdi->format("%M:%S.%X"); 
Wir erhalten erwartungsgemäß

Code:
21,987
01:21.987
also hat das mit dem Delegieren geklappt. Beim Setzen der Minuten auf 1 wurde es an die DateInterval-Instanz weitergegeben.

Während man in anderen Fällen durchaus trotzdem von der Delegat-Klasse erben kann, um auch bei Typehinting die Anforderungen an die Methodensignatur zu erfüllen, geht das in diesem speziellen Falle nicht. Sobald wir angeben, dass wir von DateInterval erben, können wir wieder nicht mehr auf die eigenen Membervariablen zugreifen.

Geht es aber z. B. darum, eine Ableitung von DomElement zu schreiben, um das Verhalten von firstChild, nextSibling u. s. w. zu modifizieren (was auch nur über Delegation möglich ist), dann können wir trotzdem von DomElement erben und dank DomDocument::registerNodeClass können wir das DomDocument anweisen, immer unsere Klasse zu verwenden.

Digg this Post! Add Post to del.icio.us Bookmark Post in Technorati Furl this Post!
Hits 7405 Kommentare 6 Stichworte bearbeiten Hinweis auf diesen Blog-Eintrag per E-Mail verschicken
« Zurück     Startseite des Blogs     Nächste »
Kommentare 6

Kommentare

  1. Alter Kommentar
    Here you click the author website and getting the awesome article here how do you enable cookies exploring in window 10 and chrome then you read this post.
    permalink
    Veröffentlicht: 28-06-2019 um 11:25 von peterpen peterpen ist offline
  2. Alter Kommentar
    Thank you! I'm glad to find this post. I agree with your opinion, I'm sure your ideas will be successful in the future! happy wheels
    permalink
    Veröffentlicht: 06-07-2019 um 06:11 von zindara zindara ist offline
  3. Alter Kommentar
    I love the way you write and share your niche! Very interesting and different! Keep it coming!
    [URL="https://games.lol/candy-crush-saga/"]candy crush[/URL]
    permalink
    Veröffentlicht: 06-07-2019 um 07:45 von alvinrr alvinrr ist offline
  4. Alter Kommentar

    review

    Zitat:
    Zitat von peterpen Kommentar anzeigen
    Here you click the author website and getting the awesome article here how do you enable cookies exploring in window 10 and chrome then you read this post.
    such an informative code to help the bego=inners to code , recently I work for my clients website have
    a lookBuy Movie Jacket
    permalink
    Veröffentlicht: 25-07-2019 um 15:37 von judy_mitchel judy_mitchel ist offline
  5. Alter Kommentar

    review

    Zitat:
    Zitat von peterpen Kommentar anzeigen
    Here you click the author website and getting the awesome article here how do you enable cookies exploring in window 10 and chrome then you read this post.
    such an informative code to help the bego=inners to code , recently I work for my clients website have
    a look Buy Movie Jacket
    permalink
    Veröffentlicht: 25-07-2019 um 15:38 von judy_mitchel judy_mitchel ist offline
  6. Alter Kommentar
    Thank you for sharing this great post, I am very impressed with your post, the information given is meticulous and easy to understand. I will often follow your next post. atari breakout
    permalink
    Veröffentlicht: 05-10-2019 um 07:00 von kubeo99 kubeo99 ist offline
 
Trackbacks 0

Trackbacks


Alle Zeitangaben in WEZ +2. Es ist jetzt 20:32 Uhr.



Search Engine Friendly URLs by vBSEO 3.3.0