PHP rocks! wünscht allen Mitgliedern einen guten Rutsch ins neue Jahr 2017 !!!
Hinweis: Das Forum zieht um! Um keine Datenverluste zu haben, schalten wir zwecks Übernahme der Daten das Forum am Sonntag, den 24.04.2016 um ca. 21:00 Uhr offline und passen anschliessend die DNS-Einträge an.
www.php-rocks.de wird euch dann nach den Aktualisierungen der DNS-Server wieder wie gewohnt uneingeschränkt zur Verfügung stehen.
Danke für euer Verständnis!

Hallo, Gast
Du musst dich registrieren bevor du auf unserer Seite Beiträge schreiben kannst.

Benutzername/E-Mail:
  

Passwort
  





Durchsuche Foren

(Erweiterte Suche)

Foren-Statistiken
» Mitglieder: 291
» Neuestes Mitglied: awajonn11
» Foren-Themen: 171
» Foren-Beiträge: 901

Komplettstatistiken

Aktive Themen
Kaufen Sie ein TELC-GOETH...
Forum: Off Topic
Letzter Beitrag: awajonn11
12.04.2024, 13:37
» Antworten: 0
» Ansichten: 183
Kaufen Sie ein TELC-GOETH...
Forum: PHP Basics
Letzter Beitrag: awajonn11
12.04.2024, 13:35
» Antworten: 0
» Ansichten: 97
PHPMailer Pfad wird nicht...
Forum: PHP Basics
Letzter Beitrag: Arne Drews
23.08.2022, 21:07
» Antworten: 5
» Ansichten: 2.876
PHP 8.1: Endlich Enums in...
Forum: PHP Basics
Letzter Beitrag: Arne Drews
22.04.2021, 16:55
» Antworten: 1
» Ansichten: 2.696
Einfache Template Engine
Forum: PHP Template Engines
Letzter Beitrag: Arne Drews
22.04.2021, 16:49
» Antworten: 4
» Ansichten: 11.085
OAuth - Should I "scrambl...
Forum: Off Topic
Letzter Beitrag: Till
05.01.2020, 04:55
» Antworten: 0
» Ansichten: 7.881
"Invisible" Captcha
Forum: PHP Basics
Letzter Beitrag: Arne Drews
11.12.2019, 15:00
» Antworten: 5
» Ansichten: 10.411
Projektvorstellung - Test...
Forum: Off Topic
Letzter Beitrag: Till
22.11.2019, 20:03
» Antworten: 3
» Ansichten: 5.668
POST/GET routes deklarier...
Forum: PHP Basics
Letzter Beitrag: Till
14.11.2019, 19:44
» Antworten: 2
» Ansichten: 4.697
PHPMailer
Forum: PreComposed
Letzter Beitrag: Arne Drews
20.10.2019, 12:44
» Antworten: 6
» Ansichten: 8.667

 
  "archive is corrupted or damaged" (.zip Archive)
Geschrieben von: Till - 18.01.2016, 03:15 - Forum: PHP Basics - Antworten (7)

Ich habe auch die auskommentierten Varianten durchgetestet und mit Content type "application/zip"...?

PHP-Code:
/*
 ZipFile::zipDir($dir, $outZipPath);
*/
if(!file_exists($outZipPath) || filemtime($outZipPath) < time() - 60){
 
 $phar = new \PharData($outZipPath);
 
// add all files in the folder, only include files with extensions
 
$phar->startBuffering();
 
$phar->buildFromDirectory($dir);
 
$phar->stopBuffering();
 
}
 
sleep(1);

header('Content-Description: File Transfer');
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename="  $outZipPathFile.'.wgt');
header("Pragma: no-cache");
header("Expires: 0");
header('Content-Length: ' filesize($outZipPath));
 
 
/*
 readfile($outZipPath);
 */
 
 $headers headers_list();
 
 $options = array();
 
 $options['headers'] = array();

 
 
    foreach 
($headers as $hdr) {
 
       $h explode(':'$hdr2);
 
       $options['headers'][$h[0]]=$h[1];
 
   }
 
   
  self
::getInstance()->serveStaticFile($outZipPath$options); 
 
 
 return
Edit: ZipFile ist nur ein Wrapper für ZipArchive (php-extension)

EDIT2: Moment, ich habe keine Extensions angegeben und inkludiere das Directory(Eintrag) irgendwie kann das sein...?

Drucke diesen Beitrag

  W3C implementations
Geschrieben von: Till - 11.01.2016, 23:32 - Forum: Off Topic - Keine Antworten

Stackoverflow hat die Frage geschlossen: http://stackoverflow.com/questions/12231045/are-w3c-widgets-dead
Ist zwar nicht mein Thread, ich stelle mir aber die selbe Frage.

Ich überlege die Specification zu implmentieren in meiner App, bzw. auf der anderen Seite möchte ich meine Komponenten so publizieren, daß auch andere Widget Engines/Apps außer meine diese verwenden könnten.

Natürlich möchte ich mir anschauen, wie andere das schon vor mir gelöst haben.
Aber ich finde da nix wirklich?

----------------------

Widget vs. WebApp
Ich verstehe es so, daß ich WebApps für "eine ganze Seite/website" verwende und widgets für Komponente auf dieser Seite.
WebApps benötigen eine webapp.manifest json-Datei und Widgets eine config.xml.
Habe ich hier ein Verständnis Problem?

