Warum nicht Error & Exception Überprüfung in Validatoren
MB
- php
- programmiertechnik
moin,
warum nicht die Überprüfungs Code in Validatoren auslagern:
function checkCustomerID ( int $id ) : bool {
if ( Database::fetch ( $id ) === null ) {
throw new Exception( 'can\'t fint Customer ID' );
} else { // der struktur IF-ELSE zu liebe
return true;
}
// so on
}
warum nicht einfach den Code auslagern, wenn mehr als eine Prüfung mit dem gleichen Sachverhalt existieren.
function checkCustomerID ( int $id ) : bool {
if ( DataValidator::existData( $id ) ) {
// so on
}
}
…oder…
function checkCustomerID ( int $id ) : bool {
DataValidator::existData( $id );
// so on
}
…dann…
class DataValidator {
public static function existData ( int $id ) : bool {
if ( Database::fetch ( $id ) === null ) {
throw new Exception( 'can\'t find Data' );
} else {
return true;
}
}
}
die herangehensweise spart Code und trennt Code bezügl. der Zuständigkeit und bewart einen besseren überblich finde ich.
lgmb
PS: Warum funktioniert „~~~[code]“ nicht?
Hallo MB,
PS: Warum funktioniert „~~~[code]“ nicht?
Was meinst du damit?
Bis demnächst
Matthias
Natürlich lagern wir Code aus wenn sich Redundanzen abzeichnen und machen ihn damit wiederverwertbar. MFG
Natürlich lagern wir Code aus wenn sich Redundanzen abzeichnen und machen ihn damit wiederverwertbar. MFG
PS: Eine Fehlerbehandlung über Exceptions abzuwickeln ist eine Frage ob damit der Code einfacher und leserlicher wird. In PHP ist es jedoch so, daß eine Exception immer auch einen Trace beinhaltet und das ist das was man einem Anwender bei einer fehlerhaften Eingabe nicht zeigen möchte denn die Zeile wo es knallt interessiert den Anwender sicher nicht.
MFG
Tach!
In PHP ist es jedoch so, daß eine Exception immer auch einen Trace beinhaltet und das ist das was man einem Anwender bei einer fehlerhaften Eingabe nicht zeigen möchte denn die Zeile wo es knallt interessiert den Anwender sicher nicht.
Das klingt, als ob es einen unauflöslichen Zusammenhang gäbe, zwischen den Daten in einer Exception und dem, was man dem Anwender präsentiert. Man kann aber display_errors auf 0 setzen und Fehlermeldungen in Logfiles schreiben. Das wirkt auch bei Exceptions.
dedlfix.
Tach!
In PHP ist es jedoch so, daß eine Exception immer auch einen Trace beinhaltet und das ist das was man einem Anwender bei einer fehlerhaften Eingabe nicht zeigen möchte denn die Zeile wo es knallt interessiert den Anwender sicher nicht.
Das klingt, als ob es einen unauflöslichen Zusammenhang gäbe, zwischen den Daten in einer Exception und dem, was man dem Anwender präsentiert. Man kann aber display_errors auf 0 setzen und Fehlermeldungen in Logfiles schreiben. Das wirkt auch bei Exceptions.
Fehlerbehandlung über Logfiles abzuwickeln ist keine gute Idee. Erstens kann es da passsieren daß Fehler übersehen werden und zweitens ist das ziemlich umständlich.
Nicht empfehlenswert!
Tach!
Fehlerbehandlung über Logfiles abzuwickeln ist keine gute Idee. Erstens kann es da passsieren daß Fehler übersehen werden und zweitens ist das ziemlich umständlich.
Die bessere Lösung lautet wie?
dedlfix.
moin,
Fehlerbehandlung über Logfiles abzuwickeln ist keine gute Idee. Erstens kann es da passsieren daß Fehler übersehen werden und zweitens ist das ziemlich umständlich.
Nicht empfehlenswert!
für kleine Projekte mag es sein das es durchaus sinnhaft und empfehlenswert ist es nicht zutun, aber bei komplexen Projekten wo ein Entwickler Team dahinter steckt, ist es - denke ich mal - pflicht es zutun!
lgmb
Hallo dedlfix,
ich würde es eher mit try/catch versuchen und im Fall eines fachlichen Fehlers, der per Exception signalisiert wird, auch eine fachliche Fehlermeldung präsentieren.
Verzeihen Sie, lieber User, aber Ihre Eingabe war einfach nur Scheiße!
Rolf
moin,
ich würde es eher mit try/catch versuchen und im Fall eines fachlichen Fehlers, der per Exception signalisiert wird, auch eine fachliche Fehlermeldung präsentieren.
sry, spl_exception_handler()
hab ich nicht erwähnt 😕. bei error_report( E_ALL )
fängt der Interpreter auch nach throw
PS: ich muss dazu sagen das ich leider seit gefühl monaten nicht mehr mit PHP entwickelt habe und jetzt wieder entsteige.
lgmb
moin,
[...] und das ist das was man einem Anwender bei einer fehlerhaften Eingabe nicht zeigen möchte denn die Zeile wo es knallt interessiert den Anwender sicher nicht.
Ich weiß 😉
lgmb
moin,
[...] und das ist das was man einem Anwender bei einer fehlerhaften Eingabe nicht zeigen möchte denn die Zeile wo es knallt interessiert den Anwender sicher nicht.
Ich weiß 😉
PS: In Perl ist es wesentlich einfacher mit Exceptions zu arbeiten. So wirft ein einfaches die "Das eingegebene Datum ist ungültig!\n"
eine Exception wobei der Backtrace infolge des angefügten Newline unterdückt wird.
Wenn man Klassen hat wo es Fehler nur aufgrund fehlerhafte Benutzereingaben geben kann, ist das eine elegante Art und Weise der Fehlerbehandlung.
Und noch cleverer ist es, die Fehlertexte gar nicht selber zu schreiben sondern das zu nehmen wo die Meldungen schon drinstehen. Beschwerden vom OS z.B. stehen in $^E
sogar in deutscher Sprache.
MFG
moin,
PS: In Perl ist es wesentlich einfacher mit Exceptions zu arbeiten. […]
Wie du weißt arbeite ich nicht mit Perl und ich werde es auch in meiner Lage nicht tun. Sicherlich gibt es Problemlösungen, wo diese Programmiersprache wirklich Perfekt ist, aber das sind nicht meine.
Schönen Abend 😀
lgmb
Tach!
warum nicht die Überprüfungs Code in Validatoren auslagern:
Was genau ist denn dein Problem, das du zu lösen versuchst?
function checkCustomerID ( int $id ) : bool { if ( Database::fetch ( $id ) === null ) { throw new Exception( 'can\'t fint Customer ID' ); } else { // der struktur IF-ELSE zu liebe return true; } // so on }
Es gibt jedenfalls kein Gesetz, dass if
immer ein else
benötigt. Der else-Zweig ist hier überflüssig, das return true
kann einfach so notiert werden. Wenn die Bedingung zutrifft, wird die Funktion über das throw verlassen und der nachfolgende Code nicht mehr ausführt. Deshalb ist es nicht notwendig, zwischen Code im false-Fall (else) und allgemein auszuführendem Code zu trennen. Im true-Fall wird weder das eine noch das andere ausgeführt, und im false-Fall sowie beides. Daran ändert auch nicht, dass es nur ein return-Statement und keinen allgemein auszuführenden Code gibt.
dedlfix.
Hallo dedlfix,
eine Funktion, die im Fehlerfall eine Exception wirft und ansonsten nur true zurückgibt, braucht gar nichts zurückzugeben.
Ich halte eine solche Funktion aber für überflüssig. Wenn ich schon die id durch fetch (also den Kunden einlesen) überprüfe und Fehler durch Exception anzeige, dann kann ich auch gleich eine Funktion get schreiben
function getCustomer( int $id ) : Customer {
$customer = Database::fetch ( $id );
if ( $customer === null ) {
throw new Exception( "can't find Customer ID $id" );
return $customer
}
das könnte dann in die Customer-Repositoryklasse hinein (wobei Database:fetch ein schlechter Methodenname ist um Kunden zu lesen - aber ich nehme mal an, dass der generische Name dem Umstand geschuldet ist, dass es sich um ein abstraktes Beispiel handelt).
Wenn ich jetzt nur wissen will, ob die id ok ist, ignoriere ich die Rückgabe. Ansonsten arbeite ich gleich mit dem gefundenen Kunden weiter. So ergibt es einen Sinn.
Eine echte checkCustomerId Methode sollte sinnvollerweise einen anderen Datenbankzugriff machen, der ausschließlich die ID validiert. Ein voller Fetch dürfte außer einem Index Seek auch das Lesen der Tabledaten benötigen. Liest man nur die ID, reicht ein Index Seek. Das ist schneller. Ich würde einer solchen Funktion aber auch einen echten bool-Rückgabewert verpassen und Exceptions nur im Fall eines fehlgeschlagenen Datenbankzugriffs werfen. Denn wenn ich eine solche Methode tatsächlich brauche, dann ist true und false halbwegs gleichwahrscheinlich, das ist nicht der Anwendungsfall für Excptions. Exceptions wirft man dann, wenn dieser Fall im Normalbetrieb nicht auftaucht; zumindest habe ich das mal so gelernt.
Rolf
Tach!
Wenn man den konkreten Fall kennt, kommt man durchaus zu einer anderen Antwort auf die Frage nach der sinnvollen Lösung, als wenn man irgendwelche Pauschalitäten beurteilen soll. Darin sind wir uns sicher einig, und deswegen hatte ich auch erstmal nur nach dem zu lösenden Problem gefragt, und als Nebenschauplatz ein paar technische Argumente zu if-else-Konstrukten mit Abbruch/Ende im if geliefert. Ob sowas überhaupt in der Lösung zum konkreten Problem auftaucht, steht auf einem anderen Blatt.
dedlfix.
Hallo dedlfix,
eine Funktion, die im Fehlerfall eine Exception wirft und ansonsten nur true zurückgibt, braucht gar nichts zurückzugeben.
Ich halte eine solche Funktion aber für überflüssig.
Hier geht es ums Prinzip!
MFG
moin,
eine Funktion, die im Fehlerfall eine Exception wirft und ansonsten nur true zurückgibt, braucht gar nichts zurückzugeben.
siehe hier
Ich halte eine solche Funktion aber für überflüssig. [...]
Ein Einwand. Jedoch ist diese Funktion aus der Luft gegriffen. ich hätte auch Metasyntaxtische Variablen fubar
schreiben können, aber man hat mir gesagt - ich glaube @@Gunnar Bittersmann, @@dedlfix oder du - das es besser ist im Konkreten Fall ein exemplarisches Beispiel zu geben.
lgmb
moin,
Es gibt jedenfalls kein Gesetz, dass
if
immer einelse
benötigt.
durch aus nicht
Der else-Zweig ist hier überflüssig, das
return true
kann einfach so notiert werden.
stimmt, aber du schreibst ja auch am ende eines Attribut-Wertes des letzten Attributes eines CSS Selektors ein Semikolon obwohl da keins hin muss.
small {
display: none;
}
lgmb
moin,
function checkCustomerID ( int $id ) : bool { if ( Database::fetch ( $id ) === null ) { throw new Exception( 'can\'t fint Customer ID' ); } else { // der struktur IF-ELSE zu liebe return true; } // so on }
Es gibt jedenfalls kein Gesetz, dass
if
immer einelse
benötigt. Der else-Zweig ist hier überflüssig[…]
Ich bin wohl einwenig zu fest in der Einhaltung gefahren und ich sehe es ein. Sorry dafür. Ich weis das es keinen unterschied mach, weil ja genau das selbe heraus kommt.
lgmb
hi MB,
ich habe Dir hier mal aufgeschrieben wie ich meine Fehlerbehandlung mache und Du kannst das da auch ausprobieren, ein Link macht einen Fehler anschaulich.
Und natürlich lege ich auch den Code für die als statische Route an den URL gebundene Klasse offen. Wie Du sehen kannst, Fehlerbehandlung und OOP kann auch etwas Einfaches sein und das ist ja auch das Ziel einer überschaubaren Programmierung.
Und es muss nicht immer eine Exception sein die im Fehlerfall geworfen wird. Bei mir wird z.B. nur die Zeilennummer ausgegeben, was völlig ausreicht, weil der Fehler faktisch nur in einer Datei, die dem Entwickler namentlich bekannt ist, auftreten kann. Also brauchen wir gar keinen Backtrace mit Dateinamen. Das ist der Vorteil einer flachen Klassenhierarchie.
MFG
Moin Hotti,
Und natürlich lege ich auch den Code für die als statische Route an den URL gebundene Klasse offen.
Hast du dazu einen Git-Link o.ä.?
Und es muss nicht immer eine Exception sein die im Fehlerfall geworfen wird. Bei mir wird z.B. nur die Zeilennummer ausgegeben, was völlig ausreicht
Und um einen Fehler zu analysieren musst du dir dann jeweils die Version auschecken, die zu dem genauen Zeitpunkt des Fehlers live war? Stelle ich mir recht aufwendig vor, wenn man, wie doch eigentlich üblich heutzutage, noch einen CI dazwischen hat..
Cheers, markk
Spezifischere Exceptions lassen sich besser verarbeiten.
Aus ausgelagertem ÜberprüfungsCode ist es schwer solche zu schmeißen.
Beispiel:
Bei mir hat jede Domäne ihre eigene Throwable-Familie.
Vorteile:
catch
wahlweise Eltern und Kinder fangen.<?php
namespace my_app;
class throwableException extends \Exception
{
}
<?php
namespace my_app\Customer;
final class EntityNotFoundException extends \my_app\throwableException
{
}
<?php
namespace my_app\Product;
final class EntityNotFoundException extends \my_app\throwableException
{
}
moin,
Das tut doch nichts zur Sache oder vertuhe ich mich da? Sry, dafür.
z.B.:
new FoobarError()
, new FoobarException()
usw.) die man geschrieben hat.Bist du mit diesem Thread im Bilde uns hast die Lösung die du präsentierst? Dann fürchte ich habe ich den zusammenhang nicht im Blick, sorry 😕!
lgmb
Tach!
- Ist dieser zu behandelnde Sachverhalt validiert, arbeitet die Klassenmethode der Business Klasse einfach weiter, wenn nicht, wirft der Validator eine Exception oder Error (z.B.
new FoobarError()
,new FoobarException()
usw.) die man geschrieben hat.
Die Validatorklasse kann nur eine allgemeine ValidationFailed werfen, aber keine Exception, die für den Business Case spezifisch ist. Dazu müsste man dort die allgemeine Exception abfangen und umpacken. Das kann man sich sparen, wenn man den Aufrufer selbst die Exception werfen lässt, die er für richtig hält. Außerdem kann eine fehlende Validierung immer nur als so schwerwiegend eingestuft werden, dass eine Exception nötig ist, selbst wenn dem Aufrufer eine einfache Information reichen würde.
Der Validator sollte seine Aufgabe des Validierens erfüllen und nicht in den Control Flow eingreifen, wenn dazu keine zwingende Notwendigkeit besteht. Wenn der Aufrufer immer eine Exception abzufangen hat, macht das die Behandlung nur unnötig komplex, auch an Stellen, wo das nicht nötig ist.
dedlfix.
Hallo dedlfix,
ich glaube, ihr seit euch über die Spezifität der Validatoren uneins.
Mir scheint, dass MB sehr spezifische Validatoren bauen will, die nur an einer einzigen Ecke der Businesslogik verwendet werden. Die können dann auch spezifische Fehler erzeugen.
Den Nutzen des Routings der Prüfung über eine Klassenmethode (= static function?) habe ich allerdings auch nicht verstanden.
Und das Auslagern von Code in eigene Klassen, um Klassen zu haben, scheint mir auch nicht die beste Idee. Ich weiß nicht wie gut PHP das unter Fast CGI oder mit anderen Code-Caching Verfahren optimieren kann, aber grundsätzlich ist eine Klasse erstmal eine Struktur, die PHP zur Laufzeit aufbaut. Jede zusätzliche Klasse ist zusätzliche Arbeit, die pro Request vom Server geleistet werden muss.
Es gibt verschiedene Anlässe, technische Klassen zu bilden (ich rede nicht von Businessklassen!):
Sicherlich gibt's noch mehr Anlässe.
Deine Validatoren könnten durch Anlass 3 gerechtfertigt sein.
Rolf
moin,
Den Nutzen des Routings der Prüfung über eine Klassenmethode (= static function?) habe ich allerdings auch nicht verstanden.
Ups, sorry!!! ich meinte Instanzmethoden. Sry 😟
lgmb
Hallo MB,
dann verstehe ich das 😀. Die kannst Du im Zweifel sogar von einer abstrakten Klasse erben oder als Trait einmischen.
Rolf
moin,
dann verstehe ich das 😀. Die kannst Du im Zweifel sogar von einer abstrakten Klasse erben oder als Trait einmischen.
Habe ich bei Template Construktor gemacht.
// create
$this->foobarTable = new Table();
// built
$this->foobarTable
->setCaption()
->setSettings()
->setHead()
->setBody()
->foot();
Tut mir wirklich leid. Ich „musste“ in der Vergangenheit mit CSS arbeite was ich garnicht mag. Da habe ich PHP aus den augen verloren 😟.
lgmb
moin,
$ta = new TableAttributs( 'id', 'class', 1, 1 );
$tcol = new TableColum( 'id', 'class', '1' );
$tset = new TableSettings( 'id', 'class', null, [ $tcol, $tcol, $tcol, $tcol ] );
$tc = new TableCell( 'foobar', $ta );
$tr = new TableRow( [ $tc, $tc, $tc, $tc ] );
$ts = new TableSection( [ $tr, $tr ], $ta );
$t = new TableConstructor( 'Foo Bar', $tset, $ts, $ts, $ts );
echo $t;
mit Template
<!-- TABLE -->
<table
<?php if( isset( $attributs->id ) and !empty( $attributs->id ) ) : ?>
id="<?php echo $attributs->id ?>"
<?php endif; ?>
<?php if( isset( $attributs->class ) and !empty( $attributs->class ) ) : ?>
class="<?php echo $attributs->class ?>"
<?php endif; ?>>
<!-- COLUMN GROUPS -->
<?php if( isset( $settings ) and !empty( $settings ) ) : ?>
<colgroup
<?php if( isset( $settings->id ) and !empty( $settings->id ) ) : ?>
id="<?php echo $settings->id ?>"
<?php endif; ?>
<?php if( isset( $settings->class ) and !empty( $settings->class ) ) : ?>
class="<?php echo $settings->class ?>"
<?php endif; ?>
<?php if( isset( $settings->span ) and !empty( $settings->span ) ) : ?>
span="<?php echo $settings->span ?>"
<?php endif; ?>
>
<?php if( isset( $settings->columns ) and !empty( $settings->columns ) ) : ?>
<?php for( $i = 0; $i < count( $settings->columns ); $i++ ) : ?>
<col
<?php if( isset( $settings->columns[ $i ]->id ) and !empty( $settings->columns[ $i ]->id ) ) : ?>
id="<?php echo $settings->columns[ $i ]->id ?>"
<?php endif; ?>
<?php if( isset( $settings->columns[ $i ]->class ) and !empty( $settings->columns[ $i ]->class ) ) : ?>
class="<?php echo $settings->columns[ $i ]->class ?>"
<?php endif; ?>
<?php if( isset( $settings->columns[ $i ]->span ) and !empty( $settings->columns[ $i ]->span ) ) : ?>
span="<?php echo $settings->columns[ $i ]->span ?>"
<?php endif; ?>>
<?php endfor ?>
<?php endif ?>
</colgroup>
<?php endif ?>
<!-- CAPTION -->
<?php if( isset( $caption ) and !empty( $caption ) ) : ?>
<caption><?php echo $caption ?></caption>
<?php endif; ?>
<!-- MATRIX -->
<?php foreach( $sections as $key => $section ) : ?>
<?php if( $section === null ) continue; // if one of section is not defined like '<tfoot>' ?>
<!-- SECTION -->
<t<?php echo $key; ?>
<?php if( isset( $section->attributs->id ) and !empty( $section->attributs->id ) ) : ?>
id="<?php echo $section->attributs->id ?>"
<?php endif; ?>
<?php if( isset( $section->attributs->class ) and !empty( $section->attributs->class ) ) : ?>
class="<?php echo $section->attributs->class ?>"
<?php endif; ?>>
<?php for( $i = 0; $i < count( $section->row ); $i++ ) : ?>
<tr
<?php if( isset( $section->row[ $i ]->attributs->id ) and !empty( $section->row[ $i ]->attributs->id ) ) : ?>
id="<?php echo $section->row[ $i ]->attributs->id ?>"
<?php endif; ?>
<?php if( isset( $section->row[ $i ]->attributs->class ) and !empty( $section->row[ $i ]->attributs->class ) ) : ?>
class="<?php echo $section->row[ $i ]->attributs->class ?>"
<?php endif; ?>>
<?php for( $j = 0; $j < count( $section->row[ $i ]->cell ); $j++ ) : ?>
<<?php if( $key === 'head' or $j === 0 and $key !== 'foot' ) :?>th<?php else: ?>td<?php endif; ?>
<?php if( isset( $section->row[ $i ]->cell[ $j ]->attributs->id ) and !empty( $section->row[ $i ]->cell[ $j ]->attributs->id ) ) : ?>
id="<?php echo $section->row[ $i ]->cell[ $j ]->attributs->id ?>"
<?php endif; ?>
<?php if( isset( $section->row[ $i ]->cell[ $j ]->attributs->class ) and !empty( $section->row[ $i ]->cell[ $j ]->attributs->class ) ) : ?>
class="<?php echo $section->row[ $i ]->cell[ $j ]->attributs->class ?>"
<?php endif; ?>
<?php if( isset( $section->row[ $i ]->cell[ $j ]->attributs->row ) and !empty( $section->row[ $i ]->cell[ $j ]->attributs->row ) ) : ?>
rowspan="<?php echo $section->row[ $i ]->cell[ $j ]->attributs->row ?>"
<?php endif; ?>
<?php if( isset( $section->row[ $i ]->cell[ $j ]->attributs->column ) and !empty( $section->row[ $i ]->cell[ $j ]->attributs->column ) ) : ?>
colspan="<?php echo $section->row[ $i ]->cell[ $j ]->attributs->column ?>"
<?php endif; ?>>
<?php echo $section->row[ $i ]->cell[ $j ]->content ?>
</<?php if( $key === 'head' or $j === 0 and $key !== 'foot' ) :?>th<?php else: ?>td<?php endif; ?>>
<?php endfor; ?>
</tr>
<?php endfor; ?>
</t<?php echo $key; ?>>
<?php endforeach; ?>
</table>
heraus kam
<table>
<!-- COLUMN GROUPS -->
<colgroup id="id" class="class">
<col id="id" class="class" span="1">
<!-- soon -->
</colgroup>
<!-- CAPTION -->
<caption>Foo Bar</caption>
<!-- MATRIX -->
<!-- SECTION -->
<thead id="id" class="class">
<tr>
<th id="id" class="class" rowspan="1" colspan="1">
foobar
</th>
<!-- soon -->
</tr>
<!-- soon -->
</thead>
<tbody>
<!-- soon -->
</tbody>
<tfoot>
<!-- soon -->
</tfoot>
</table>
Ich habs mit abstrakten Eltern Klassen gemacht und Interfaces verwendet
lgmb
Hallo MB,
Whoa, was ist denn das für ein Template? Wieso <?php ... ?> auf jeder einzelnen Zeile? Und man darf in einem Template, wenn es mit PHP geschrieben ist, durchaus programmieren. Also Helper-Funktionen und -Variablen verwenden.
Unter anderem auch htmlspecialchars(), das fehlt bei Dir noch ganz - es sei denn das hast Du schon anderweit gemacht. Ich baue es hier deshalb bewusst nicht ein, aber ich denke, sowas gehört eigentlich ins Template, nicht ins Viewmodel.
Zum Beispiel könntest Du diesen Helper bauen:
function renderAttribute($attrName, $value) {
if (!empty($value))
echo " $attrName='$value'";
}
isset brauchst Du nicht, empty() ist ein Sprachkonstrukt, keine Funktion und prüft isset implizit mit ab. Im Handbuch steht:
A variable is considered empty if it does not exist or if its value equals FALSE
(Beachte: equals FALSE meint == FALSE
, nicht === FALSE
).
Damit schreibst Du ins Template:
<table<?=renderAttribute("id", $attributs->id)?><?=renderAttribute("class", $attributs->class)?>>
oder verlässt PHP für den Moment gar nicht (sollte unterm Strich aber egal sein):
<table<?=renderAttribute("id", $attributs->id) . renderAttribute("class", $attributs->class)?>>
Es geht aber noch deutlich besser. Du könntest deine Klasse TableAttributs
ertüchtigen (und vermutlich könnte sie sogar ElementAttributes
heißen, weil sie nicht nur für Tabellen funktioniert):
class ElementAttributes {
public function render(...$attributes) {
foreach ($attributes as $attr) {
if (!empty($this->$attrName))
echo " $attrName='{$this->$attrName}'";
}
}
}
Ich habe das ganze Attribut mit echo ausgegeben, statt dauernd zwischen PHP und HTML zu wechseln.
Damit hättest Du dann statt 7 Zeilen nur noch diese (und es ist auch kompakter, <?= ... ?> statt <? echo ...; ?> zu schreiben. Das predige ich schon länger 😉
<table <?=$attributs->render("id", "class")?> >;
Es wäre dann auch sinnvoll, hinter den beiden for-Schleifen jeweils eine Variable mit dem aktuellen Schleifenwert zu setzen. Oder gleich die foreach-Schleife zu verwenden - was aber nur sinnvoll ist wenn Du $i oder $j nicht brauchst...
<?php foreach ($section->row as $rowData) :
<tr <?=$rowData->attributs->render("id", "class")?> >
<?php for( $j = 0; $j < count( $rowData->cell ); $j++ ) :
$cellData = $rowData->cell[$j];
$cellTag = ( $key === 'head' or $j === 0 and $key !== 'foot' ) ? "th" : "td"; ?>
<<?=$cellTag . $cellData->attributs->render("id", "class", "rowSpan", "colSpan")?>>
<?=$cellData->content?>
</<?=$cellTag?>>
<?php endfor; ?>
</tr>
<?php endforeach; ?>
Das ist signifikant kleiner als dein Code. Ich find's auch besser lesbar.
Ob es sinnvoll ist, im Schleifenkern den PHP-Modus zu verlassen, weiß ich nicht. Vermutlich nicht. Außer < und > kommt ja eh alles aus PHP.
Schneller laufen wird's auch nicht, durch die Methodenaufrufe gehen einige Mikrosekunden drauf. Ob das jetzt sofort funktioniert weiß ich nicht, das ist teilweise in der Sandbox probiert, aber teilweise auch vom Hirn ins Forum gehauen.
Rolf
moin,
Whoa, was ist denn das für ein Template? Wieso <?php ... ?> auf jeder einzelnen Zeile?
Kontext wechsel 😕
Und man darf in einem Template, wenn es mit PHP geschrieben ist, durchaus programmieren. Also Helper-Funktionen und -Variablen verwenden.
function renderAttribute($attrName, $value) {
if (!empty($value))
echo " $attrName='$value'";
}
mir ist es deutlich lieber wenn ich BuiltIn-Funktionen nehme
Ich zerhacke immer alles und plaziere immer alles schön seeehr weit weg 😕. Hab da gute und weniger gute Erfahrungen gemacht.
Unter anderem auch htmlspecialchars(), das fehlt bei Dir noch ganz - es sei denn das hast Du schon anderweit gemacht.
hätte ich is aber nur n Test 😉
Ich baue es hier deshalb bewusst nicht ein, aber ich denke, sowas gehört eigentlich ins Template, nicht ins Viewmodel.
ich denke nicht, weil es ja nur ein Template ist. Aber ich bin im gegensatz zu dir (einen Profil) n echtes greenhorn 😏.
isset brauchst Du nicht, empty() ist ein Sprachkonstrukt, keine Funktion und prüft isset implizit mit ab. Im Handbuch steht:
…ja ich erinner mich da an was. Danke für die Auffrischung
Damit hättest Du dann statt 7 Zeilen nur noch diese (und es ist auch kompakter, <?= ... ?> statt <? echo ...; ?> zu schreiben. Das predige ich schon länger 😉
ja aber php.ini schaltet das per Default aus und es gibt da glaube ich Probleme mit DB bezügl. SQL-Injection oder so. Lasst mich erst in der PHP-Welt ankommen 😀.
Irgend wann baue ich mal einen simplen MarkDown-PlugIn.
Es wäre dann auch sinnvoll, hinter den beiden for-Schleifen jeweils eine Variable mit dem aktuellen Schleifenwert zu setzen. Oder gleich die foreach-Schleife zu verwenden - was aber nur sinnvoll ist wenn Du $i oder $j nicht brauchst...
da habe ich auch meine Problemchen wie ich das lösen kann. Dank für den Rat.
[…] Das ist signifikant kleiner als dein Code. Ich find's auch besser lesbar.
das ist es… ich werde mich apäter damit beschäftigen, weil ichs weniger wichtig finde, Wichtig! aber weniger.
Ob es sinnvoll ist, im Schleifenkern den PHP-Modus zu verlassen, weiß ich nicht. Vermutlich nicht. […]
Kontext wechsel. Wenn ich im HTML Template bin dann notiere ich z.B. <?php echo $foobar; ?>
und <?php if ( true ) : ?>
.
lgmb
Tach!
Damit hättest Du dann statt 7 Zeilen nur noch diese (und es ist auch kompakter, <?= ... ?> statt <? echo ...; ?> zu schreiben. Das predige ich schon länger 😉
ja aber php.ini schaltet das per Default aus und es gibt da glaube ich Probleme mit DB bezügl. SQL-Injection oder so.
Nein, <?=
steht seit Version 5.4 unabschaltbar zur Verfügung. Die Versionen, in denen das abschaltbar war sind ausgestorben (lies: unsupportet). Ein Zusammenhang zu DB-Handling und SQL-Injection oder jeglicher Art anderer Injection besteht auch nicht. Wenn ein Kontextwechsel vorhanden ist, muss der in beiden Schreibweisen in exakt gleicher Art und Weise berücksichtigt werden.
Ob es sinnvoll ist, im Schleifenkern den PHP-Modus zu verlassen, weiß ich nicht. Vermutlich nicht. […]
Jedenfalls ist es nicht sonderlich sinnvoll, zwischen ?>
und <?php
nur Whitespace stehen zu haben, der für die Ausgabe irrelevant ist. Da kann man gleich im PHP-Modus bleiben. Der erzeugte HTML-Code ist nur bedingt lesbar, weil ein dem ?>
nachfolgender Zeilenumbruch nicht in der Ausgabe erscheint, aber die Einrückungs-Whitespace-Zeichen. Da entsteht also ohne zusätzliche Zeilenschaltung nur eine lange Zeilenwurst. Wichtig ist in erster Linie die syntaktische Korrektheit, nicht die menschliche Lesbarkeit. Syntaktische Korrektheit kann auch ein Automat prüfen, dem der Whitespace egal ist. Inhaltliche Probleme geht man hingegen meist in der DOM-Ansicht an, die der Browser anhand der logischen Struktur des Dokuments präsentiert.
dedlfix.
moin,
ja aber php.ini schaltet das per Default aus und es gibt da glaube ich Probleme mit DB bezügl. SQL-Injection oder so.
Nein,
<?=
steht seit Version 5.4 unabschaltbar zur Verfügung. […]
wuste ich nicht. Danke für di Info!!!
Der erzeugte HTML-Code ist nur bedingt lesbar, weil ein dem
?>
nachfolgender Zeilenumbruch nicht in der Ausgabe erscheint, aber die Einrückungs-Whitespace-Zeichen.
habe ich mir gedacht!!! Bedankt
lgmb
Hallo MB,
mir ist es deutlich lieber wenn ich BuiltIn-Funktionen nehme
Du musst natürlich so programmieren, wie Du es am besten lesen und verstehen kannst.
Deswegen aber auf Helper-Funktionen zu verzichten und Megabytes an Boilerplate-Code zu tippen - ist das besser? Je mehr Code da steht, desto mehr musst Du lesen. Wenn der relevante Code aus ein paar Stecknadeln in Bergen von repetitivem Basiszeugs besteht, erschwert das die Fehlersuche ungemein. Sind nur meine 2ct dazu, die Entscheidung bleibt bei Dir. Ich werde dir - weil ich keine Akten über meine Gesprächspartner führe und sowas hier schnell wieder vergesse - vermutlich trotzdem regelmäßig dazu was erzählen wenn Du Codebeispiele bringst. Nimm's mir dann nicht übel 😉
Rolf
moin,
mir ist es deutlich lieber wenn ich BuiltIn-Funktionen nehme
Du musst natürlich so programmieren, wie Du es am besten lesen und verstehen kannst.
da hast du natürlich recht. Es läst sich wohl nicht vermeiden aber da wo ich es vermeiden kann, werde ich nichtt drauf zurück greifen, esd sein den es ist im Templates üblich. Mit wachsendem Code wird es auf jeden Fall der Fall sein 😕.
Ich werde dir […] vermutlich trotzdem regelmäßig dazu was erzählen wenn Du Codebeispiele bringst. Nimm's mir dann nicht übel 😉
Ach iwo. Immer her damit.
lgmb
Nun, wie wärs denn damit:
$t = new TableConstruktor( $tableprops );
$t->tr( $rowprops );
$t->tbody($options);
$t->th($opts);
$t->td($opts);
Da hast Du alles in einer Klasse und ruft nur noch die Methoden auf! Wobei die übergebenen Options auch die Zelleninhalte und Attribute enthalten.
MFG
Hier mal ein Beispiel was Du auch auf Deine Tabellenklasse anwenden kannst:
# Klasse zum Erstellen einer sitemap.xml /urlset
class Sitemap{
function __construct($opts){
$opts = array_merge( array(
'proto' => '',
'domain' => '',
'version' => '1.0',
'encoding' => 'UTF-8',
'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9'
), $opts);
$this->OPTS = $opts;
$this->URLSET = array();
$this->BUFFER = '';
}
function add($url){
$this->URLSET[] = $url;
}
function out(){
$this->BUFFER = sprintf("<?xml version='%s' encoding='%s'>\n", $this->OPTS['version'], $this->OPTS['encoding']);
$this->BUFFER .= sprintf("\t<urlset xmlns='%s'>\n", $this->OPTS['xmlns']);
$this->urlset();
$this->BUFFER .= "\t</urlset>";
return $this->BUFFER;
}
function urlset(){
foreach( $this->URLSET as $url ){
$this->BUFFER .= sprintf("\t\t<url><loc>%s</loc></url>\n", $url);
}
}
}
$s = new Sitemap( array(
'proto' => 'https',
'domain' => 'example.org'
));
$s->add('/index.html');
$s->add('/foo.html');
$s->add('/find');
echo $s->out();
Damit ist es einfach, weitere XML-Elemente einzubauen. OOP lebt von der Vielgestaltigkeit und umgekehrt.
MFG
Hallo pl,
Polymorphie? Wo? Ich sehe keine Klassenvererbung und keine Interfaces. Eins von beiden bräuchtest Du dafür schon.
Zum Code:
Du solltest deine Properties deklarieren.
URLSET, urlset und OPTS sollten private sein.
Nicht jede Variable muss ein Property sein. BUFFER sollte statt dessen lokale Variable sein und als Wert von urlset zurückgegeben werden. Das Ding ist temporär.
Die Namen einer öffentlichen Schnittstelle sollen so sprechend wie möglich sein. Sprechende Namen sind besser als jede Doku. Also - beispielsweise - toXmlString statt out, addUrl statt add, getUrlSetAsXmlFragment statt urlset und urlList statt BUFFER.
Wo findet der Kontextwechsel zu XML für die Zeichen &, ', ", < und > statt? Zumindest das & kann gern mal Teil einer URL sein, bei den vier anderen mag es weniger wichtig sein.
Die Parametrierbarbeit von UTF-8 und XML Version ist sinnlos. Sitemaps müssen UTF-8 codiert sein, und einen Sinn in einer XML 1.1 Sitemap sehe ich nicht, da kein Freitext enthalten sein darf und eine URL immer in XML 1.0 darstellbar ist.
Dass die Sitemap-Elemente lastmod, changefreq und priority nicht aufscheinen, ist sicherlich der Kürze des Beispiels geschuldet, oder?
Rolf
Dass die Sitemap-Elemente lastmod, changefreq und priority nicht aufscheinen, ist sicherlich der Kürze des Beispiels geschuldet, oder?
Nein. Warum das fehlt hab ich doch geschrieben. MFG
Hallo pl,
mag sein. Aber nicht in diesem Thread.
Rolf