Yadgar: PHP: Variablen nehmen falsche Werte an

Hi(gh)!

Folgendes Problem: Auf einer Eingabeseite für eine Datenbanktabelle will ich eine Art vereinfachte phpmyadmin-Anzeige für die enthaltenen Datensätze (siehe angehängten Screenshot) programmieren.

Man soll sowohl mit explizit über ein HTML-Formular eingegebenen Start- und Endwerten für die Datensatznummern blättern können als auch mittel vier Links, die per GET Startwerte übergeben.

Der erste dieser Links setzt den Startwert ("offset") auf 0, der zweite zieht 10 (bzw. soviele, wie möglich sind, ohne dass der Startwert negativ wird) vom aktuellen Startwert ab, der dritte addiert 10 dazu, der vierte ermittelt die Gesamtzahl der Datensätze und zieht davon 10 ab, so dass die letzten 10 Datensätze in der Tabelle angezeigt werden.

Link Nr. 1 und 4 funktionieren einwandfrei, Link 2 und 3 aber bewirken immer nur -10 (was natürlich zu einer Fehlermeldung führt) bzw. 10 als Startwert. Aus irgendeinem Grund ist der Startwert bei jedem neuen Aufruf wieder 0! Wie kann ich der Seite beibringen, dass sie sich den letzten Wert merkt - geht das nur über eine externe Textdatei?

Hier ist der Code:

    <div style="position:absolute; width:74%; height:40%; top:0%; left:26%; padding-left:5px; background-color:#99ff99">
<?php
  if (!isset($_GET["listpos"]) || !isset($_POST["liststart"]) || !isset($_POST["listend"]))
  {
    $offset = 0;
    $range = 10;
  }
  else
  {
    if ($_POST["liststart"] < 0 || $_POST["listend"] - $_POST["liststart"] < 1)
    {
      echo "<p class='error'>Fehler: erste Datensatznummer kleiner als 1 oder größer als letzte Datensatzummer!</p>";
    }
    else
    {
      $offset = trim(htmlspecialchars($_POST["liststart"]));
      $range = trim(htmlspecialchars($_POST["listend"])) - $offset;
    }
  }
  if (isset($_GET["listpos"]))
  {
    $sql = "SELECT COUNT(*) FROM land";
    $res = $db->query($sql);
    $zeile = $res->fetch_array();
    $all = $zeile[0];
    $range == 10;
    switch ($_GET["listpos"])
    {
      case "first10":
	$offset = 0;
	break;
      case "previous10":
	$offset = $offset-10;
	echo $offset;
	break;
      case "next10":
	$offset = $offset+10;
	echo $offset;
	break;
      case "last10":
	$offset = $all-10;
	echo $offset;
	break;
    }
  }
  if ($offset < 0)
  {
    echo "Fehler: erste Datensatznummer kleiner als 1!";
  }
  else
  {
    $sql = "SELECT * FROM land LIMIT 0".$offset.", ".$range.";";
    $res = $db->query($sql);
    echo "<table>\n";
    echo "  <tr>\n";
    echo "    <th>Operationen</th>\n";
    echo "    <th>id</th>\n";
    echo "    <th>name</th>\n";
    echo "  </tr>\n"; 
    while ($zeile = $res->fetch_array())
    {
      echo "  <tr>\n";
      echo "    <td>&nbsp;</td>\n";
      echo "    <td>{$zeile['id']}</td>\n";
      echo "    <td>{$zeile['name']}</td>\n";
      echo "  </tr>\n";
    }
    echo "</table>\n";
  }
?>
      
      <form style="position:absolute; top:70%" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" method="POST">
	Datensätze von Nr.
	<input type="text" size="2" maxlength="2" name="liststart"> bis
	<input type="text" size="2" maxlength="2" name="listend">
	<input type="submit" value="anzeigen">
      </form>
      <p style="position:absolute; top:80%">
	<a href="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>?listpos=first10">Erste 10</a> | 
	<a href="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>?listpos=previous10">vorige 10</a> | 
	<a href="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>?listpos=next10">nächste 10</a> | 
	<a href="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>?listpos=last10">letzte 10 Datensätze</a>
      <p>
    </div>

Eingabe Tabelle land Der Screenshot:

  1. Hello Yadgar,

    ich machs's mal kurz: dein Code ist Schrott. :-/

    So gehört z. B. htmlspecialchars() nicht in den Dateneingang (E), sondern beim Kontextübergang in den Datenausgang (A) der guten alten EVA-Strategie.

    Du wolltest sicherlich die Dateneingabe aus $_POST escapen, und das müsste hier z. B. mit mysqli_real_escape_string() geschehen. Da es sich um numerische Werte handelt, deren Bounds ohnehin noch geprüft werden, würde ich alternativ zu intval() tendieren.

    Um dein eigentliches Problem mit der Logik zu klären, male bitte zunächst einen PAP. Dann sehen wir weiter.

    Glück Auf
    Tom vom Berg

    --
    Es gibt nichts Gutes, außer man tut es!
    Das Leben selbst ist der Sinn.
    1. Hello Yadgar,

      ich machs's mal kurz: dein Code ist Schrott. :-/

      Ah ja.

      So gehört z. B. htmlspecialchars() nicht in den Dateneingang (E), sondern beim Kontextübergang in den Datenausgang (A) der guten alten EVA-Strategie.

      Florence MAURICE empfiehlt in "PHP 7 und MySQL", Heidelberg 2019, etwaige missbräuchliche Eingaben möglichst früh abzufangen!

      geschehen. Da es sich um numerische Werte handelt, deren Bounds ohnehin noch geprüft werden, würde ich alternativ zu intval() tendieren.

      Kann man ja machen...

      Um dein eigentliches Problem mit der Logik zu klären, male bitte zunächst einen PAP. Dann sehen wir weiter.

      Logikproblem hat sich mittlerweile geklärt, ich lasse den jeweils letzten Startwert für die Datensatz-Ausgabe aus einer kleinen (2 Bytes) Textdatei einlesen und bei Bedarf zurückschreiben!

      Bis bald im Khyberspace!

      Yadgar

      1. Hallo Yadgar,

        So gehört z. B. htmlspecialchars() nicht in den Dateneingang (E), sondern beim Kontextübergang in den Datenausgang (A) der guten alten EVA-Strategie.

        Florence MAURICE empfiehlt in "PHP 7 und MySQL", Heidelberg 2019, etwaige missbräuchliche Eingaben möglichst früh abzufangen!

        Ja. Aber immer für den Empfänger aufbereitet, für den es gefährlich werden kann. Wenn Daten in die Datenbank geschrieben werden sollen, muss verhindert werden, dass deine Datenbank Schaden nimmt oder deine Daten verändert werden: z. B. mysqli_real_escape_string().

        Wenn Daten im Browser angezeigt werden sollen, muss verhindert werden, dass die Anzeige kaputt geht oder fremdes JavaScript ausgeführt wird: htmlspecialchars().

        Bis demnächst
        Matthias

        --
        Pantoffeltierchen haben keine Hobbys.
        ¯\_(ツ)_/¯
      2. Hi,

        Logikproblem hat sich mittlerweile geklärt, ich lasse den jeweils letzten Startwert für die Datensatz-Ausgabe aus einer kleinen (2 Bytes) Textdatei einlesen und bei Bedarf zurückschreiben!^

        Gibt es nur einen einzigen User? Ansonsten überschreibt ein User den Wert eines anderen Users ...

        In der Session wäre der Wert vermutlich besser aufgehoben …

        cu,
        Andreas a/k/a MudGuard

      3. Hallo

        So gehört z. B. htmlspecialchars() nicht in den Dateneingang (E), sondern beim Kontextübergang in den Datenausgang (A) der guten alten EVA-Strategie.

        Florence MAURICE empfiehlt in "PHP 7 und MySQL", Heidelberg 2019, etwaige missbräuchliche Eingaben möglichst früh abzufangen!

        Entweder sie empfiehlt ein falsches Vorgehen, oder du gibst ihre Empfehlung stark verkürzt und sinnentstellt wieder. Das mag durchaus Folge einer Fehlinterpretation sein.

        Grundsätzlich erst einmal: Missbräuchliche oder nicht ins Datenmodell passende Eingaben bei ihrem eintreffen abzufangen, ist richtig. An dieser Stelle eine Maskierung für einen ganz bestimmten Kontext einzufügen, ist jedoch falsch. Dies zumal dann, wenn dieser Kontext im gegenwärtigen Verarbeitungsschritt absolut keine Rolle spielt.

        Halte dich an den Grundsatz „Alles zu seiner Zeit“.

        Gehen wir mal das typische Werden einer zu speichernden Eingabe durch.

        1. Eingabe in einem HTML-Formular und absenden an ein serverseitiges Skript.
        2. Prüfung der Eingaben auf Plausibilität und Korrektheit (soweit möglich) in jenem Skript.
          • Texteingaben lassen sich grundsätzlich nur auf Plausibilität prüfen (zum Beispiel Mindest- und/oder Maximallänge der Eingabe), da jede Eingabe anders sein kann, als die zuvor getätigte.
          • Bei den Werten aus Checkboxen, Radiobutton-Gruppen und Select-Feldern sind die möglichen Werte bekannt. Die tatsächliche Eingabe kann also gegen eine bekannte Menge von Werten geprüft werden.
          • Auch Eingaben aus Zahlen-, E-Mail-, Datums- und ähnlichen Feldern können auf die Einhaltung bestimmter Regeln geprüft werden.
        3. Speichern der erfolgreich geprüften Eingaben (zum Beispiel) in einer Datenbank. Hier erfolgt die eine kontextgerechte Maskierung in dem Prozess von Eingabe, Aufbereitung und Speicherung, passend zur Datenbank (beispielsweise mit mysqli_real_escape_string für an eine MySQL-Datenbank zu übertragende Zeichenketten). Maskierungen für eine etwaige Ausgabe, zum Beispiel als Bestandteil eines HTML-Dokuments, erfolgen zu diesem Zeitpunkt explizit nicht.

        Nun folgt eine Ausgabe als HTML-Dokument.

        1. Lies die auszugebenden Daten aus der Datenbank.
        2. Führe eventuell notwendige Bearbeitungen der Daten durch (zum Beispiel Rundung und Formatierung von Zahlen).
        3. Maskiere jetzt, bei der Generierung der Ausgabe als letztem Schritt des Prozesses, die Daten passend zum Kontext HTML (in PHP mit htmlspecialchars).

        Warum erfolgt die Maskierung erst vor der Ausgabe? Weil die Daten eventuell vor der Ausgabe noch umformatiert oder anderweitig bearbeitet werden sollen und weil man das mit Rohdaten machen will und nicht mit bereits für HTML aufbereiteten Daten.

        Warum will man in der Datenbank die Rohdaten aufheben und nicht die, die bereits für eine HTML-Ausgabe aufbereitet sind? Weil man die Daten eventuell auch in anderen Kontexten ausgeben will, wo die Aufbereitung für HTML bestenfalls unnütz und schlimmstenfalls falsch ist. Für die Ausgabe von Text in einer Nur-Text-E-Mail ist die HTML-Maskierung falsch. Für eine Ausgabe mehrerer Datensätze als CSV-Datei ist die HTML-Maskierung falsch. Für eine Ausgabe als PDF-Dokument ist die HTML-Maskierung falsch. Für eine Ausgabe von Daten auf Papier ist die HTML-Maskierung falsch, außer, es wird ein HTML-Dokument als Basis des Ausdrucks benutzt.

        In all diesen Fällen will man die Rohdaten haben und nicht aus für eine HTML-Ausgabe maskierten Daten eben diese HTML-Maskierungen herausprökeln.

        Tschö, Auge

        --
        Ein echtes Alchimistenlabor musste voll mit Glasgefäßen sein, die so aussahen, als wären sie beim öffentlichen Schluckaufwettbewerb der Glasbläsergilde entstanden.
        Hohle Köpfe von Terry Pratchett
      4. So gehört z. B. htmlspecialchars() nicht in den Dateneingang (E), sondern beim Kontextübergang in den Datenausgang (A) der guten alten EVA-Strategie.

        Florence MAURICE empfiehlt in "PHP 7 und MySQL", Heidelberg 2019, etwaige missbräuchliche Eingaben möglichst früh abzufangen!

        Das htmlspecialchars() ist aber kein "Abfangen", sondern eine ("teure") "Veränderung" der Daten. Das "frühe Abfangen" hat den Sinn, bei Angriffsversuchen, die regelmäßig durch eine Vielzahl von Requests (z.B. Versuche von SQL-Injections) stattfinden, eine Belastung des Servers durch den jeweiligen Versuch der Erstellung einer Antwort zu vermeiden, also den Programmablauf möglichst frühzeitig, insbesondere vor "teuren" Operationen abzubrechen und ggf. durch weitere Maßnahmen (Loggen + passende Einrichtung z.B. von fail2ban) Zugriffe von der IP des mutmaßlichen Angreifers (zeitweilig) zu unterbinden.

  2. Hallo Yadgar,

    folgende Hinweise hätte ich zu deinem Code:

    • nach DB-Abfragen muss man zwingend überprüfen, ob die API-Methoden einen Fehler gemeldet haben

    • es ist kompakter und meiner Meinung nach besser lesbar, <?= ... ?> statt <?php echo ...; ?> zu verwenden

    • Ein Webserver weiß (ohne besondere Maßnahmen) nichts über frühere Requests, insbesondere merkt sich ein PHP Script nicht die Werte von Variablen aus früheren Seitenabrufen. Deswegen enthält deine $offset Variable den Wert vom letzten Seitenabruf nicht mehr.

    • Aus diesem Grund ist es auch ungeschickt, einen Link so zu konstruieren, dass er relativ arbeitet. Den aktuellen Offset in einer Datei zu speichern ist grundfalsch. In der Session würde funktionieren, aber richtig ist es, die Links so zu erzeugen, dass der nächste Offset direkt vom Link übergeben wird. Also: erste 10: ?listpos=0, vorige 10: ?listpos=<?=$prev10 ?>, nächste 10: ?listpos=<?=$next10 ?>, letzte 10: ?listpos=<?=$last10?>

    • Da stehen Variablen $prevPage, $nextPage, $lastPage, und nicht einfach $offset-10, $offset+10 und $all-10. Grund 1: Die Seitengröße mag variieren. Grund 2: Du musst diese Werte basierend auf $offset und $all berechnen, aber du musst auch prüfen, ob die Werte plausibel sind. Es muss gelten: $prevPage >= 0, $lastPage >= 0 und $nextPage <= $lastPage. Beim Wert für $lastPage solltest Du ggf. auf den nächsten Zehner aufrunden. Hast Du bspw. 12 Einträge, möchtest Du die letzte Seite vermutlich ab Offset 10 anzeigen und nicht 2.

    • Beim Seitenaufruf darfst Du den in listPos übergebenen neuen $offset natürlich nicht blindlings verwenden, sondern musst sicherstellen, dass es ein Integer-Wert im Bereich 0 bis $all-1 ist.

    • die geposteten Werte für $liststart und $listend mit trim und htmlspecialchars zu behandeln ist sinnlos.

      • Das sind Eingabeparameter, die Werte werden nicht ausgegeben. htmlspecialchars braucht man nur zur Ausgabe.
      • Du erwartest hier Zahlen, und du rechnest sogar damit. Also konvertiere die Werte auch in Zahlen (mit intval). Sie vorher von Leerstellen zu befreien, ist nicht nötig. Du kannst sie, wenn Du magst, vorher mit einer Regex auf eine Ziffernfolge prüfen,
    • Beschäftige Dich mit Flexbox und Grid. Es ist weder geschickt noch Stand der Technik, die Zonen auf einer Seite mit position:absolute zu platzieren. Sowas gehört ohnehin nicht in ein style-Attribut, sondern ein einen <style> Abschnitt im <head>, bzw. in eine CSS Datei. Wenn Du die Seite nur für Dich brauchst und sie nicht auf einem Gerät mit kleinem Bildschirm darstellen willst, will ich nicht auf responsivem Layout herumreiten. Aber - sollte das mal kommen, ist es mit Grid VIEL einfacher machbar als mit absolut positionierten Brocken.

    Rolf

    --
    sumpsi - posui - clusi
    1. @@Rolf B

      • es ist kompakter und meiner Meinung nach besser lesbar, <?= ... ?> statt <?php echo ...; ?> zu verwenden

      Entscheidender finde ich noch, Markup (HTML-Tags) nicht mit echo auszugeben, sondern die PHP-Ausgaben innerhalb der HTML-Tags vorzunehemen. Also nicht

      <?php
          echo "<table>\n";
          echo "  <tr>\n";
          echo "    <th>Operationen</th>\n";
          echo "    <th>id</th>\n";
          echo "    <th>name</th>\n";
          echo "  </tr>\n"; 
          while ($zeile = $res->fetch_array())
          {
            echo "  <tr>\n";
            echo "    <td>&nbsp;</td>\n";
            echo "    <td>{$zeile['id']}</td>\n";
            echo "    <td>{$zeile['name']}</td>\n";
            echo "  </tr>\n";
          }
          echo "</table>\n";
        }
      ?>
      

      sondern

        <table>
          <thead>
            <tr>
              <th>Operationen</th>
              <th>id</th>
              <th>name</th>
            </tr>
          </thead>
          <tbody>
      <?php while ($zeile = $res->fetch_array()): ?>
            <tr>
              <td></td>
              <td>{<?= $zeile['id'] ?>}</td>
              <td>{<?= $zeile['name'] ?>}</td>
            </tr>
      <?php endwhile; ?>
          </tbody>
        </table>
      

      thead und tbody hab ich ergänzt. Aber was soll die leere Tabellenzelle am Anfang jeder Zeile? Ich hab sie mal leer gemacht; &nbsp; gelöscht.

      LLAP 🖖

      --
      „Man kann sich halt nicht sicher sein“, sagt der Mann auf der Straße, „dass in einer Gruppe Flüchtlinge nicht auch Arschlöcher sind.“
      „Stimmt wohl“, sagt das Känguru, „aber immerhin kann man sich sicher sein, dass in einer Gruppe Rassisten nur Arschlöcher sind.“

      —Marc-Uwe Kling
    2. Hi(gh)!

      Hallo Yadgar,

      folgende Hinweise hätte ich zu deinem Code:

      • nach DB-Abfragen muss man zwingend überprüfen, ob die API-Methoden einen Fehler gemeldet haben

      Nach $db new mysqli(...) habe ich das auch gemacht (by they way, ist die() eigentlich deprecated? Ich habe dabei mal eine Fehlermeldung bekommen...)... und nach darauf folgenden Abfragen ist es natürlich am einfachsten, auf !$result zu prüfen... aber wie kommt man an die konkrete Error-Nummer ran?

      Bis bald im Khyberspace!

      Yadgar

      1. Hallo Yadgar,

        muss ich dir das Handbuch vorlesen?

        Oder reicht ein Link?

        Siehe connect_error, connect_errno, errno, error und error_list am mysqli Objekts. Da steht alles drin.

        Rolf

        --
        sumpsi - posui - clusi
        1. Hi(gh)!

          Oder reicht ein Link?

          Siehe connect_error, connect_errno, errno, error und error_list am mysqli Objekts. Da steht alles drin.

          Ich habe jetzt die Fehlerabfragen in Funktionen gepackt:

          function ccheck(object $db)
          {
            if ($db->connect_error)
            {
              echo "Fehler bei der Verbindung:".$db->connect_error();
              exit(1);
            }
          }
            
          function qcheck($res, object $db)
          {
            if (!$res)
            {
              echo "<br>Die Abfrage kann nicht ausgeführt werden.\n";
              echo "<br>Fehler: ".$db->error;
              exit(1);
            }
          }
          
          

          und frage mich (und natürlich auch euch) bei dieser Gelegenheit, ob mysqli-Objekte (in diesem Fall $db) auch global definiert werden können... im Manual habe ich dazu nichts gefunden!

          1. Hallo Yadgar,

            ja, kannst Du. Du solltest aber sicher sein, dass Du auch tatsächlich immer nur eins brauchst.

            Rolf

            --
            sumpsi - posui - clusi
            1. Hallo Yadgar,

              ja, kannst Du. Du solltest aber sicher sein, dass Du auch tatsächlich immer nur eins brauchst.

              Rolf

              Wie würde das denn syntaktisch aussehen?

              Etwa

              global new mysqli->connect(...) ?

              1. Hallo Yadgar,

                hatte ich Dir schon mal das PHP Handbuch nahegelegt?

                global new mysqli->connect(...)
                

                So keinesfalls, aus zwei Gründen. Erstens legt man nicht bei der Definition fest, dass etwas global sein soll, sondern da, wo es verwendet werden soll. Und zweitens verwendest Du entweder mysqli_connect(...) oder new mysqli(...). Eine Methode connect auf dem mysqli-Objekt gibt es nicht.

                Mit "global" sagt man PHP, dass eine bestimmte Variable nicht im lokalen Scope gelten soll, sondern aus dem globalen Scope verwendet werden soll.

                // Erzeuge die Variable $db. Da dies außerhalb einer Funktion
                // oder Klasse geschieht, ist die Variable global.
                $db = new mysqli(...);
                
                function do_something() 
                {
                   // Das schlägt fehl. Per Default nimmt PHP an, dass Variablen
                   // nicht global sind.
                   $db->query(...);
                }
                
                function use_something() 
                {
                   // So geht's. PHP sucht $db jetzt nicht mehr im Geltungs-
                   // bereich der Funktion, sondern im globalen Bereich.
                   global $db;
                   $db->query(...);
                }
                

                Rolf

                --
                sumpsi - posui - clusi
    3. Hi(gh)!

      • Aus diesem Grund ist es auch ungeschickt, einen Link so zu konstruieren, dass er relativ arbeitet. Den aktuellen Offset in einer Datei zu speichern ist grundfalsch.

      Wieso?

      Bis bald im Khyberspace!

      Yadgar

      1. <Ingrid>

        Wieso?

        </Ingrid>

        Ich kann es mir denken: konkurrierende Zugriffe im Mehrbenutzerbetrieb - nur ist der in meinem Fall gar nicht vorgesehen!

  3. @@Yadgar

          <form style="position:absolute; top:70%" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" method="POST">
    	Datensätze von Nr.
    	<input type="text" size="2" maxlength="2" name="liststart"> bis
    	<input type="text" size="2" maxlength="2" name="listend">
    	<input type="submit" value="anzeigen">
          </form>
    

    Wie ich unlängst erst wieder sagte:

    • Alle Formularfelder brauchen eine Beschriftung.
    • Für Buttons gibt es das button-Element. type="submit" ist default, kann also auch weggelassen werden.
    <form action="<?= htmlspecialchars($_SERVER["PHP_SELF"]) ?>" method="POST">
    	<label>Datensätze von Nr.
    	  <input type="text" size="2" maxlength="2" name="liststart">
    	</label>
    	<label>bis
    	  <input type="text" size="2" maxlength="2" name="listend">
    	<label>
    	<button>anzeigen</button>
    </form>
    

    LLAP 🖖

    --
    „Man kann sich halt nicht sicher sein“, sagt der Mann auf der Straße, „dass in einer Gruppe Flüchtlinge nicht auch Arschlöcher sind.“
    „Stimmt wohl“, sagt das Känguru, „aber immerhin kann man sich sicher sein, dass in einer Gruppe Rassisten nur Arschlöcher sind.“

    —Marc-Uwe Kling
    1. Hi(gh)!

      Wie ich unlängst erst wieder sagte:

      • Alle Formularfelder brauchen eine Beschriftung.
      • Für Buttons gibt es das button-Element. type="submit" ist default, kann also auch weggelassen werden.

      submit als default kenne ich, ja. button ist mir neu, das liegt daran, dass ich erstmal versuche, mir das aktuelle PHP und MySQL draufzuschaffen, bevor ich zu HTML 5 übergehe...

      ...meine Güte, ich bin ein Amateur, der sich alleine in seinem Sozialhilfe-Wohnklo ein lokales(!!! Mit nur mir als User!) Backend für eine Datenbank zusammenklöppelt, keine Softwarefirma, in der Hundertschaften von Einser-Informatikabsolventen bis in die Haarspitzen mit Kokain vollgedröhnt in 120-Stunden-Wochen das Universum neu erfinden!

      Bis bald im Khyberspace!

      Yadgar

      1. @@Yadgar

        button ist mir neu, das liegt daran, dass ich erstmal versuche, mir das aktuelle PHP und MySQL draufzuschaffen, bevor ich zu HTML 5 übergehe...

        Das button-Element gab’s auch schon in HTML 4; also praktisch schon immer™.

        ...meine Güte, ich bin ein Amateur

        Das ist kein Grund, es nicht vernünftig zu machen. Es gibt schon genügend Profis, die unsäglichen Code verzapfen.

        --
        „Man kann sich halt nicht sicher sein“, sagt der Mann auf der Straße, „dass in einer Gruppe Flüchtlinge nicht auch Arschlöcher sind.“
        „Stimmt wohl“, sagt das Känguru, „aber immerhin kann man sich sicher sein, dass in einer Gruppe Rassisten nur Arschlöcher sind.“

        —Marc-Uwe Kling
        1. Hi(gh)!

          Das ist kein Grund, es nicht vernünftig zu machen. Es gibt schon genügend Profis, die unsäglichen Code verzapfen.

          ...und deswegen sollen ausgerechnet die Amateure die Programmierkultur retten? Das erinnert mich an das Gewese um die "Neue Bürgerlichkeit" vor 15 Jahren, als in den (konservativen) Feuilletons hektoliterweise Krokodilstränen über die angebliche oder tatsächliche Verwahrlosung der Unterschicht vergossen wurden, über Flachbildfernseher, Rauchen, Dosenbier, Übergewicht, Fast Food, Nagelstudios, Tätowierungen, Billigflugreisen, tiefergelegte BMWs bla bla bla. Dabei sind es oft genug die hochtugendsamen bürgerlichen Leistungsträger mit Arnulf Baring und Paul Nolte im Aktenkoffer, die selbst zwar die edelstahlglänzenden Renommierküchen in ihren Speckgürtel-Domizilen stehen haben, aber vor lauter Karrierestress gar nicht zum Selbstkochen kommen, die ihre Kinder zwar nicht vor der Playstation, aber dafür in Internaten parken, deren Ökobilanz verheerender ist als die ganzer Hartz-IV-Hochhäuser und so weiter und so fort. Die Unterschicht wurde von diesen Leuten mit moralischen Ansprüchen überfrachtet, die sie selbst auch nicht erfüllen wollten, genauso kommt mir dein Anspruch an die Programmier-Amateure vor: die edlen Wilden, die uns doch bitte gefälligst erlösen sollen.

          Profi-Programmierer machen von früh bis spät, sieben Tage die Woche, nichts anderes als programmieren, programmieren, programmieren. Nervender, ablenkender Alltagsschlamassel fällt in Idealfall gar nicht an, weil man sowieso in der Firma (oder wenigstens in einem schicken Boardinghouse in einer angesagten IT-Metropole, selbstredend mit vollem Hotelservice) wohnt und schläft, das Essen per Pizzabote kommt und den Dreck die georgische Putzfrau wegmacht. Amateuren stehen alle diese Annehmlichkeiten nicht zur Verfügung - was die Zeit, die man als Amateur zum Erwerb von Programmier-Routine wie auch von theoretischem Programmierwissen aufbringen kann, begrenzt. Und ausgerechnet Amateure sollen jetzt besseren Code schreiben als Profis?

          Säße ich in einer ergonomisch perfekten, atablank cleanen IT-Umgebung statt in einem engen, chaotischen, versifften Vorstadt-Wohnklo und könnte mich 16 Stunden am Tag ganz aufs Coden konzentrieren, sähen meine Programme auch anders aus...

          1. Hallo Yadgar,

            Profi-Programmierer machen von früh bis spät, sieben Tage die Woche, nichts anderes als programmieren, programmieren, programmieren. Nervender, ablenkender Alltagsschlamassel fällt in Idealfall gar nicht an, weil man sowieso in der Firma (oder wenigstens in einem schicken Boardinghouse in einer angesagten IT-Metropole, selbstredend mit vollem Hotelservice) wohnt und schläft, das Essen per Pizzabote kommt und den Dreck die georgische Putzfrau wegmacht.

            Falls Du mal eine Stelle als Profi-Programmierer bekommen solltest, wirst Du sehr enttäuscht sein.

            Spaß beiseite - Gunnar ist für Maximalforderungen im UI-Bereich bekannt. Wenn Du eine öffentlich verfügbare Seite machst, ist das meiste davon für die heutige Vielfalt an Geräten und Benutzern unverzichtbar, oder Du schließt Anwender aus. Wenn Du eine Seite nur für Dich machst, wird sie Dir wahrscheinlich nicht zerbröseln weil Du diese Dinge nicht beachtet hast. Aber vielleicht doch. „Hey, tolle Seite, kann ich darauf auch Zugriff haben?“ - und in dem Moment fängt jedes "works for me" an zu stinken. Nur meine 2 Cent...

            Rolf
            Profi-Programmierer seit 34 Jahren.

            --
            sumpsi - posui - clusi
    2. Hi(gh)!

      ...wenn sie auch von (Triggerwarnung: politisch unkorrekter Sprachgebrauch!) Sehbehinderten benutzt werden sollen - dann muss natürlich auch Sprachausgabe installiert sein. Da es hier aber um ein lokales Backend mit mir als einzigem User geht, wage ich zu behaupten, dass ich darauf verzichten kann - meine Augen sind scharf genug, um die Eingabefelder zu erkennen, und auch der mit Label vergrößerte Klickbereich ist entbehrlich.

      Bis bald im Khyberspace!

      Yadgar

      1. @@Yadgar

        ...wenn sie auch von (Triggerwarnung: politisch unkorrekter Sprachgebrauch!) Sehbehinderten benutzt werden sollen - dann muss natürlich auch Sprachausgabe installiert sein. Da es hier aber um ein lokales Backend mit mir als einzigem User geht

        Noch. Vielleicht soll deine Anwendung auch mal von anderen bedient werden. Vielleicht entwickelst du mal eine andere Anwendung.

        Außerdem lesen hier auch andere mit, die Anwendungen fürs Web entwickeln.

        wage ich zu behaupten, dass ich darauf verzichten kann - meine Augen sind scharf genug, um die Eingabefelder zu erkennen

        Noch. Möge dir deine Sehschärfe lange erhalten bleiben; aber sie nimmt mit zunehmendem Alter ab.

        und auch der mit Label vergrößerte Klickbereich ist entbehrlich.

        Noch. Mögen dir deine motorischen Fähigkeiten lange erhalten bleiben; aber auch diese nehmen mit zunehmendem Alter ab.

        LLAP 🖖

        --
        „Man kann sich halt nicht sicher sein“, sagt der Mann auf der Straße, „dass in einer Gruppe Flüchtlinge nicht auch Arschlöcher sind.“
        „Stimmt wohl“, sagt das Känguru, „aber immerhin kann man sich sicher sein, dass in einer Gruppe Rassisten nur Arschlöcher sind.“

        —Marc-Uwe Kling
        1. Moin,

          können wir bitte den Satz

          Außerdem lesen hier auch andere mit, die Anwendungen fürs Web entwickeln.

          in Gold eingerahmt übers Forum hängen? Danke.

          Viele Grüße
          Robert