Drucke diesen Beitrag

  Worpress
Geschrieben von: Till - 11.01.2016, 23:22 - Forum: Off Topic - Antworten (1)

Alles funktional aufgebaut. Lauter globale Variablen?
Warum benutzen es aber so viele Menschen?

---
Ich weiß Thread ist überlüßig, Thema erledigt.
Finde ich aber trotzdem interessantes OFF-Topic zum Luft machen?

Drucke diesen Beitrag

  CORS not working
Geschrieben von: Till - 11.01.2016, 22:04 - Forum: JavaScript / JavaScript Frameworks / Ajax - Antworten (8)

Server:

PHP-Code:
header("Access-Control-Allow-Credentials: true");        
header("Access-Control-Allow-Orgin: ".((isset($_SERVER['HTTP_ORIGIN'])) ? $_SERVER['HTTP_ORIGIN'] : "*"));
header("Access-Control-Allow-Headers: X-Requested-With"); 

Client:
Code:
$.ajax( {
               
              
                        url: url,    
                        crossDomain: true,
                        headers: {'X-Requested-With': 'XMLHttpRequest'},
                        type: 'GET',  
                        dataType: 'XML',
                        data: {}  
                } )
                .done(function(response) {
                    $scope.config = $.parseXML(response);
                    
                    alert(JSON.stringify($scope.config));
                    
                })
               .fail(function(jqXHR, textStatus) {
                    console.error('Error: ' + jqXHR.status);
               })
              .always(function() {
                      
              });               
  
        }

Request:
Code:
OPTIONS /cdn/frdl/flow/components/webfan/tmock/config.xml HTTP/1.1
Host: frdl.webfan.de
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: de,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: GET
Access-Control-Request-Headers: x-requested-with
Origin: http://shell.frdl.de
Connection: keep-alive

Response:
Code:
HTTP/1.1 200 OK
Date: Mon, 11 Jan 2016 18:37:56 GMT
Server: Apache/2.2.16 (Debian)
Accept-Ranges: bytes
Cache-Control: public
Pragma: public
Expires: Wed, 30 Mar 2016 22:59:42 GMT
Etag: "3eb5eee2-0ab1-5174-8183-ce2237626fb1"
X-Frdl-Stability: dev; v3.0.1-beta
X-Cache: cdn://de.webfan.frdl/0.0.1/frdl.webfan.de//cdn/frdl/flow/components/webfan/tmock/config.xml/; pragma=public
Age: 1452537476
Connection: Keep-Alive, Keep-Alive
access-control-allow-credentials: true
Access-Control-Allow-Orgin: http://shell.frdl.de
Access-Control-Allow-Headers: X-Requested-With
Content-Length: 6005
Last-Modified: Thu, 31 Dec 2015 22:59:42 GMT
Keep-Alive: timeout=15, max=100
Content-Type: application/xml

Was mich wundert ist beim request das OPTIONS, ich habe doch als type explizit GET angegeben?
Die gleichen Response header scheinen mit jquery JSONP zu funktionieren, ich möchte hier aber ein .xml Dokument crossdomain laden.

Ich habe schon getest mit crossDomain:false und mitohne X-Requested-With header...

Bestimmt hat das jemand so schonmal gemacht?

Achja, Fehlermeldung:
Zitat:Quellübergreifende (Cross-Origin) Anfrage blockiert: Die Gleiche-Quelle-Regel verbietet das Lesen der externen Ressource auf http://frdl.webfan.de/cdn/frdl/flow/components/webfan/tmock/config.xml. (Grund: CORS-Kopfzeile 'Access-Control-Allow-Origin' fehlt).
Ich sehe aber diese Zeile in meinen headers?

---
Eine andere Frage: Der jquery ajax request sendet keine Cache headers, also Modified-Since und ETag und so?
---

Ich wollte es erstmal mit jquery versuchen, um zu sehen ob es erstmal so funktioniert, lieber wäre mir eigentlich wenn ich es mit einem angular promise realisieren könnte...

---
Oder kann ich mein Vorhaben gleich vergessen (XMLHttpRequest != JSOPN)?

Drucke diesen Beitrag

  google experiment mod 442
Geschrieben von: Till - 05.01.2016, 11:36 - Forum: JavaScript / JavaScript Frameworks / Ajax - Antworten (6)

Hallo Arne,
ich kann mal wieder Hilfe gebrauchen, diesmal wirklich.
D.h. es nicht so dringend im Sinne von Fehler, es würde mich doch aber stark sehr interessieren:

Vorweg:
- Ich nutze das angularjs framework von google, allerdings natürlich auf eigenem server gehostet
- Ich blende auf meinen Seiten optional und asynchron Werbung von google.adsense ein

Soweit so gut, hier habe ich eine Seite zur verwaltung von cookies und browser cache:
Ich habe da einen Eintrag auf der rechten Seite (local storage "Web Storage"):

Zitat:key: google experiment mod
value: 442
Mh, nicht von mir bewußt gesetzt.
Was ich von google nutze, siehe oben.

Das bringt mich auch nicht wirklich weiter:
https://www.google.de/?gws_rd=ssl#q=google_experiment_mod+442

Ich mein von google hab ich nichts anderes erwartet als das es mir da in den browser storage fuscht, aber ein bischen mehr Transparenz , äh ... Hilfe?!?

Viele Grüße
Till

Drucke diesen Beitrag

  Kleiner Bug in Benachrichtigungsmail
Geschrieben von: Till - 01.01.2016, 01:22 - Forum: Off Topic - Antworten (5)

Kleiner Bug in Benachrichtigungsmail

Das kam bei mir per Mail an:

Zitat:...------------------------------------------

Um das Thema zu besuchen, kannst du auf den folgenden Link klicken:
http://www.php-rocks.de/http://www.php-rocks.de/thema/61-blog-gestartet-newpost.html

Es könnte bereits auch weitere Antworten zu diesem Thema geben...


Der Links ist fehlerhaft/doppelt.

Drucke diesen Beitrag

  Verrannt
Geschrieben von: Till - 31.12.2015, 23:42 - Forum: JavaScript / JavaScript Frameworks / Ajax - Antworten (6)

Meine VCS-Raid-HD ist hardwaremäßig abgeschmiert und es wird mich wohl noch ein paar Tage kosten das zu reparieren.
Ich habe mich in einem Projekt etwas "verrant", bzw. hab ich irgendeine Blockade, ich habe irgendeinen Fehler eingebaut und komme nicht richtig weiter und wegen s.o auch kein passendes Backup.

Ich werde das library updaten, d.h. die derzeitige Version ist noch nicht das Ende vom Lied, allerdings sollte sie zumindest soweit bis hierhin funktionieren, tat sie auch, bis ich ... ich weiß auch nicht, ich tippe auf einen Typo, oder möglicherweise habe ich irgendwo fälschlicherweise eine Zuweisung wo eigentlich eine Variable GELESEN werden sollte!?

Zitat:Error: __noSuchMethod__ is deprecated library.js:1:46555
Warning: Method checkReady of overloaded object and magic method __call is not defined! library.js:8:8147
Method checkReady of overloaded object and magic method __call is not defined!

Code:
     this.resetReady = function(labelText, pro, func){
      $.WebfanDesktop.bootReady = false;
     
     
      if('undefined' === typeof  $.WebfanDesktop.o.processFactor){
$.WebfanDesktop.o.processFactor = 1;
}
      if('function' === typeof func && null !== func){
$.WebfanDesktop.o.readyStack.push(func);
}


     
      try{
     
      $('.img-ajax-loader').show();
        $('.abs window ui-draggable ui-resizeable window_stack').hide();
        $('.abs icon').hide();
        $('#menu_top').hide();
         
        var labelText=('undefined' === typeof labelText) ? '' : labelText,
        progressbar = $( $.WebfanDesktop.o.doms.progress ),
        progressLabel = $( $.WebfanDesktop.o.doms.progress_label );
        if('string' === typeof labelText)$(progressLabel).text( labelText );
        $(progressLabel).prepend('<img src="http://images.webfan.de/ajax-loader_2.gif" alt="lade..." style="border:none;" class="img-ajax-loader" /> ');
       
        $(progressbar).show();
        $(progressLabel).show();
         
         
        if('undefined' !== typeof pro && pro > $.WebfanDesktop.o.progress){
            $.WebfanDesktop.o.progress = pro;
        }


        if($.WebfanDesktop.o.progress >= 100){
           $.WebfanDesktop.o.progress = 99;
           if($.WebfanDesktop.o.readyStack.length > 0)$.WebfanDesktop.o.progress -= $.WebfanDesktop.o.readyStack.length;
        }
        if($.WebfanDesktop.o.progress < 0)$.WebfanDesktop.o.progress = 1;
       

          $(progressbar).progressbar({
                  value : $.WebfanDesktop.o.progress,
                    create: function(event, ui) {
                     for(var j = 0; j < $(this).WebfanDesktopTheme().css.length; j++){
  if($(this).WebfanDesktopTheme().css[j].selector === '.ui-widget-header'){
  $(this).find('.ui-widget-header').css('background-color', $(this).WebfanDesktopTheme().css[j].value );
     break;
  }
  }
                     
                    }
          });
           
         
     $(document).ready(function(){
        if(false === uhrTimer.is($.WebfanDesktop.o.ids.TIMER_READY)){
             uhrTimer.add($.WebfanDesktop.o.ids.TIMER_READY, function(){
             frdl.wd().checkReady();
         });  
   }    
      });    



} catch(err){
console.error(err);
}
         
       
       return $.WebfanDesktop.bootReady;
  };
 
 
 
 
 
 
 
     
 
 
 
 
 
 
      this.checkReady = function(){
         var i = 0, WD = frdl.wd();
         
         if('undefined' === typeof  WD.bootReady)WD.bootReady=false;
         if('undefined' === typeof  WD.o)return WD.bootReady;
         
         
 $(WD.o.doms.progress ).progressbar({value : WD.o.progress  });
 
         if(  'undefined' === typeof $($wd_opt_destination_locale).locale
           
           ){
            return false;
  }
                 
       
       

       for(i=0;i<WD.o.readyStack.length;i++){
         
  if(null === WD.o.readyStack[i] || true===WD.o.readyStack[i]()){
WD.o.readyStack.splice(i,1);
               i--;
               WD.o.readyStack = frdl.filterArray(WD.o.readyStack, function(el){
                return (null !== el && 'function' === typeof el);
               });
               WD.o.progress += (WD.o.processFactor * 1);
}
else{
WD.bootReady = false;  
}
 }
          $(WD.o.doms.progress ).progressbar({value : WD.o.progress  });
          if(WD.o.readyStack.length > 0)return false;
         
         
             var handleState = function(state){
             switch (state) {
                  case "loading":
                      $.WebfanDesktop.bootReady = false;
                      break;
                  case "interactive":
                     
                      $.WebfanDesktop.bootReady = false;
                      break;
                 case "complete":
                     try{
  uhrTimer.remove($.WebfanDesktop.o.ids.TIMER_READY);
            $.WebfanDesktop.bootReady = true;
                          $.WebfanDesktop.o.progress = 100;
                          $($.WebfanDesktop.o.doms.progress ).progressbar({value : $.WebfanDesktop.o.progress  });
                      $('.img-ajax-loader').hide();
                      $( $.WebfanDesktop.o.doms.progress_label  ).hide();
                      $( $.WebfanDesktop.o.doms.progress ).hide();
                          $('.abs window ui-draggable ui-resizeable window_stack').show();
                          $('.abs icon').show();
                          $('#menu_top').show();
                          $.WebfanDesktop.translate();
          $.WebfanDesktop.o.progress = 0;  
          }catch(err){
           console.warn( 'Error: ' + err);
          }

   
                 break;
             }  
  };
         
         
         document.onreadystatechange = function () {
           handleState(document.readyState);
         };

         
          handleState(document.readyState);
      return WD.bootReady;      
  };
 
 
 
 
 

Wenn mich nicht alles täuscht verweist library.js:8:8147 lediglich auf die Fehlerbehandlung selbst (Console Object):
Code:
originalConsole.log(message );

Method checkReady gehört zum Objekt $.WebfanDesktop, welches an dieser Stelle eigentlich korrekt instanziert sein soll und scheinbar auch existiert, aber irgendwie nicht in diesem dem fehlerhaften Scope?
Vor der "Instanzierung" existiert das gleichnamige Object als Proxy zwecks späterem Lazy-Load. (jQuery $.lazy Plugin)

Wenn ich auf das Icon "User" klicke oder den Klick trigger, scheint $.WebfanDesktop wieder vorhanden und die Anwendung (in diesem Test-Fall klick System->Theme wechseln) "funktioniert" wieder plötzlich.

Ich habe auch Verständnis wenn jemand keine Lust hat sich mein Problem anzuschauen, ich habe schliesslich keine konkrete Frage nur hier halt ein Debug-Problem, ich frage trotzdem mal nach, möglicherweise kann mir jemand auf die Sprünge helfen.

Wenn nötig poste oder verlinke ich gerne weiteren Code!?

Testen kann man den Code z.B. hier http://test.freizeittreffen.de/admin/ oder hier (klick auf "Workspace") http://shell.frdl.de/
Die beiden Seiten unterscheiden sich dahingehend, daß der "workspace"/desktop bei ersterer per default geladen wird plus eine Applikation.

...

Guten Rutsch!


EDIT: Warning: Method checkReady of overloaded object and magic method __call is not defined! library.js:8:8147
Code:
var OverloadableObject = _ObjectFlow().inherit(_ObjectFlow, _ObjectFlow);

OverloadableObject.__proto__.__noSuchMethod__ = function(name, args) {
   if('function' === typeof this.__call)return this.__call.call(name, args);
   
   console.warn('Method ' + name + ' of overloaded object and magic method __call is not defined!');
   
};
Mh, es ist aber eigentlich gar nicht vorgesehen, das $.WebfanDesktop.checkReady() von OverloadableObject vererbt wird?

Drucke diesen Beitrag

  Das E.V.A.-prinzip, kurz und knapp
Geschrieben von: Arne Drews - 20.09.2015, 22:47 - Forum: Tutorials - Keine Antworten

Das EVA-Prinzip als Grundlage der Datenverarbeitung
Eines der grundlegensten und wichtigsten Prinzipien im Bereich der Programmierung ist das EVA-Prinzip, das jedem Entwickler ( fast jeder Sprache ) bekannt sein sollte. Das EVA-Prinzip definiert den Ablauf einer Programmlogik und steht für "Eingabe -> Verarbeitung -> Ausgabe", was einfach umschrieben bedeutet, daß benutzerdefinierte Eingaben zunächst verarbeitet und dann erst an die Ausgabe gesendet werden.

Eine logische Erklärung zur Einhaltung für das E.V.A.-Prinzip dürfte die Tatsache sein, daß PHP auf dem Server arbeitet, also die Daten dort auch verarbeitet werden. Senden wir bereits Ausgaben in Form von HTML und wollen hinterher noch PHP-Code ausführen, machen wir dem Interpreter und uns selbst das Leben schwer. Warum? Nun, der Interpreter muß permanent den PHP-Code verlassen und wieder betreten und wir unterbinden im schlimmsten Fall die Verwendung von Funktionen, wie session_start() oder header().

Um das EVA-Prinzip zu verdeutlichen, verwenden wir als Beispiel ein kurzes Login, das eine Usereingabe per Formular entgegennimmt, diese verarbeitet und dann mit einer Ausgabe reagiert.

Hinweis
Das verwendete Beispiel soll keinen Anspruch auf Vollständigkeit in allen Details haben, sondern dient einfach nur der Erklärung des EVA-Prinzips anhand eines funktionierenden und nachvollziehbaren Beispiel-Codes.

Eingabe
Die Eingabe aus unserem Beispiel soll über ein Formular kommen, das wie folgt aufgebaut sein soll:
Code:
<form name="login" action="login.php" method="post">
   <label for="useremail">Deine E-Mail Adresse ein</label>
   <input type="text" id="useremail" name="useremail" vallue="" />
   <label for="userpassword">Passwort</label>
   <input type="password" id="userpassword" name="userpassword" vallue="" />
   <input type="submit" name="loginsubmit" vallue=" Login " />
</form>
I.d.R. kommen die Usereingaben bei PHP über das HTTP-Protokoll ( Hypertext Transfer Protocol ) und können dann über die Superglobalen Arrays abgerufen und verarbeitet werden:
  • $_GET
  • $_POST
  • $_FILES
  • $_SERVER
  • $_COOKIE
Für unser Beispiel greifen wir auf $_POST zurück, da die Übertragungsmethode POST für Formulare am sinnvollsten ist.

Verarbeitung
Wird das Formular ausgefüllt und abgesendet, haben wir die Möglichkeit, über $_POST die eingetragenen Daten zu verarbeiten. Dazu nehmen wir an, daß die erforderlichen Daten in einer Datenbank hinterlegt sind.
Wir wollen nun herausfinden, ob der User korrekte Daten eingegeben hat und der Login somit erfolgreich ist. Wir setzen eine gültige Datenbankverbindung voraus, worauf in dem PDO-Tutorial näher eingegangen wird.
Das Verbindungsobjekt soll uns für das Beispiel als PDO-Objekt in $dbObject zur Verfügung stehen.

Wir fragen also die Daten des Users ab, validieren diese und senden eine Anfrage mit den Daten an die Datenbank:
PHP-Code:
// Eingabe filtern
$sUserMail filter_var$_POST['useremail'], FILTER_SANITIZE_EMAIL );
$sUserPassword filter_var$_POST['userpassword'], FILTER_SANITIZE_STRING );

// Datenbank-Abfrage
$sQuery "SELECT `userName` FROM `userTable` WHERE `userMail`='" $sUserMail "' AND `userPassword`='" $sUserPassword "'";
$stmntResult $dbObject->query$sQuery );

// auswerten
$sUserName null;
 
if ( 
$stmntResult ) {

 
   $sUserName $stmntResult->fetchPDO::FETCH_OBJ )->userName;



Hinweis
Die Verarbeitung der Daten ist für dieses Beispiel absichtlich minimal gehalten. Für den produktiven Fall empfiehlt es sich, bspw. die Verarbeitung abhängig der tatsächlich übertragenen Daten auszuführen und PreparedStatements zu verwenden. Die einfachste Variante ist das Abfragen eines POST-Wertes, der erwartet wird.
PHP-Code:
if ( isset($_POST['useremail']) ) {

 
   // ...hier kommt die weitere Verarbeitung rein



Ausgabe
Wir haben jetzt die Eingabe entgegen genommen und diese verarbeitet. Mit dem Resultat sind wir nun in der Lage, die Ausgabe zu entscheiden/steuern.
PHP-Code:
if ( is_null($sUserName) ) {

 
   echo 'Deine Eingabe war fehlerhaft!';
 
   exit;

}

echo 
'Willkommen, ' $sUserName

Wir haben an dieser Stelle nun eine kleine Formularverarbeitung nach dem EVA-Prinzip durchgeführt.
  • Eingabe
    Die Daten haben wir über unser kleines Formular erhalten, das der User ausgefüllt hat.
  • Verarbeitung
    Wir haben die Daten aus der Eingabe zur Abfrage an die Datenbank genutzt und das Ergebnis verarbeitet.
  • Ausgabe
    Wir können anhand des Ergebnisses der Verarbeitung unsere Ausgabe dem Benutzer entsprechend steuern.

Generelle Verstöße gegen das E.V.A.-Prinzip
Wir haben jetzt das E.V.A.-Prinzip anhand eines kleinen konstruierten Falles mit einem Login gesehen. Grundsätzlich sieht man häufig deutliche Verstöße gegen das E.V.A.-Prinzip, in dem Berechnungen mitten im HTML-Code durchgeführt werden, wie in folgendem kleinen Beispiel zu sehen:
PHP-Code:
<body>

<?
php

$sUserMail 
filter_var$_POST['useremail'], FILTER_SANITIZE_EMAIL );
$sUserPassword filter_var$_POST['userpassword'], FILTER_SANITIZE_STRING );

// Datenbank-Abfrage
$sQuery "SELECT `userName` FROM `userTable` WHERE `userMail`='" $sUserMail "' AND `userPassword`='" $sUserPassword "'";
$stmntResult $dbObject->query$sQuery );

// auswerten
$sUserName null;
 
if ( 
$stmntResult ) {

 
   $sUserName $stmntResult->fetchPDO::FETCH_OBJ )->userName;

}

if ( 
is_null($sUserName) ) {
?>

    <div class="login-fehler">Fehler beim Login</div>

<?php

} else {

?>

    <div class="welcome">Hallo <?php echo $sSuserName?></div>

<?php

}

?>

</body> 
Hier wird permanent zwischen PHP und HTML gewechselt, was aus Sicht des Interpreters nicht sonderlich sinnvoll ist. Es spricht prinzipiell nichts dagegen, die Ausgabe von PHP-Variablen in die Ausgabe zu übernhehmen, wie es bspw. bei dem <div>-Tag mit der CSS-Klasse welcome zu sehen ist. Der bessere Weg wäre hier allerdings auch, die Ausgabe in der Verarbeitung vorzubereiten und die Ausgabe im Anschluß als gesamtes auszuliefern.

Drucke diesen Beitrag

  Einführung DOMDocument / DOMXPath
Geschrieben von: Arne Drews - 20.09.2015, 15:01 - Forum: Tutorials - Keine Antworten

DOMDocument und DOMXPath... Äh... Was ist das denn?

Mit DOMDocument steht uns ein Werkzeug zur Verfügung, HTML oder XML Dokumente zu untersuchen und auf bestimmte Elemente darin zuzugreifen.
Auch die Manipulation des Dokumentes ist bspw. mit dem Erstellen und Einhängen neuer Elemente im Dokumentenbaum selbst möglich, sowie bspw. auch das Erstellen eines neuen XML-Dokumentes.

DOMXPath ist ein zusätzliches, mächtiges Selektionswerkzeug, mit dem ein Dokument vom Typ DOMDocument auf Pfad-Ebene bereitgestellt wird.
Auf dieser Pfadebene lassen sich per DOMXPath sog. Queries anwenden, die das Selektieren von Elementen zum Kinderspiel machen.
Den Vorteil gegenüber den DOMDocument Boardmitteln möchte ich in diesem Tutorial anhand von kleinen Beispielen kurz aufzeigen.

DOMDocument
Ich möchte zu Beginn kurz anhand einiger kleinen Beispiele die Möglichkeiten von DOMDocument aufzeigen, um dann zu DOMXPath zu wechseln.

Als Grundlage für das gesamte Tutorial verwende ich folgende Beispiel HTML-Seite ( startseite.html ):
Die Seite ist nur mit für das Tutorial nützlichen Elementen gefüllt und stellt nicht unbedingt ein sinnvolles oder konformes Dokument dar!

Code:
<!DOCTYPE html>
<html lang="de">
<head>
  <title>Das Forum mit Tutorials rund um Webentwicklung mit Schwerpunkt PHP</title>
</head>
<body>

<div id="header">
  <img id="logo" src="image.png" alt="" border="0">
</div>

<div class="content">
  <h1>Seitentitel</h1>
  <p class="absatz">irgendein Text</p>
</div>

<div id="footer">
  <p class="erster">erster Absatz</p>
  <p class="zweiter">zweiter Absatz</p>
  <p class="dritter">dritter Absatz</p>
</div>

</body>
</html>

Wir fangen an!
Zu Beginn erstellen wir eine Instanz der Klasse DOMDocument und laden unsere Beispiel HTML-Datei dort hinein:
PHP-Code:
$oDom = new DOMDocument;
$oDom->loadHTMLFile'startseite.html' ); 
Das ist schon alles? Ja, wir haben jetzt mit $oDom Zugriff auf den gesamten Dokumentenbaum unserer Beispiel-Datei.
Aber was können wir jetzt damit anfangen? Nun, wir können bspw. Elemente selektieren und weiter verarbeiten.

Elemente/Nodes selektieren und verwenden
Dazu stehen uns mehrere Möglichkeiten zur Verfügung, wovon ich die folgenden gerne kurz aufzeigen möchte:
  • DOMDocument::getElementById()
  • DOMDocument::getElementsByTagName()
Fangen wir mit der Methode getElementById() an. Mit dieser können wir das Element selektieren, welches den angegebenen Wert in dem Attribut id besitzt.
Die Methode ist also quasi ein Server basiertes Äquivalent zu der gleichnamigen JavaScript-Methode:
PHP-Code:
// selektieren des Elements mit id="logo"
$oDomElement $oDom->getElementById'logo' ); 
Wir selektieren also das Element, mit dem Attribut id="logo". Die Methode DOMDocument::getElementById() liefert als Rückgabe im Erfolgsfall eine Instanz der Klasse DOMElement, über die wir auf bestimmte Eigenschaften und Methoden verfügen, um das selektierte Element weiter zu verarbeiten.

Bspw. könnten wir uns nun mit der Methode DOMElement::getAttribute() den Wert der Attribute unseres Elements zurückgeben lassen, buw. über DOMElement:: setAttribute() auch Attribute neu setzen.
PHP-Code:
// Attribut auslesen
$oDomElement->getAttribute'src' );

// Attribut setzen/überschreiben
$oDomElement->setAttribute'src''images/anotherone.jpg' ); 
Hinweis
Die Klasse DOMElement erbt Eigenschaften und Methoden der Klasse DOMNode, so daß uns bspw. die Möglichkeit zur Verfügung steht, den Inhalt unseres Elementes anzeigen zu lassen: 
PHP-Code:
echo $oDomElement->nodeValue
Ausführliche Informationen dazu und weitere Möglichkeiten können in der offiziellen Dokumentation nachgelesen werden:

