Delphi-PRAXiS
Seite 2 von 2     12   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   PHP - sind hier "Sicherheitsexperten" an Board? (https://www.delphipraxis.net/152621-php-sind-hier-sicherheitsexperten-board.html)

Matze 4. Jul 2010 09:39

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Die sind auch nicht böse, nur verhältnismäßig langsam.
Das kommt immer auf den Anwendungsfall an.

himitsu 4. Jul 2010 09:41

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Nja, weil sich doch "beschwert" wurde, daß ich ein paar davon verwendete.
(wobei sie nichtmal oft aufgerufen wurden)

alcaeus 4. Jul 2010 09:54

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Boah. Wahnsinn. Himi, sorry, aber manchmal frag ich mich echt ob dir zu helfen ist.

RegExen sind langsam. Das gilt fuer jede Art davon. Ich zeig dir mal einen Performance-Test:
PHP-Quellcode:
$preg = '#Foobar#i';
$toMatch = 'Foobar';

$sample = 'Foobar';

$num = 100000;

$pregStart = microtime(true);
$i = 0;
while ($i < $num) {
   preg_match($preg, $sample);
   $i++;
}
$pregStop = microtime(true);

$eqStart = microtime(true);
$i = 0;
while ($i < $num) {
   ($sample == $toMatch);
   $i++;
}
$eqStop = microtime(true);

echo 'Time preg: '. ($pregStop-$pregStart) .' seconds<br />';
echo 'Time eq: '. ($eqStop-$eqStart) .' seconds<br />';
Die Ausgabe bei mir:
Zitat:

Time preg: 0.75503706932068 seconds
Time eq: 0.045335054397583 seconds
Soll heissen: vermeide preg wenn es nicht sein muss. Wenn benoetigt wird dann gibts keinen Weg drumrum. Ich habe einen Fall wo ich eine URL der Form <md5>.png in latex.php?id=<md5> umwandeln muss. Das geht nicht ohne mod_rewrite. Wenn ich es jetzt aber verwende um eine saubere URL-Struktur reinzubringen, bin ich echt selber Schuld (also sowas wie /forum/Foo-Bar/topic/Blabla/50 zu viewtopic.php?t=50 umzuschreiben) und verdien nichts besseres als eine sacklangsame Applikation.

Aber, wenn du auf solchen Sachen rumreiten willst, bitte. Du hast nach einer Meinung gefragt und sie gekriegt. Du musst halt damit leben dass sie nicht unbedingt das ist, was du dir erhofft hast.

Greetz
alcaeus

himitsu 4. Jul 2010 10:32

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Dein Vergleich hinkt aber etwas.

Wenn man das preg_match nutzt, um etwas zu suchen, dann kann man es doch nicht mit einem einfachen Vergleich (=) vergleichen?

strpos oder so wäre da wohl ein besserer Vergleichspartner.

Code:
$preg = '#Foobar#i';
$pos = 'Foobar';
$toMatch = ' Foobar ';

$num = 100000;

$pregStart = microtime(true);
$i = 0;
while ($i < $num) {
    preg_match($preg, $toMatch);
    $i++;
}
$pregStop = microtime(true);

$eqStart = microtime(true);
$i = 0;
while ($i < $num) {
    strpos($toMatch, $pos);
    $i++;
}
$eqStop = microtime(true);

echo 'Time preg: '. ($pregStop-$pregStart) .' seconds<br />';
echo 'Time pos: '. ($eqStop-$eqStart) .' seconds<br />';
Time preg: 0.090588092803955 seconds
Time pos: 0.047449111938477 seconds

Gut, hier ist es zwar immernoch knapp doppelt so langsam ... ~2:1,
aber das 17:1 kann ich so nicht bestätigen.


Aber mal ganz im Ernst, fallen die 0.5 Microsekunden mehr pro Vergleich, bei den wenigen Vergleichen und vorallem für soein kleines Projekt, wirklich noch auf?
Bei z.b. 20 Vergleichen (ich hatte viel weniger drin) macht das hier grade mal 10 Microsekunden aus, was durch die "langsame" Internetverbindung allemale überboten wird.

Mit einem etwas längerem Text vor dem Gesuchten, kam ich maximal auf die 4-fache Zeit. (also in dem Größenbereich/Länge, welchen ich erwarten würde)



Ich spare auch gerne hier und da mal 'nen bissl Zeit ein, aber hier (vorallem in meinem Fall) fällt es schlußendlich garnicht auf.

Valle 4. Jul 2010 11:33

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Hi,

Zitat:

Zitat von himitsu (Beitrag 1033351)
Wenn RegExen doch sooooo böse sind und möglichst vermieden werden sollen,
dann darf man auch keine RewriteEngine verwenden, denn dieses Ding ist ja bis zum Rand damit vollgestopft. :stupid:

im Prinzip hast du da auch Recht. Apache kann diese Regexen aber ganz toll (im kompillierten Zustand) zwischenspeichern. Bin mir im Moment auch gar nicht sicher, ob PHP das nicht auch irgendwie kann (sowas wie preg_compile?). Ich mag mod_rewrite auch nicht so besonders. Meine Applikationen bau ich auf, indem ich einfach alle URLs auf die index.php weiterleite - sofern die aufgerufene URL nicht als Datei wirklich existiert. (sinnvoll für statische Daten wie Bilder oder CSS)

Liebe Grüße,
Valle

mirage228 4. Jul 2010 11:53

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Also ich hatte mit himitsu's Code so Mittel ca. (PHP 5.2 und PHP 5.3):
Code:
Time preg: 0.11731195449829 seconds
Time pos: 0.036504030227661 seconds
Dennoch würde ich sagen, dass es in vielen Fällen eine Fallentscheidung ist, ob reguläre Ausdrücke eingesetzt werden sollten oder nicht.
Bei seltenen Tasks kann man sie auf jeden Fall einsetzen. Bei Scripten, die ggf. häufig aufgerufen werden könnten, würde ich auf ausgiebigere Last-Tests ansetzen. Damit kann man ermitteln, ob eventuelle Bottlenecks wirklich am RegEx liegen oder nicht vielleicht doch an anderen Code-Stellen (DB-Zugriff, SQL-Statements, Design im allgemeinen...). Je nach Komplexität der Überprüfung, ist ein RegEx vielleicht sogar besser - wenn z.B. ein String sonst erst komplett zerbröselt werden müsste...

Edit: Bei meinem PHP Inspection Unit (siehe Signatur) Projekt wird ein PHP-Quelltext auch über reguläre Ausdrücke analysiert und im Endeffekt ist die Performance auch noch recht praktikabel (750 KB Quelltext in < 1 Sekunde) - ohne reguläre Ausdrücke wäre der Code viel länger und komplizierter (und somit schwieriger zu warten) geworden.

alcaeus 4. Jul 2010 14:12

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
*seufz* Leute, es geht hier nicht um gut oder schlecht. Man sollte sich aber ueberlegen, ob man wirklich Regular Expressions einsetzen muss.

@himitsu: dein Vergleich hinkt genauso - mein Beispiel berief sich auf genau den Fall den ich (entweder in diesem Thema oder in einem anderen "Helft mir mit PHP"-Thema) beschrieben hatte.

Im konkreten Fall mussten fuer ein Ticketing-System das Support-Emails annimmt Blacklist-Eintraege geprueft werden. Die Blacklist kann auch Regex, weshalb auch vollstaendige E-Mail-Adressen als RegEx der Form "^foo@bar\.com$" gespeichert wurden. Dass das ein Performance-Loch (und das meine ich woertlich) sein kann, wurde damals gar nicht bedacht. Selbst wenn man dein Beispiel mit strpos nimmt, ein Faktor von zwei ist immer noch ein Faktor von zwei.

Was ich sagen will ist: pruef ob du etwas evtl. ohne preg machen kannst. Code wie z.B.
PHP-Quellcode:
return !preg_match('#^(\.|\.\.|\.htaccess|index\.htm(l)?)$#i', $FileName);
ist naemlich absolut sinnbefreit und ausserdem schlecht testbar.

Greetz
alcaeus

PS da ich waehrend der preg-Untersuchung darauf gestossen bin:
PHP-Quellcode:
$MailMask = '#^[\w.+-]{1,64}\@[\w.-]{1,255}\.[a-z]{2,6}$#';
Ich empfehle eine Lektuere von RFC 5322 und RFC 5321. Angenommen meine Mail-Adresse ist foo.bar|foobar@meinmailprovider.de. Willst du mir sagen dass ich keine Post von dir kriegen darf?
Kleiner Tipp an alle: wenn ihr schon ne Regular Expression schreibt, die E-Mail-Adressen validieren soll, dann macht euch bitte schlau wie eine Mail-Adresse aufgebaut ist. Auch der Hostname-Part der Validierung ist leider falsch.

himitsu 4. Jul 2010 18:51

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Ja, ich hab auch irgendwo eine angeblich "komplette" RFC-irgendwas kompatible RegEx für eMails rumliegen, aber das Monstrum wollte ich nicht einbauen.
War auch geplant da mal eine bessere Methode zu verwenden, aber zum Testen reichte mir dieser "einfache" Vergleich. :stupid:



Da es grade da drüben um Foreign Keys geht und MySQL dieses ja kennt.
Damit könnte man ja so Einiges vereinfachen.

MyISAM kennt zwar die Syntax, jedoch ignoriert es dann alles.
InnoDB soll es können, aber dieses soll ja langsamer sein.

Was empfiehlst du nun da?
- langsamere Engine (bei den Tabellen, wo sowas verwendet wird)
- oder keine Foreign Keys verwenden und selber Code gegenprüfen, aufräumen usw.

jfheins 4. Jul 2010 18:55

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Zitat:

Zitat von himitsu (Beitrag 1033494)
MyISAM kennt zwar die Syntax, jedoch ignoriert es dann alles.
InnoDB soll es ja können, aber dieses soll langsamer sein.

Was empfiehlst du nun da?
- langsamere Engine (bei den Tabellen, wo sowas verwendet wird)
- oder keine Foreign Keys verwenden und selber Code gegenprüfen, aufräumen usw.

Wo hast du denn das her? Okay, einmal googlen reicht :angel2:
InnoDB ist meines Wissens nach neuer und mind. genauso schnell. Meine Faustregel war bisher: Immer InnoDB benutzen es sei denn, man braucht unbedingt die Volltextsuche von MyISAM.
Ist aber trotzdem nicht verkehrt. InnoDB bringt einfach viele Features mit, die inzwischen Standard sein sollten. Transaktionen z.B.

fkerber 4. Jul 2010 18:55

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Hi!

Ich bin der Meinung, man sollte FKs nutzen, wo man kann.
Daher würde ich zu InnoDB raten.


Grüße, Frederic

DP-Maintenance 4. Jul 2010 19:19

Dieses Thema wurde am "04. Jul 2010, 20:19 Uhr" von "mkinzler" aus dem Forum "Klatsch und Tratsch" in das Forum "Programmieren allgemein" verschoben.

himitsu 4. Jul 2010 19:38

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Nja, irgendwo war auch ein Test zu sehn, wo InnoDB mindestens doppelt so langsam war.
Außerdem soll InnoDB mehr Speicher belegen. (hab's aber noch nicht getestet)

Mindestens eine Tabelle werde ich vermutlich auch als MEMORY anlegen (mal sehn ob as was ausmacht, ist ja eh nur für 'ne Cache)


Die Volltextsuche werd' ich an ein/zwei Stellen benötigen,
also ich hätte ich mich so oder so je nach Tabelle entschieden.

Also gut, dann mal sehn wie's weitergeht.
Den Kern meines winzigen/kranken CMS hab ich soweit wieder am laufen und alles schön mit Klassen usw.
(Das Einzige, was sich kaum geändert hat, ist meine MySQL-Klasse :stupid: )

alcaeus 5. Jul 2010 21:11

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Moin,

ja, InnoDB ist etwas langsamer als MyISAM. Trotzdem rate ich dir dringendst, InnoDB zu verwenden. Warum?
  • MyISAM ist nicht transaktionssicher. Da kannst du dir das DBMS gleich sparen.
  • InnoDB ist eine relationale Engine, d.h. du kannst Beziehungen zwischen Tabellen herstellen. D.h. du kannst z.B. das Loeschen eines Benutzers verhindern, solange noch Records vorhanden sind die sich auf diesen Benutzer beziehen - und das ohne durch die Datenbank pfluegen zu muessen.
  • InnoDB beherrscht Row-Level-Locking. Bei MyISAM muss immer die gesamte Tabelle gesperrt werden, was im Produktivbetrieb richtig viel Aerger machen kann. InnoDB sperrt auf Zeilenebene, d.h. Schreiboperationen werden parallelisiert.
  • InnoDB ist zwar etwas langsamer als MyISAM, skaliert aber signifikant besser. Die Verwendung von RAW-Files fuer die Speicherung der Datenbank (d.h. das DBMS geht am Betriebssystem vorbei und schreibt im RAW-Modus auf die Festplatte. Das ist um einen sehr grossen Faktor schneller - mangels Testsystem kann ich dir da grad keine konkreten Zahlen liefern.

Kurzum: wenn du mit Datenbanken arbeiten willst, nimm InnoDB. Wenn du deine Daten irgendwo parken willst um dich in ein paar Jahren darueber zu aergern dass die DB mehr Loecher in den Relationen hat als die Schweiz im Kaese dann verwende InnoDB. Vertrau mir, InnoDB sollte deine Wahl sein.

Greetz
alcaeus

himitsu 5. Jul 2010 21:19

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Also kann ich es mir sparen verschiedene Engines zu verwenden?
Brauche ja nicht überall diese schönen Sachen und hätte InnoDB jetzt nur da verwendet, wo ich dieses benötigt hätte.


Ich versuch ja grade diese ForeignKeys verwenden zu wollte ... wenn es denn mal ginge.
(kämpfe grade mit diesem blöden "Can't create table 'test.#sql-ce0_ba' (errno: 150)" und noch keiner der Tipps half. )

alcaeus 5. Jul 2010 21:24

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Fuehr nach dem create table mal ein "show innodb status" aus. Da steht dann unter "LATEST FOREIGN KEY ERROR" unter anderem folgendes:
Zitat:

Cannot find an index in the referenced table where the
referenced columns appear as the first columns, or column types
in the table and the referenced table do not match for constraint.
Und ja, verwende InnoDB. Fuer deine Cache-Tabelle kannst MEMORY verwenden, aber beachte dass
  1. Keine TEXT/BLOB-Felder moeglich sind und
  2. Die Daten bei einem Neustart des MySQL-Servers geloescht werden.

Greetz
alcaeus

PS: obiges Ergebnis ergibt eine Google-Suche nach wenigen Momenten. Nur so als Hinweis.

himitsu 5. Jul 2010 21:33

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Zitat:

Zitat von alcaeus (Beitrag 1033676)
aber beachte dass
  1. Keine TEXT/BLOB-Felder moeglich sind und
  2. Die Daten bei einem Neustart des MySQL-Servers geloescht werden.

hatte ich schon gelesen :)

Zitat:

Zitat von alcaeus (Beitrag 1033676)
PS: obiges Ergebnis ergibt eine Google-Suche nach wenigen Momenten. Nur so als Hinweis.

Da bin ich schon unterwegs und ja, es liefert massenhaft Ergebnisse,
aber irgendwas stimmt wohl einfach nicht ... weiß nur noch nicht was :cry:

SQL-Code:
CREATE TABLE IF NOT EXISTS `hCMS_Config_Group` (
  `Name` VARCHAR(31) UNIQUE KEY,
  `Description` VARCHAR(31)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_unicode_ci
result: OK

CREATE TABLE IF NOT EXISTS `hCMS_Config` (
  `Name` VARCHAR(63) UNIQUE KEY,
  `Value` VARCHAR(127),
  `Type` ENUM ('String', 'Integer', 'Boolean', 'Serialize') DEFAULT 'String',
  `Description` VARCHAR(31), `Group` VARCHAR(31)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_unicode_ci
result: OK

ALTER TABLE `hCMS_Config` ADD INDEX (`Group`)
result: OK

ALTER TABLE `hCMS_Config` ADD FOREIGN KEY (`Group`)
REFERENCES `Config_Group` (`Name`) ON DELETE NO ACTION ON UPDATE NO ACTION
result (1005): Can't create table 'test.#sql-ce0_ba' (errno: 150)
das sollte doch eigentlich stimmen? :|

"show innodb status" meint dann auch noch irgendwas von
Zitat:

Cannot resolve table name close to:
(`Name`) ON DELETE NO ACTION ON UPDATE NO ACTION

fkerber 5. Jul 2010 21:41

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Hi!

Das Problem steht doch da?!
Er kann `Config_Group` nicht finden, da die Tabelle `hCMS_Config_Group` heißt.


Grüße, Frederic

alcaeus 5. Jul 2010 21:48

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
:shock:


:shock:




:shock:










:shock:



Aehm....ja. Bitte, bitte, bitte, mach dich schlau was DB-Design betrifft. Also:
Die Tabelle hCMS_Config_Group hat als Unique-Key einen Varchar von 31 Zeichen Laenge. Da MySQL bei UTF-8 3 Bytes pro Zeichen reserviert hast du dir da grad einen 93 Byte grossen Index gebastelt. Dumme Idee.
hCMS_Config verwendet einen 63 Zeichen langen String, d.h. einen 189 Byte grossen Index. Noch viel duemmer.
Fuer sowas verwendet man numerische Primary-Keys.

Naechster Punkt: hCMS_Config hat einen Index namens `Group`, dieser beinhaltet aber keine Spalten - er bringt also gar nichts.
Du versuchst nun einen Foreign-Key auf diesen Index zu legen welcher auf die hCMS_Config_Group zeigt. Das kann nicht gut gehn.

Greetz
alcaeus

PS: gib es einen Grund warum du deinen Strings so interessante Laengen wie z.B. 31 Zeichen, 63 Zeichen, 127 Zeichen, etc.?

himitsu 5. Jul 2010 22:15

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
es raucht ... es raucht ... ruft die Feuerwehr :firejump:
  • Viele Felder könnte ich als Binary oder Ansi deklarieren.

    Wie ist das mit der den Tabellen und der Datenbankverbindung?
    - Kann ich diese auf UTF-8 eingestellt lassen und wird dann automatisch umgewandelt?
    - oder soll ich alles z.B. auf Ansi umwandeln, nut die größen sprachabhänhingen Textfelder als UTF-8 deklarieren und dann auf PHP-Ebene die Kodierung umwandeln, bzw. kann man nur diese Felder als UTF-8 übertragen lassen.
    .
  • Es wird sich vermutlich nicht um rießige Datenmengen handeln.
    Und wenn ich jetzt für alles wirklich nur Integer als Indize und für die ForeignKeys verwende, dann müßte ich an vielen Stellen zusätzliche ID-Name-Umwandlungen einbauen, welche jetzt nicht nötig sind.
    Oder ich muß die Datenbankanfragen so ändern, daß dann auch der Name aus einer anderen Tabelle zur ID in der ausgelesenen Tabelle mit ausgelesen wird (ich weiß, daß es geht ... weiß nur noch nicht wie)

    Ist denn wirklich soooooooooooooo schlimm, wenn man nicht alles auf Integer-Verbindungen runteroptimiert und stattdessen Namentliche verwendet?
    Immerhin handelt es sich auch nicht um große Datenmengen, welche auch extrem schnell verwaltet werden müssen ... es soll halt nur einfach sein.
    (OK, abgesehn von dem UTF-8-Problemchen)

Nja, immerhin hab ich nun alles auf Klassen umgestellt, dank der Vererbung und den Autoloadern konnte vieles vereinfacht und automatisiert werden.
(Der Installer suchst sich jetzt z.B. alles selber zusammen, fragt die Klassen nach ihren Datenbank anbindungen und macht dann alles von alleine ... selbst nach erweiterungen muß ich den zukünftig wohl nicht mehr ändern müssen :thumb: , wenn ich dann mal ein ordentliches passendes DB-Design zusammen hab)

himitsu 7. Jul 2010 09:45

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
*verwirrung*

Zitat:

Tipp: Um mit UTF-8 Speicher zu sparen, verwenden Sie VARCHAR statt CHAR. Andernfalls muss MySQL 3 Byte pro Zeichen in einer CHAR CHARACTER SET utf8-Spalte reservieren, da dies die maximale Länge ist. So muss MySQL etwa 30 Byte für eine CHAR(10) CHARACTER SET utf8-Spalte vorsehen.
> http://dev.mysql.com/doc/refman/5.1/...t-unicode.html / http://dev.mysql.com/doc/refman/5.1/...code-utf8.html

heißt das nun, daß bei VARCHAR doch nicht 3 Byte für UTF-8 gespeichert werden? :?

Zitat:

utf8mb4, a UTF-8 encoding of the Unicode character set using one to four bytes per character
Wobei das ab MySQL 5.5 doch dann 4 Byte sein müßten. :shock:

Mithrandir 7. Jul 2010 13:44

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Kleines OT:

Himi,

mit [CODE=PHP] kannst du deinen PHP-Code entsprechend highlighten. Das sieht dann deutlich besser aus... Aber das nur am Rande. :stupid:

himitsu 16. Jul 2010 23:25

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Bin ich nur zu doof, oder steht wirklich nirgends, daß man von MultiQuerys (mysqli) unbedingt die Results auslesen muß, damit die nächste Query nicht mit 'nem Fehler abbricht?

Zitat:

Commands out of sync; you can't run this command now
Hab 'ne ganze Weile gebraucht, bis ich rausbekam, daß es daran liegt und nicht an der nachfolgenden Query. :wall:

Denn, wenn ich jetzt
PHP-Quellcode:
while ($this->more_results() && $this->next_result());
nach der MultiQuery einfüge (mich interessieren die Einzelergebnisse eigentlich nicht), dann funktioniert es plötzlich.

wicht 16. Jul 2010 23:58

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Ich werde jetzt vermutlich von einigen Menschen auf den Deckel bekommen und einiges an Reputation verlieren, sofern vorhanden, aber ich kann es mir nach der Fadenlänge nicht mehr verkneifen...

http://www.steike.com/code/php-must-die/

Aber um wenigstens ein bisschen was beizutragen, man sollte vielleicht das nächste mal ein paar Frameworks evaluieren, bevor man ein Projekt startet. Ich mag PHP nicht (mehr) und nutze für meine Webseiten nur noch Python und Django, aber ich denke, dass es Frameworks für PHP gibt, die einem diese ganzen Probleme, die von dir hier in diesem Thread beschrieben wurden, wenigstens teilweise schon vorher abnehmen.
Versteh mich nicht falsch, ich habe auch lange mit PHP entwickelt und möchte hier nicht rumpöbeln oder so, aber ich kam dabei am Ende auch auf ein selbst gebautes Framework, womit ich drei Webseiten betrieben habe, quasi aus den selben Quellen, und weil es solche Frameworks schon gibt, denke ich, dass man sich da sehr viel Arbeit hätte ersparen können...

Gutes Nächtle..

Mithrandir 17. Jul 2010 07:25

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Zitat:

Zitat von wicht (Beitrag 1035783)
[..] und einiges an Reputation verlieren [..]

Welche Reputation? :mrgreen:


Der Inhalt des Artikels ist natürlich nicht verkehrt, aber der Titel klingt doch sehr - polemisch. ;)

himitsu 20. Jul 2010 12:50

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Hätte da noch eine Verständnisfrage.

Ich weiß zwar, daß es so richtig ist, aber ich versteh nicht ganz warum.
Es steht auch überall erklart, daß man es so macht ... nur halt immer ohne Begründung.

Es geht um überschriebene Methoden und den Aufruf der Originals (quasi delphis Inherited)
PHP-Quellcode:
function MyMethod() {
  ...
  parent::MyMethod();
}
Im Grunde ist es ja so:
PHP-Quellcode:
Class::StaticMethod();
Object->Method();
, also müßte ich doch theoretisch eigentlich parent->MyMethod(); aufrufen? :gruebel:

Für die eigene Klasse gibt es ja auch
PHP-Quellcode:
self::StaticMethod();
und
PHP-Quellcode:
$this->Method();
,
aber für parent:: scheint es kein $parent-> oder Ähnliches zu geben.

Nja, es funktioniert ja, egal ob die Methode nun statisch ist oder nicht,
aber ich find's einfach nicht logisch.

alcaeus 20. Jul 2010 17:40

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Willkommen in der Welt von PHP. Da ist schon mehr unlogisch.
Aber ja, der Aufruf einer Funktion der Parent-Klasse wird mit parent::foo() erledigt. Dies gilt allerdings nur, wenn du foo() ueberschrieben hast und das Original aufrufen willst. Alle anderen Faelle kannst du mit $this->foo() abdecken.

Greetz
alcaeus

PS: ich weiss nicht was passiert wenn du parent::foo() ausserhalb von foo() aufrufst. Wenn du das machen musst, solltest du schleunigst den Code schreddern und von vorne anfangen.

himitsu 20. Jul 2010 18:37

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Zitat:

Zitat von alcaeus (Beitrag 1036448)
Aber ja, der Aufruf einer Funktion der Parent-Klasse wird mit parent::foo() erledigt. Dies gilt allerdings nur, wenn du foo() ueberschrieben hast und das Original aufrufen willst. Alle anderen Faelle kannst du mit $this->foo() abdecken.

Das ist schon klar und für genau für solche Fälle nutze ich es auch.

hab ja nun 'ne Menge OOP-Zeugs und vorallem Vererbung verbaut,
da ist sowas ja wohl nützlich/angebracht :angel:

Zitat:

Zitat von alcaeus (Beitrag 1036448)
PS: ich weiss nicht was passiert wenn du parent::foo() ausserhalb von foo() aufrufst.

also ich kann auch in "bar" parent::foo() aufrufen (wenn Beide oder mindestens foo überschrieben sind)

schon komisch, daß alle Foo/Bar für ihre Beispiele nutzen :lol:

himitsu 21. Jul 2010 10:46

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Wie gesagt, extrem schnelle Ausführungsgeschwindigkeiten werd ich wohl eh nicht, für meine Zwecke, benötigen.

Ich glaub ich hab jetzt meine MySQL-Klasse soweit umgestellt, so daß jetzt MySQLi verwendet wird und dazu auch noch die prepared Statements (oder wie auch immer die nochmal heißen) verwendet werden.

In meinem Query sind jetzt prepare+bind_param+execute, multi_query und das normale query gekapselt und werden automatisch ausgewählt.
Wobei query hierbei ein real_query mit einem speziellem store_result sind, damit die Result-Klasse ausgetauscht werden kann,
welche dann das free_result automatisch aufruft, wenn das Result-Objekt freigegeben wird.

Hab nun auch mal angefangen dieses PHPDoc zu verwenden.
(das macht sich, z.B. bei der Autovervolständigung in NetBeans, richtig gut :-D)

also mir gefällt's so schon irgendwie
und aus diesem
PHP-Quellcode:
$Var1 = 'abc';
$Var2 = 123;
//$Result = $DB->Query('INSERT INTO `#PrefixMyTable` (`Field1`, `Field2`) VALUES (?, ?)',
//  array($Var1, $Var2), array('#Prefix' => 'xxx'));
# das #Prefix ist schon vordefiniert enthalten
$Result = $DB->Query('INSERT INTO `#PrefixMyTable` (`Field1`, `Field2`) VALUES (?, ?)', array($Var1, $Var2));
wird dann intern das gemacht
PHP-Quellcode:
$Var1 = 'abc';
$Var2 = 123;
$State = $DB->prepare('INSERT INTO `xxxMyTable` (`Field1`, `Field2`) VALUES (?, ?)');
$State->bind_param('si', $Var1, $Var2);
$Result = $State->execute();
wobei es auch so ginge :angel:
PHP-Quellcode:
$Var1 = 'abc';
$Result = $DB->Insert('MyTable', array('Field1' => $Var1, 'Field2' => 123));
Bei diesem Preparedzeugs fehlte mir etwas, womit man auch mal normalen Text einfügen kann und nicht nur ganze Parameter.



Auch wenn's böse aussieht, fand es so letztendlich aber einfacher ... im __construct hol ich die Configuration nun direkt aus einem Config-Objekt, anstatt diese weiterhin via Parameter zu übergeben.

generic 21. Jul 2010 12:49

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Warum nimmst du nicht einfach ein fertiges Framework wie z.B. Zend.
Bei Zend kann man auch nur einzelne Klassen nutzen, wie die ZendDB.

himitsu 21. Jul 2010 13:09

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Hatte damals (ist bestimmt schon 2 Jahre her) mal in Zend reingeguckt ... und dachte das kann man nur komplett nutzen, bzw daß es nicht so einfach wäre, da auch mal nur eine einzelne Klasse rauszunehmen.

nja, muß jetzt nur noch ein paar "Kleinigkeiten" in meinen Basisklassen (also meinem Framework) an die neue DB-Klasse anpassen,
dann fehlt noch die Umstellung der Cache und der Templatte-Klasse, dann das Grundsystem/Framework hoffentlich fertig :)

Und nebenbei hab ich dadurch auch noch vieles Neues (mehr OOP, Vererbung, Autoloader, PHPDoc) gelernt. :stupid:

'nen "kleines" Framework, welches mir gefällt und vorallem womit ich auch zurechtkomme fand ich auch nicht.

himitsu 8. Aug 2010 21:35

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Wo jetzt erstmal wieder alles läuft und zusätzlich noch die mysql- und templatte-klassen fertig sind (und hoffentlich auch alles läuft)
und bevor ich mich ans Rechte und Benutzer-System wende, wollte ich mal wieder etwas fragen:


Und zwar hab ich von vielen Klassen deren Namen direkt für die Anbindung an die DB genutzt.
Leider kann man ja nicht von einer Klasse den Namen auslesen.
Der PHP-Parser hat was dagegen, wenn man den Klassennamen direkt als "String" verwenden will, aber diesen nicht schon als String in den Quelltext reinschreibt.
PHP-Quellcode:
class MeineKlasse {
}

echo MeineKlasse;
if (is_subclass_of($obj, MeineKlasse)) ...
Hier meckert er jedenfalls rum.
Zitat:

Notice: Use of undefined constant MeineKlasse - assumed 'MeineKlasse' in ... on line 6
Notice: Use of undefined constant MeineKlasse - assumed 'MeineKlasse' in ... on line 7
Und nein, den Namen als String will ich nicht angeben, denn da fallen Verschreiber nicht immer gleich auf.
Wenn man den Klassennamen nutzen würde, dann könnte eine Meldung ala "unbekannte Klasse" erzeugt werden.

nun war ich erstmal auf diese saublöde Idee (und verwende sie schon eine kleine Weile)
PHP-Quellcode:
class test {
  static function Name()
  {
    return get_called_class();
  }
}

echo test::Name();
leider finde ich das etwas umständlich,
aber immerhin kommt bei einem Verschreiber etwas wie "... besitzt diese Methode nicht" :-D

Nun bin ich vor ein paar Minuten auf diese noch krankere Idee gekommen, hab's ausprobiert und erschreckender Weise scheint es zu funktionieren. :shock:
(also, es geht um die Konstante ... das __toString ist nur bei dem Test mit drin)
PHP-Quellcode:
class test {
  function __toString()
  {
    return get_called_class();
  }
}
define('test', 'test');

$test = new test;
if ($test instanceof test) echo 'yes';

echo test;
echo $test;
Wie gesagt, in diesem kleinen Test funktioniert es, auch wenn ich nicht verstehe, wie PHP nun zwischen der Konstannte und der Klasse unterscheidet. :gruebel:


Spricht jetzt irgendwas gegen dieses Vorgehen?
(natürlich würde ich mir diese Konstanten automatisch generieren lassen, um da ebenfalls Verscheiber zu unterbinden)

alcaeus 8. Aug 2010 21:49

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Zitat:

Zitat von himitsu (Beitrag 1040298)
Leider kann man ja nicht von einer Klasse den Namen auslesen.

Falsch. Ich erwaehne hier einfach mal get_class(). Alternativ gibts auch instanceof.
Zitat:

Zitat von himitsu (Beitrag 1040298)
Der PHP-Parser hat was dagegen, wenn man den Klassennamen direkt als "String" verwenden will, aber diesen nicht schon als String in den Quelltext reinschreibt.

Aus gutem Grund. Das was du im angegebenen Code machst ist eine Konstante namens "MeineKlasse" zu verwenden. Es ist einfach: alles was keine Zahl, kein String-Literal, keine Variable ist in diesem Kontext eine Konstante. Insofern hat der Parser auch recht wenn er dir den Code mit der Notice um die Ohren wirft. Das ist wirklich beschissener Code den du da geschrieben hast.

Zitat:

Zitat von himitsu (Beitrag 1040298)
Und nein, den Namen als String will ich nicht angeben, denn da fallen Verschreiber nicht immer gleich auf.
Wenn man den Klassennamen nutzen würde, dann könnte eine Meldung ala "unbekannte Klasse" erzeugt werden.

Nein, koennte nicht. Schliesslich sieht der eine Konstante die nicht existiert. Ob du MeineKlasse oder MaineKlasse schreibst ist dem Parser scheissegal - es ist immer "Use of undefined constant".

Zitat:

Zitat von himitsu (Beitrag 1040298)
nun war ich erstmal auf diese saublöde Idee (und verwende sie schon eine kleine Weile)
PHP-Quellcode:
class test {
  static function Name()
  {
    return get_called_class();
  }
}

echo test::Name();
leider finde ich das etwas umständlich,
aber immerhin kommt bei einem Verschreiber etwas wie "... besitzt diese Methode nicht" :-D

Aua. Aua. Aua. Aua.

Zitat:

Zitat von himitsu (Beitrag 1040298)
Nun bin ich vor ein paar Minuten auf diese noch krankere Idee gekommen, hab's ausprobiert und erschreckender Weise scheint es zu funktionieren. :shock:
PHP-Quellcode:
class test {
  function __toString()
  {
    return get_called_class();
  }
}
define('test', 'test');

$test = new test;
if ($test instanceof test) echo 'yes';

echo test;
echo $test;
Wie gesagt, in diesem kleinen Test funktioniert es, auch wenn ich nicht verstehe, wie PHP nun zwischen der Konstannte und der Klasse unterscheidet. :gruebel:

WTF????????
Du baust dir da ein Konstrukt hin das einfach nur noch bescheuert ist. Also wirklich bescheuert. Erstens solltest du $test mithilfe von
PHP-Quellcode:
new test();
erzeugen. Zweitens, fuehr mal den Code aus:
PHP-Quellcode:
class foo {
}
class bar {
}
define('foo', 'bar');

$test = new foo();
echo get_class($test);
So, und jetzt rate was ausgegeben wird, dann weisst du auch was Vorrang hat: Klassenname oder Konstantenname.
Wenn du direkt im Anschluss diesen Code ausfuehrst:
PHP-Quellcode:
define('foo', 'baz');
$test = new foo();
echo get_class($test);
wirst du feststellen dass beim Instanzieren von Objekten Konstantennamen nicht aufgeloest werden.

Zitat:

Zitat von himitsu (Beitrag 1040298)
Spricht jetzt irgendwas gegen dieses Vorgehen?
(natürlich würde ich mir diese Konstanten automatisch generieren lassen, um da ebenfalls Verscheiber zu unterbinden)

WTF? Du wuerdest dir automatisch Konstanten fuer Klassennamen generieren lassen weil du (verzeih mir meine Wortwahl) zu dumm bist einen Klassennamen fehlerfrei zu tippen? In dem Fall hast du ganz andere Probleme als komischen PHP-Code. Und fuers Protokoll: es sprechen zigtausende Gruende dagegen, solche komischen Eigenheiten von PHP produktiv zu nutzen. Nur weil dich die Autobahnauffahrt nicht daran hindert, gegen die Fahrtrichtung aufzufahren machst dus auch nicht um von A nach B zu kommen, oder?

Greetz
alcaeus

himitsu 8. Aug 2010 22:13

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
auf die Idee bin ich auch schon gekommen und
PHP-Quellcode:
get_class(MeineKlasse)
funktioniert nicht :zwinker:

und du weißt garnicht wie oft man sich verschreiben kann :oops:
(in Strings gibt's ja nimma 'ne Autovervollständigung)

alcaeus 9. Aug 2010 06:27

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Zitat:

Zitat von himitsu (Beitrag 1040310)
auf die Idee bin ich auch schon gekommen und
PHP-Quellcode:
get_class(MeineKlasse)
funktioniert nicht :zwinker:

Was heisst hier "funktioniert nicht"? Versuchs mal mit
PHP-Quellcode:
get_class($meinObjekt);
Und trotzdem, was ist das Problem mit
PHP-Quellcode:
if ($meinObjekt instanceof MeineKlasse)
Greetz
alcaeus

himitsu 9. Aug 2010 06:51

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Zitat:

Zitat von alcaeus (Beitrag 1040324)
Was heisst hier "funktioniert nicht"? Versuchs mal mit
PHP-Quellcode:
get_class($meinObjekt);
Und trotzdem, was ist das Problem mit
PHP-Quellcode:
if ($meinObjekt instanceof MeineKlasse)

In der Klasse kann man z.B. __CLASS__ als String verwenden, um den Klassennamen als Tabellen-/Feldnamen für DB-Anfragen zu nutzen.

Code:
meinObjekt             << Name von Klasse
get_class(meinObjekt)  << Name von Klasse
get_class($meinObjekt) << Name von Objekt
und es kommt natürlich der bekannte Fehler, mit der unbekannten Konstante

Tja, nun hab ich alles nach String umgewandelt und auch denganzen __CLASS__-Schrott rausgemacht,
denn es sieht sch***e aus, wenn an einer Stelle die Klasse und an anderer Stelle einen String genutzt wird.

Ich wollte einfach nur ein einheitliches Aussehn.
> in der Klasse nutzte ich __CLASS__ und self
> und z.B. im Installer wollte ich den Klassennamen direkt angeben.

öfters genutzte DB-Zugriffe und Ähnliches hab ich in Klassen zusammengefaßt
und selten/fast nie genutztes in getrennten Dateien, damit diese nicht ständig mit geladen werden müssen.

alcaeus 9. Aug 2010 18:08

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Erklaer mal dein Problem in Ruhe von Anfang an, ohne komischen WTF-Code. Ich hab absolut nicht verstanden was du ueberhaupt willst.

Kurzer Code zur Erklaerung:
PHP-Quellcode:
class A
{
  public function foo()
  {
    echo __CLASS__;
  }
}

class B extends A
{
}

$b = new B();
Kurz zur Erklaerung: __CLASS__ ist eine magische Konstante, die immer den Namen der aktuellen Klasse beinhaltet (oder nicht definiert ist wenn du nicht im Klassen-Kontext bist).
PHP-Quellcode:
$b->foo();
Dieser Code gibt "A" aus.

get_class() ist eine Methode, der du ein Objekt, keinen String (auch nicht sowas: get_class(meineKlasse), denn das ist auch nur ein String mit Compiler-Warnung) uebergibst und die dir den Klassennamen zurueckliefert.
PHP-Quellcode:
get_class($b)
Dieser Code gibt "B" aus.

instanceof prueft ob ein Objekt ein Child einer Klasse ist.
PHP-Quellcode:
var_dump($b instanceof B);
var_dump($b instanceof A);
In diesem Fall wird zweimal true ausgegeben, denn durch die Vererbung ist $b auch eine Instanz von A.

Klar soweit?

Greetz
alcaeus

PS: __CLASS__ ist kein Schrott. Es sieht auch nicht scheisse aus wenn an einer Stelle die Klasse benutzt wird und an anderer der String. Hin und wieder brauchts das eben so. Schrott oder Scheisse ist es nur, wenn du es nicht schaffst Konzepte auseinanderzuhalten.

himitsu 9. Aug 2010 19:58

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Was __CLASS__ ist das weiß ich schon,
aber ich wollte einfach nicht an einer Stelle eine Konstannte(Klasse) und anderer Stelle einen String für das Selbe verwenden.

PHP-Quellcode:
$Result = array();
$Database->Select(Language, array('NameID' => $NameID));
while ($Row = $Database->FetchRow())
  $Result[$Row->Lang] = $Row->Text;
return $Result;
PHP-Quellcode:
$Database->AddForeignKey(Template, 'Description', Language, 'ID');
.

Ich finde es auch praktisch, wenn die "Klasse" in der Syntaxhervorhebung des Editors anders angezeigt wird. (is irgendwie übersichtlicher, als wenn da nur irgendein String unter Anderen untergeht.


Hab aber nun allen Klassen (wo ich's genötige) eine Konstannte verpaßt, in welcher der Tabellen/Klassenname nochmal drinsteht.
PHP-Quellcode:
$Database->Select(Language::TAB, array('NameID' => $NameID));
.
Auch innerhalb der Klassen scheib ich nun dieses, obwohl da auch die magische String-Konstante __CLASS__ gegangen wäre.

Valle 9. Aug 2010 21:34

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Also ich bin mir recht sicher, dass ich verstanden habe was du meinst. Das wird' dir aber jetzt nicht gefallen... Dein Datenbankkonzept ist in der Hinsicht einfach nicht dafür zu gebrauchen. Ich sag' dir mal wie ich (und viele andere, vielleicht auch alcaeus :-) ) das machen:
  • jede Tabelle der Datenbank entspricht einer Klasse
  • diese Klassen sind alle abgeleitet von einer gemeinsamen Tabellen-Klasse
  • diese Eltern-Klasse bietet Funktionen wie UPDATE usw. an
  • so könnte man das dann benutzen:

PHP-Quellcode:
$user = new User();
$user->passsword = md5( /*...*/ );
$user->name = $name;
$user->save();
Um SELECTs zu machen gibt es verschiedene Möglichkeiten. Darunter zum Beispiel, dass man dieser Eltern-Klasse ein Attribut (z.B. "objects") gibt, oder auch direkt einfache Methoden. So kann man das benutzen:

PHP-Quellcode:
$users = new User()->objects->all();
foreach ($users as $user) { /* ... */ }
Eine Klasse bzw. Tabelle definiert sich dann beispielsweise durch sowas hier:

PHP-Quellcode:
class User extends BaseTable
{

    public function getFields()
    {
        return array(
            "name" => new VarChar(255),
            "password" => new VarChar(32)
        );
    }

}
Das ist alles nur beispielsweise und auch nur ungefähr. Vieles davon kann man ändern. So ist der Weg, den ich kennen gelernt habe und zu schätzen weiß. Im Vergleich zu den vielen, recht simple gehaltenen Anwendungen, kann man das auch durchauss als "professionell" bezeichnen.

Fertige Implementationen von Ähnlichem (!) sind zum Beispiel Doctrine oder das Zend Framework. Ich persönliche bin total verliebt ( :love: ) in Django (Python) und leite von deren Strukturen auch vieles auf meine PHP-Apps ab.

Hoffe ich konnte dir damit helfen. PHP ist nicht so schlimm wie du immer denkst. Du machst es nur schlimm... PHP hat viele Nachteile, das kann man nicht abstreiten. Aber das sind nicht die, die du immer nennst. Schlechtes Design, inkonsequente APIs und unlogisches Verhalten definieren sich an ganz anderen Stellen in PHP. Aber auch damit lernt man umzugehen.

Liebe Grüße,
Valle :-)

alcaeus 9. Aug 2010 22:34

AW: PHP - sind hier "Sicherheitsexperten" an Board?
 
Zitat:

Zitat von himitsu (Beitrag 1040604)
PHP-Quellcode:
$Database->Select(Language, array('NameID' => $NameID));

Das? Dafuer brauchst du solches Konstantengedoens statt Klassennamen als String anzugeben? Wie vermutet liegt der Fehler oder das Problem nichtmal ansatzweise bei PHP sondern in der Architektur der Anwendung. Ohne den Code zu kennen: du uebergibst der Select-Funktion die Parameter fuer die Prepared Statements und den Namen der Klasse von dem die Objekte beim Zurueckgeben via FetchRow() sein sollen?
Erstens: wenn du deinem DB-Layer sagen musst, in welche Klasse er die Daten reinhydrieren soll dann hast du schon was falsch gemacht. Entweder das Ding weiss es von selbst oder du musst nochmal ans Zeichenbrett.
Zweitens: in dem Codebeispiel von vorhin ist es absolut sinnbefreit, eine Hydrierung in Objekte vorzunehmen (die kostet richtig viel Zeit) nur um danach ein Array zu befuellen. Da bist du von Anfang an mit Arrays besser bedient. Spaetestens wenn du mal ein paar Hundert Datensaetze ein paar Hunder mal auslesen musst, wirst du mir fuer den Tipp danken.
Drittens: hoer auf ein n-Eck zu entwickeln, es als Rad zu verkaufen und wenn man sagt "ne, das ist kein Rad" dann einfach ein n+1-Eck zu bauen und das Ganze von vorne losgehn zu lassen. Es mag jetzt boese klingen, aber es gibt zwei richtig gute Projekte (ok, ein richtig gutes und ein...naja, egal) die genau das Umsetzen was du da halbherzig mit ohne Features umgesetzt hast. Das Ganze nennt sich "Object-Relational Mapper" und kann das Hundertfache von dem was deine Library kann. Auch von mir kommt nochmal der bereits oft erwaehnte ernsthafte Rat, dir wenigstens Doctrine anzugucken. Wenn du selbstzerstoererisch veranlagt bist kannst du dir auch noch Propel angucken. Ich mag Doctrine lieber, aber ich erwaehn auch immer Propel.

Und jetzt eine ganz, ganz, ganz grosse Bitte: befasse dich bitte mit Software-Architektur. In Delphi wird einem viel vorgegeben, weshalb es schwer ist sich in den Fuss zu schiessen. Du hast ein winziges Projekt angefangen und hast dir mehr Fuesse weggeschossen als jemals Prothesen dafuer gebaut werden koennen. PHP ist eine Sprache die dich in kein Muster zwingt. Das ist auf der einen Seite sehr schoen, weil Quick'n'Dirty-Programming echt gut funktioniert. Wenn du aber Software entwickeln willst (im Unterschied zum ueblichen "Scripte basteln") dann befass dich bitte mit Patterns wie eben ORM, MVC, Factories, Singletons, Decorators. Zusaetzlich solltest du dir das Konzept der Dependency Injection angucken - all diese Dinge sind in der modernen SW-Entwicklung (und in meinen Augen v.a. in PHP) unerlaesslich.
Auch wenn du nur hobby-maessig PHP-Projekte entwickelst, als hauptberuflicher PHP-Entwickler tut es echt weh, diese Leier immer wieder erwaehnen zu muessen.

Greetz
alcaeus


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:46 Uhr.
Seite 2 von 2     12   

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz