OOP in PHP

Dieses Thema OOP in PHP im Forum "Webentwicklung, Hosting & Programmierung" wurde erstellt von Michael_B, 17. Nov. 2004.

Thema: OOP in PHP Hiho, ich programmiere gerade in PHP ein Schiebepuzzle (ihr kennt doch noch diese kleinen Dinger mit 4x4 oder 5x5...

  1. Hiho,

    ich programmiere gerade in PHP ein Schiebepuzzle (ihr kennt doch noch diese kleinen Dinger mit 4x4 oder 5x5 Feldern, wobei eines freigelassen wurde, und man nun die Felder hin und her schieben musste, bis sich das Bild korrekt zusammenfügt).

    Um ein wenig fitter in Objektorientierung unter PHP zu werden, wollte ich das direkt mit 2 klassen (Spiel und Feld) realisieren.

    Habe nun die Funktionalität soweit implementiert, dass ein paar Testausgaben möglich wären. Dabei meldet er aber immer Fatal error: Call to a member function on a non-object in ...\spiel.php on line 94. Zeile 94 in spiel.php ist die Zeile if($feld->getRow() > 1) array_push($nachbarn, $this->getFeldByPosition($feld->getRow()-1, $feld->getCol())); in der Methode getNeighbors($feld).

    Ich vermute mal, dass der Interpreter davon ausgeht, dass das übergebene Element ($feld) kein Objekt der Klasse Feld ist. Wie kann ich dem das nun klarmachen?

    Zur Erläuterung: Wenn ein Spiel-Objekt erzeugt wird, wird im Konstruktor ein File mit bestimmten Parametern ausgelesen. Unter anderem auch die Startaufstellung der Felder. Im Spiel-Konstruktor werden also auch direkt Feld-Objekte angelegt und in einem dem Spiel-Objekt zugehörigen Array abgespeichert. Beim Verschieben der Felder (im Spielverlauf) werden lediglich die Inhalte der den entsprechenden Feld-Objekten zugehörigen $content-Variablen vertauscht.

    Sorry, ist vielleicht ein bisschen viel. Aber hier steh ich voll aufm Schlauch. Das ist doch bei JAVA wesentlich einfacher... ???

    THX in advance
    M.

    Edit: Gibts in PHP vielleicht auch sowas wie ein Typecast. So könnte ich an dieser Stelle vielleicht nochmal extra erwähnen, dass es sich um ein Feld-Objekt handelt.

    Mein Testskript (index.php) ist folgendes:
    Code:
    include(spiel.php);
    include(feld.php);
    
    session_save_path(../../tmp);
    session_start();
    
    if(!isset($move)) $spiel=new Spiel();
    else {
       $spiel=$_SESSION[spiel];
       $feld1 = $spiel->getFieldById($move);
       $feld2 = $spiel->getFreeField();
       $spiel->exchangeContents($feld1, $feld2);
    }
    $spiel->out();
    $_SESSION[spiel]=$spiel;
    Die Klasse Feld (feld.php) sieht so aus:
    Code:
    class Feld {
       
       var $id;
       var $row;
       var $col;
       var $position;
       var $content;
       
       function Feld($id, $row, $col, $content) {
          
          $this->id=$id;
          $this->row=$row;
          $this->col=$col;
          $this->content=$content;
          $this->position=$row.$col;
          
       }
       
       function getId() {
          return $this->id;
       }
       
       function getRow() {
          return $this->row;
       }
       
       function getCol() {
          return $this->col;
       }
       
       function getPosition() {
          return $this->position;
       }
       
       function getContent() {
          return $this->content;
       }
       
       function setContent($content) {
          $this->content=$content;
       }
       
       function isFree() {
          return ($this->content==free);
       }
       
       function out($isMovable) {
          $s=;
          if($this->isFree()) {
             $s.= ;
          }
          else {
             if($isMovable) $s.=<a href=\.$PHP_SELF.?move=.($this->getId()).\><img src=\.($this->getContent()).\ border=\0\></a>;
             else $s.=<img src=\.($this->getContent()).\ border=\0\>;
          }
          
          return $s;
       }
    }
    und Spiel (spiel.php) so:
    Code:
    class Spiel {
       
       var $felder;
       var $counter;
       var $game;
       var $rows;
       var $cols;
       var $width;
       var $height;
       var $file_pre;
       var $file_suf;
       
       function Spiel($game=jc, $counter=0) {
          
          $this->game=$game;
          $this->counter=$counter;
          
          #Daten für das $game einlesen und array parat machen
          if(file_exists($game..cfg)) {
             $f = fopen($game..cfg, r);
             while(!feof($f)) {
                $zeile = fgets($f, 256);
                if(substr($zeile, 0, 1)==#) continue;
                if(strstr($zeile, =)) {
                   #Parameter
                   $parval = explode(=, $zeile);
                   switch($parval[0]) {
                      case rows: $this->rows=$parval[1]; break;
                      case cols: $this->cols=$parval[1]; break;
                      case width: $this->width=$parval[1]; break;
                      case height: $this->height=$parval[1]; break;
                      case file_pre: $this->file_pre=$parval[1]; break;
                      case file_suf: $this->file_suf=$parval[1]; break;
                   }
                }
                else {
                   #Startaufstellung
                   $c=1;
                   for($i=1; $i<=$this->rows; $i++) {
                      for($k=1; $k<=$this->rows; $k++) {
                         if($zeile==free) $this->felder[$i][$k]=new Feld($c++, $i, $k, $zeile);
                         else $this->felder[$i][$k]=new Feld($c++, $i, $k, $this->file_pre.$zeile.$this->file_suf);
                      }
                   }
                }
             }
             fclose($f);
          }
          
       }
       
       function getFields() {
          return $this->felder;
       }
       
       function getGameParameters() {
          return GAME: .$this->game.<br>ROWS: .$this->rows.<br>COLS: .$this->cols.<br>WIDTH: .$this->width.<br>HEIGHT: .$this->height.<br>FILE_PRE: .$this->file_pre.<br>FILE_SUF: .$this->file_suf;
       }
       
       function getFieldByPosition($row, $col) {
          return $this->felder[$i][$k];
          return null;
       }
       
       function getFieldByContent($content) {
          for($i=1; $i<=$this->rows; $i++) {
             for($k=1; $k<=$this->cols; $k++) if($felder[$i][$k]->getContent()==$content) return $felder[$i][$k];
          }
          return null;
       }
       
       function getFieldById($id) {
          for($i=1; $i<=$this->rows; $i++) {
             for($k=1; $k<=$this->cols; $k++) if($felder[$i][$k]->getId()==$id) return $felder[$i][$k];
          }
          return null;
       }
       
       function getFreeField() {
          return getFieldByContent(free);
       }
       
       function exchangeContents($feld1, $feld2) {
          $content_tmp = $feld2->getContent();
          $feld2->setContent($feld1->getContent());
          $feld1->setContent($content_tmp);
       }
       
       function getNeighbors($feld) {
          $nachbarn=array();
          #eine Zeile über dem Feld nachgucken
          if($feld->getRow() > 1) array_push($nachbarn, $this->getFeldByPosition($feld->getRow()-1, $feld->getCol()));
          #eine Zeile unter dem Feld nachgucken
          if($feld->getRow() < $this->rows) array_push($nachbarn, getFeldByPosition($feld->getRow()+1, $feld->getCol()));
          #links von dem Feld nachgucken
          if($feld->getCol() > 1) array_push($nachbarn, getFeldByPosition($feld->getRow(), $feld->getCol()-1));
          #rechts von dem Feld nachgucken
          if($feld->getCol() < $this->cols) array_push($nachbarn, getFeldByPosition($feld->getRow(), $feld->getCol()+1));
          return $nachbarn;
       }
       
       function areNeighbors($feld1, $feld2) {
          $nachbarn = getNeighbors($feld1);
          for($i=0; $i<count($nachbarn); $i++) {
             if($nachbarn[$i]->getId==$feld2->getid()) return true;
          }
          return false;
       }
       
       function isMovable($feld) {
          $nachbarn = $this->getNeighbors($feld);
          for ($i=0; $i<count($nachbarn); $i++) if($nachbarn[$i]->isFree()) return true;
          return false;
       }
       
       function out() {
          $s = <table width=\.($this->width*$this->cols).\ height=\.($this->height*$this->rows).\ cellpadding=\0\ cellspacing=\0\ border=\1\>\n;
          for($i=1; $i<=$this->rows; $i++) {
             $s.=  <tr>\n;
             for($k=1; $k<=$this->cols; $i++) {
                $s.=   <td width=\.$this->width.\ height=\.$this->height.\>\n;
                $s.=    .$this->felder[$i][$k]->out($this->isMovable($felder[$i][$k]));
                $s.=   </td>\n;
             }
             $s.=  </tr>\n;
          }
          $s.= </table>\n;
          return $s;
       }
    }
     
  2. Hi

    Mach einfach in der Methode mal ein var_dump($feld).
    Ich denke mal du bekommst nen Null Objekt oder so.

    Gruß, Michael
     
  3. Jo danke...

    es war komischerweise tatsächlich ein NULL-Objekt. Komisch das. Offenbar kann man hier nicht so unbeschwert OO programieren wie in Java. Einmal ein $this vergessen und schon klappts net. Naja, ich schlängele mich mit dem var_dump also immer weiter nach oben (durch mehrere Funktionen durch).

    Jetzt habe ich aber noch etwas sehr seltsames entdeckt...
    die Ausgabe des Skripts sieht nun so aus (hab in jeder Funktion erstmal die übergebenen Parameter gevar_dumpt):
    function out()
    function isMovable()
    $feld: object(feld)(5) { [id]=> int(1) [row]=> int(1) [col]=> int(1) [position]=> string(2) 11 [content]=> string(10) jc .jpg }
    function getNeighbors()
    $feld_id: NULL
    $this->rows: string(3) 4
    $this->cols: string(3) 4
    $feld: object(feld)(5) { [id]=> int(1) [row]=> int(1) [col]=> int(1) [position]=> string(2) 11 [content]=> string(10) jc .jpg }
    Fatal error: Call to undefined function: getfieldbyposition() in ...\spiel.php on line 109

    Zeile 109 ist aus der Funktion getNeighbors($feld)
    Code:
    function getNeighbors($feld) {
    echo <br>function getNeighbors()<br>  \$feld_id: ; var_dump($feld_id);
    echo <br>  \$this->rows: ; var_dump($this->rows);
    echo <br>  \$this->cols: ; var_dump($this->cols);
    #$feld = $this->getFieldById($feld_id);
    echo <br>  \$feld: ; var_dump($feld);
    $nachbarn=array();
    #eine Zeile über dem Feld nachgucken
    if($feld->getRow() > 1) array_push($nachbarn, getFieldByPosition($feld->getRow()-1, $feld->getCol()));
    #eine Zeile unter dem Feld nachgucken
    if($feld->getRow() < $this->rows) array_push($nachbarn, getFieldByPosition($feld->getRow()+1, $feld->getCol())); // HIER IST ZEILE 109
    #links von dem Feld nachgucken
    if($feld->getCol() > 1) array_push($nachbarn, getFieldByPosition($feld->getRow(), $feld->getCol()-1));
    #rechts von dem Feld nachgucken
    if($feld->getCol() < $this->cols) array_push($nachbarn, getFieldByPosition($feld->getRow(), $feld->getCol()+1));
    return $nachbarn;
    }
    Zeile 109 ist durch Kommentar markiert... Witzig ist nur, dass er 2 Zeilen vorher, also in 107 bereits getFieldByPosition aufgerufen hat, und zwar offenbar mit erfolg. Warum er jetzt meint, diese Funktion nicht zu kennen, bleibt mir schleierhaft... :(

    Greetz
    M.
     
  4. Sodele,

    nun funktioniert alles... Ursachen der Probleme waren erstmal, dass ich nicht vor jede Variable und vor jeder Funktion der aktuellen Klasse ein $this stehen hatte und später auch, dass ich nicht wusste, dass die Feld-Objekte (die in der Klasse Spiel in einem Array gespeichert sind) nach einer Änderung wieder an die entsprechenden Position im Array gespeichert werden muss.
    Anders als bei JAVA arbeitet OO PHP nicht mit Referenzen, sondern scheint Objekte, die ich an eine Methode übergebe, lediglich zu kopieren...

    Besten Dank für die Hilfe... das var_dump hat mir dennoch sehr weiter geholfen.

    Greetz
    M
     
  5. Hi

    $a =& $b

    Dann hast du eine Referenz.

    Gruß, Michael
     
  6. Stelle auch sicher, dass die Klassenvariablen alle initialisiert sind. Ist für Dich besser und ein besserer Programmierstil.

    Irgendwo hast Du geschrieben
    In PHP kann man noch viel unbeschwerter programmieren, denn Fehler gibt es dort auch, wenn Du NULL-Objekte übergibst. Nur die Fehlermeldungen in Java sind aussagekräftiger ;)

    Ansonsten gebe ich Dir recht: das ständige mitbenennen des $this ist etwas nervend.