Element-/NodeList selektieren
Wir gehen einen Schritt weiter und möchten jetzt bspw. alle <p>-Tags unseres Dokumentes selektieren. Diese werden bspw. über die Methode DOMDocument::getElementsByTagName() selektiert und als Instanz der Klasse DOMNodeList zur Verfügung gestellt:
PHP-Code:
$oDomNodeList $oDom->getElementsByTagName'p' ); 
Diese Liste von Elementen können wir bspw. mit foreach iterieren und haben so wieder Zugriff auf jedes einzelne Element:
PHP-Code:
foreach ( $oDomNodeList as $oDomNode ) {

    echo $oDomNode->nodeValue;


Für unsere Beispieldatei sollte dieser Code folgende Ausgabe erzeugen:
Ausgabe:
irgendein Text
erster Absatz
zweiter Absatz
dritter Absatz
Das sieht doch schon mal gut aus. Aber irgendetwas stört und bereitet unter bestimmten Anforderungen Probleme. Mit den uns bekannten Methoden kommen wir nämlich bspw. nicht so ohne weiteres direkt an ein bestimmtes <p>-Tag aus dem Footer ran. Wir müssten immer eine Liste von Elementen selektieren und mittels Iteration und Prüfungen unser gesuchtes Element aufspüren.

Einfacher geht's mit DOMXPath!
Mit DOMXPath wird diese Anforderung über eine sogenannte Abfrage ( Query ) umgesetzt. Dazu bildet DOMXPath den Dokumentenbaum als Pfad ab, in dem wir uns quasi wie in Verzeichnissen ( mit einigen Besonderheiten natürlich ) bewegen können.

Fangen wir nochmal ganz vorne an, um uns eine Instanz der Klasse DOMXPath für unser Dokument zu erstellen:
PHP-Code:
$oDom = new DOMDocument;
$oDom->load'startseite.html' );

// jetzt kommt DOMXPath
$oXPath = new DOMXPath$oDom ); 
Das war's schon. Der Konstruktor von DOMXPath benötigt lediglich das DOMDocument-Objekt zur Initialisierung.
Uns stehen jetzt alle Möglichkeiten von DOMXPath zur Verfügung. Nehmen wir doch gleich mal ein Beispiel, das den Vorteil an unserem Dokument deutlich zeigt:
PHP-Code:
$oDomNodeList $oXPath->query'//p[@class="zweiter"]' ); 
Was war das denn jetzt?! Nun, wir haben auf ganz simple Art den Pfad nach einem <p>-Tag mit der CSS-Klasse zweiter abgefragt. Wurde ein solches Element gefunden, wird dies in einer DOMNodeList zurückgeliefert. Wir könnten jetzt über das Ergebnis iterieren, genau wie wir es bei dem Beispiel von DOMDocument::getLementsByTagName() gemacht haben. Da wir in diesem Fall aber wissen, daß wir nur ein Element in der Liste haben, können wir auch direkt darauf zugreifen. Ermöglichen tut uns dies die Methode DOMNodeList::item(), die uns eine Instanz der Klasse DOMNode liefert:
PHP-Code:
echo $oDomNodeList->item(0)->nodeValue
Ausgabe:
zweiter Absatz
Hinweis
DOMXPath::query() unterscheidet erstmal nicht zwischen der Menge der gefunden Elemente und gibt erstmal alles als DOMNodeList zurück, auch wenn es nur ein Element beinhaltet! Über die Methode DOMNodeList::item() haben wir jedoch direkten Zugriff auf jedes einzelne Element darin.

Aber so eine Abfrage sieht kompliziert aus!
Eigentlich nicht, wenn man weiß, was da steht. Um das zu klären, wollen wir die grundsätzlichen Merkmale einer Query betrachten.
Wir haben bereits gelernt, daß DOMXPath unser Dokument als Pfad zur Verfügung stellt. Als Pfad betrachtet, schauen wir uns mal den Weg zu den <p>-Tag Elementen des Footers an:
Pfad:
/html/body/div/p
Man erkennt ein wenig die Ähnlichkeit mit Verzeichnispfaden im Dateisystem. Mit dieser Query erhalten wir sogar auch ein Ergebnis, nämlich alle <p>-Tag Elemente als DMONodeList.
Was wir aber wollen, ist nur das zweite Element, daher könnte man jetzt einfach hergehen und aus der DOMNodeList das zweite item extrahieren:
PHP-Code:
$oDomNodeList $dXPath->query'/html/body/div/p' );
echo 
$oDomNodeList->item(1)->nodeValue
Ausgabe:
erster Absatz
Aber wieso denn jetzt erster Absatz? Was haben wir falsch gemacht?
Grundsätzlich haben wir keinen Fehler gemacht. Uns war vermutlich nur nicht bewußt, daß es ja auch noch andere <p>-Tag Elemente gibt, die über genau den gleichen Pfad abgebildet werden. In unserem Fall befindet sich ein weiterer <p>-Tag in dem <div>-Tag mit der Klasse content. Und als Ergebnismenge liefert uns DOMXPath->query() alle Elemente, die es mit der Pfad-Angabe findet. Also müssen wir genauer werden, und genau da kommen die besonderen Möglichkeiten einer Query ins Spiel. Wir sagen einfach, daß wir nur die <p>-Tag Elemente haben möchten, die sich im <div>-Tag mit der CSS-Klasse footer befinden. Aber wie?
Ganz einfach, dazu verwendet man unmittelbar hinter dem Tag-Bezeichner eckige Klammern, um auf dessen Attribute zugreifen zu können. Dabei sollten wir nur wissen, daß ein Attributname mit einem @ eingeleitet wird. Die Anforderung sieht damit dann nun so aus:
PHP-Code:
$oDomNodeList $oXPath->query'html/body/div[@class="footer"]/p' );
echo 
$oDomNodeList->item(1)->nodeValue
Ausgabe:
zweiter Absatz
Prima! Jetzt sind wir zumindest im richtigen Container und erhalten auch unser gewünschtes Element an der erwarteten Stelle der Ergebnisliste. Ein wenig unzufrieden sind wir allerdings schon noch, denn wir wollen ja eigentlich speziell nur das eine Element haben.

Mit dem Wissen über Attribute, das wir gerade erlernt haben, können wir das auch relativ einfach umsetzen:
PHP-Code:
$oDomNodeList $oXPath->query'html/body/div[@class="footer"]/p[@class="zweiter"]' );
echo 
$oDomNodeList->item(0)->nodeValue
Ausgabe:
zweiter Absatz
Hier befindet sich unser gesuchtes Element nun als einziger in der Ergebnisliste, weshalb wir diesen jetzt mit item(0) ansprechen müssen.

Geht das auch kürzer?
Ja, das geht natürlich auch kürzer, denn DOMXPath::query() kennt mehr als nur einen Slash. Er kennt den Doppelslash!
Mit Angabe zweier Slashes als Pfadtrenner sagen wir der Query, daß es vollkommen egal ist, wo wir uns im Pfad gerade befinden, er soll alles, was danach kommt einfach suchen.
Klingt erstmal einfach, aber doch verwirrend. Deshalb wollen wir das direkt an unserem Beispiel zeigen.

Wir wissen, unser <p>-Tag hat die CSS-Klasse zweiter. Also sagen wir der Query einfach, sie soll im gesamten Dokument nach jedem <p>-Tag mit der Klasse zweiter suchen:
PHP-Code:
$oDomNodeList $oXPath->query'//p[@class="zweiter"]' );
echo 
$oDomNodeList->item(0)->nodeValue
Ausgabe:
zweiter Absatz
Das war schon das ganze Geheimnis der Query.
Hinweis
Wie in den Ausgaben zu erkennen ist, steht uns in der Property DOMNode::nodeValue lediglich der PlainText zur Verfügung. Möchten wir den Node als HTML haben, müssen wir diesen zunächst über die Methode DOMDocument:: saveHTML() HTML formatiert speichern. Ab PHP v5.3.6 kann man hier allerdings auch den optionalen Parameter von DOMDocument:: saveHTML([DOMNode $node]) verwenden, um den DOM direkt auszugeben. Bezogen auf unser letztes Beispiel könnte eine Ausgabe in HTML-Form so aussehen:
PHP-Code:
$oDomNode $oXPath->query'//p[@class="zweiter"]' )->item(0);
echo 
$oDomNode->ownerDocument->saveHTML$oDomNode ); 
DOMNode::ownerDocument liefert uns das referenzierte DOMDocument-Objekt, so daß wir das wie oben zu sehen bequem über chaining lösen können.
Wäre $oDomNode jetzt bspw. eine Tabelle, hätten wir als Ausgabe diese komplett in HTML Form.
Damit soll es auch genug sein in Bezug auf unser kleines Tutorial zu DOMDocument/DOMXPath.
Es gibt noch einige weitere Feinheiten zu DOMXPath zu sagen, aber das werde ich in einem weiterführenden Tutorial noch mal ausführlicher beschreiben.

Hinweis
Alle in diesem Tutorial besprochenen Klassen und deren weitergehende Nutzung sind in der offiziellen Dokumentation nachlesbar:

Drucke diesen Beitrag

  Maximalwerte aus drei verschiedenen Tabellen über View
Geschrieben von: Arne Drews - 12.09.2015, 02:17 - Forum: MySQL - Antworten (3)

Hallo,

Ich stehe leider grad ein wenig auf dem Schlauch.
Ich habe 3 Tabellen, die als einzige Gemeinsamkeit eine bestimmte Spalte enthalten.

Es geht dabei um CSV-Importe, wobei ich anhand der filemtime entscheide, ob die CSV neu ist und in die DB importiert werden soll.
LOAD DATA fällt hier leider aus, da die CSV noch überarbeitet werden muß, bevor sie importiert wird.

Es handelt sich um 3 CSV Dateien und sinniger Weise auch um 3 zugehörige Tabellen.
Nun möchte ich zwecks Entscheidung, ob importiert werden soll, zunächst aus jeder Tabelle die aktuellste filemtime haben.

Generell liefert mir das bereits mein gewünschtes Ergebnis:

Code:
SELECT
   MAX(table1.filemtime) as table1,
   MAX(table2.filemtime) as table2,
   MAX(table3.filemtime) as table3

FROM
   table1,
   table2,
   table3
Allerdings weigert er sich, dies in einer View umzusetzen! Erstelle ich eine View mit dieser Query, erhalte ich nach Abfrage dieser View für alle drei Spalten den DB-Wert NULL.

Kann es sein, daß ich in Views nicht aggregieren darf?
Wäre mir zwar neu, aber wundern würd's mich nicht.


EDIT:
Habe es gerade nochmal als View erstellt, nun kommen zwar die korrekten Ergebnisse, aber die Abfrage dauert ca. 5 Sekunden.
Geht das besser?

Für Tipps und Infos, bin ich dankbar.

Gruß Arne

Drucke diesen Beitrag