// Listings aus dem Artikel
// "Wider den Spaghetti-Code" von Susanne Pfeiffer
// iX 04/2005, S. 42

// Der Code des gesamten Projekts liegt in zwei 
// in diesem Verzeichnis vorhandenen tar-Archiven vor:
// 1. fuer PostgreSQL
// 2. generisch, durch die Verwendung von ADOdb


!!! Listing 1:

<?php

class BookClass
{
	private $authorid = 0;
	private $author = null;
	private $title = "";
	private $subtitle = "";

	public function __construct($authorid = 0, 
				$author= null,
				$title= '', 
				$subtitle = '')
	{
		$this->authorid = $authorid;
		if (isset ($author))
		{
			$this->setAuthor($author);
		}
		else 
		{
			$this->author = new AuthorClass();
		}

		$this->title= $title;
		$this->subtitle = $subtitle; 
	}
	
	public function __destruct () {}

	public function setAuthorId ($authorid)
		{ $this->authorid = $authorid; }

	public function getAuthorId () 
		{ return $this->authorid; }

	public function checkAuthorId() 
		{ return $this->authorid > 0; }

	public function setAuthor($author)
	{ 
		if ($author instanceof AuthorClass)
			{$this->author = $author;}
		else 
		{
			// ...
		}
	}

	public function getAuthor (){ return $this->author; }

	public function checkAuthor()
		{return $this->author->checkLastname()
			&& $this->author->checkFirstname();}

	// get- und set-Methoden fuer title und subtitle
	// ...

	public function isValid()
	{ 
		return $this->checkAuthorId()
			&& $this->author->isValid() 
			&& $this->checkTitle()
			&& $this->checkSubtitle();
	}
	
	public function isEmpty()
	{
		return ( ($this->authorid == 0)
			&& $this->author->isEmpty()
			&& ! $this->title 
			&& ! $this->subtitle );
	}
}
?>

!!! Listing 2:

<?php
    class SimpleDbMapperClass
    {
	// Statische Parameter fuer Datenbankzugriff
	private static $dbname = false;
	private static $dbuser = false;
	private static $dbpassword = false;

	// Statische Parameter fuer Verbindungsverwaltung
	private static $connection = false;
	private static $instancecounter = 0;

	// Konstruktor
	public function __construct($dbname, $dbuser, $dbpassword) 
	{
	    if (self::$connection)
		{
		    // Pruefe, ob neue Verbindung mit
		    // bestehender uebereinstimmt
		    if (   $dbname     != self::$dbname
			or $dbuser     != self::$dbuser
			or $dbpassword != self::$dbpassword )
			{
				// ... Error Handling ...
			}
		}
	    else 
		{
		    self::$dbname     = $dbname;
		    self::$dbuser     = $dbuser;
		    self::$dbpassword = $dbpassword;

		    // Stelle Datenbankverbindung her
		    self::$connection 
			=  pg_connect("dbname=" . $dbname 
				      . " user=" . $dbuser 
				      . " password=" . $dbpassword);

		    //  ... Pruefung, ob Verbindung hergestellt werden konnte
		    //      und entsprechendes Error Handling ...

		    // initialisiere Instanzenzaehler
		    self::$instancecounter = 0;
		}

	    // Inkrementiere Instanzenzaehler
	    self::$instancecounter++; 
	}
	
	public function __destruct()
	{
	    // Dekrementiere Instanzenzaehler
	    self::$instancecounter--;
 
	    // Trenne Datenbankverbindung, 
	    // falls letzte Instanz
	    if (self::$instancecounter == 0)
		{
		    pg_close(self::$connection);
		}
	}
	

	public function insertBook(& $Book)
	{
	    // Ermittle Id des einzufuegenden Buches
	    $result = pg_exec(self::$connection, 
				"select nextval('s_books')");
	    $returnvalue = pg_result ($result, 0, "nextval");
					 
	    
	    // Lege Buch in Datenbank an
	    $sql = "insert into t_books values "
		. "($returnvalue, " . $Book->getAuthorId()
		. ", '" . $Book->getTitle() . "', "
		. "'" . $Book->getSubtitle() . "')";
		
	    if (! ($result = pg_exec(self::$connection, $sql)) )
		{
			// ... Error Handling ...
		};

	    return $returnvalue;	    
	}

	public function updateBook($bookid, & $Book)
	{
		// analog zur insert-Methode
		// ID des Buches wird als Parameter uebergeben. 
	}



	public function readBook($bookid)
	{
	    $sql = "select * from t_books b, t_authors a "
		. "where a.authorid = b.authorid "
		. "and bookid = $bookid";

	    $returnvalue = null;
	    
	    if ($result = pg_exec(self::$connection, $sql))
		{
		    if (1 == pg_numrows($result))
			{
			    $returnvalue = new BookClass 
				(pg_result
				 ($result, 0, "authorid"), 
				 new AuthorClass
				     (pg_result($result, 0, "lastname"), 
				      pg_result($result, 0, "firstname") ), 
				 pg_result($result, 0, "title"), 
				 pg_result($result, 0, "subtitle") );
			}
		    else // Kein Buch mit passender ID gefunden
			{
			    $returnvalue = new BookClass;
			}
		    
		}
	    else // Ein Fehler ist aufgetreten
		{
			// ... Error Handling ...
		}

	    return $returnvalue;
	}


	public function readBooks()
	{
		// Analog zur readBook-Methode, 
		// allerdings wird eine Liste (array) von Buechern
		// zurueckgegeben
	}


	public function deleteBook($bookid)
	{
	    $sql = "delete from t_books"
		. " where bookid = $bookid";
		
	    if (! ($result = pg_exec(self::$connection, $sql)) )
		{
			// ... Error Handling ...
		};

	    return;	    
	}

	// nicht implementiert: Methoden zum Zugriff auf Autorendaten. 
	
    }

?>

!!! Listing 3:

<?php
class SimpleDbMapperClass
{
	// statische Parameter fuer den DB-Verbindungsaufbau
	private static $connection      = array();
	private static $instancecounter = array();

	// Referenz auf das eigene DB-Handle
	private $myconnection            = false;
	private $mydbuser                = '';
	private $mydbname                = '';
	private $mydbpassword            = '';

	// Konstruktor, Destructor
	public function __construct($dbuser, $dbname, $dbpassword)
	{
    		$this->mydbname     = $dbname; 
	    	$this->mydbuser     = $dbuser;
	    	$this->mydbpassword = $dbpassword;

		if (! isset( $connection [$dbuser][$dbname][$dbpassword] ) )
		{
			// ... baue DB-Verbindung auf und speichere
			//     das DB-Handle in $connection ...

			// setze Instanzenzaehler auf 0
			self::$instancecounter
				[$dbuser][$dbname][$dbpassword] = 0;
		}	

		// Inkrementiere Instanzenzaehler 
	    	self::$instancecounter[$this->mydbname]
			[$this->mydbuser][$this->mydbpassword]++;

	    	$this->myconnection = & self::$connection 
			[$this->mydbname][$this->mydbuser][$this->mydbpassword];

	}

	public function __destruct()
	{
	   	// Dekrementiere Instanzenzaehler
	    	self::$instancecounter[$this->mydbname]
			[$this->mydbuser][$this->mydbpassword]--;
 
	    	if (self::$instancecounter[$this->mydbname]
			[$this->mydbuser][$this->mydbpassword] <= 0)
		{    
		    	// ... trenne DB-Verbindung ...

			// loesche statische Variablen 
		    	unset (self::$connection[$this->mydbname]
				   [$this->mydbuser][$this->mydbpassword]);
		    	unset (self::$instancecounter[$this->mydbname]
				   [$this->mydbuser][$this->mydbpassword]);
		}		
	}
	
	// ...
}
?>

!!! Listing 4:

<?php
    class SessionClass
    {
	// Session Status
	//  0: nicht eingeloggt
	//  1: eingeloggt
	private $sessionstate = 0;

	// Benutzername. Ist gesetzt, wenn der
	// Benutzer eingeloggt ist. 
	private $username = "";

	public function __construct()
	{
	    session_start();
	  
	    // Ueberpruefe, ob die Session existiert
	    // und der Benutzername gesetzt ist. 
	    // Wenn der Benutzername gesetzt ist, 
	    // ist der Benutzer eingeloggt
	    if (isset($_SESSION["username"]) )
		{
		    $this->username = $_SESSION["username"];
		    $this->sessionstate = 1; 
		}
	}

	// Destructor
	public function __destruct() {}
	
	// get- und set-Methoden
	public function getState()
	{ return ($this->sessionstate); }
	
	public function setState($state)
	{ $this->sessionstate = $state; }

	public function getUsername()
	{ return $this->username; }
	
	public function setUsername($username)
	{ $this->username = $username; }

	// login-Methode
	public function login($username, $password, 
			      $dbname, $dbusername, $dbpassword)
	{
	    // ... lies Benutzer $username aus Datenbank ...

	    // Pruefe Benutzereingabe gegen die gelesenen Werte
	    if ($myuser->isValid()
		&& $myuser->getUsername() == $username
		&& $myuser->getPassword() == $password)
		{  
		    // Wenn alle o.k. ist, setze Session-Daten
		    $_SESSION["username"] = $username;
		    $this->username = $username;
		    $this->sessionstate = 1; 
		}
	    else // sonst setze Session State auf 0 
		{ 
			$this->sessionstate = 0; 
		}

	    return $this->sessionstate; 
	}
      

	// logout: zerstoere die Session
	public function logout()
	{
	    session_destroy(); 
	    $this->sessionstate = 0;
	    $this->username = 0;
	}     
    }

?>

!!! Listing 5:

<?php

    class LoggClass
    {
	// statische private Instanz
	private static $instance = false;
	private static $instancecounter = 0;
	
	// filehandle auf Logdatei
	private $logfile = false;
	
	private function __construct() 
	{
	  // Logfile oeffnen	
	  // LOGFILENAME ist eine globale Konstante, die den Namen 
	  // des zu oeffnenden Logfiles enthaelt
	  $this->logfile = fopen (LOGFILENAME, "a");

	  // falls das Logfile nicht geoeffnet werden konnte
	  // --> entsprechendes Error Handling
	  if (! $this->logfile ) 
	    { 
			// ... Error Handling ...
	    }
	    
	  self::$instancecounter++;
	}
	
	public function __destruct()
	{
	    // Erst muss geprueft werden, ob das Filehandle ueberhaupt
	    // vorhanden ist, da der Destruktor auch aufgerufen wird, 
	    // wenn der Konstruktor bereits mit einer Ausnahme
	    // abgebrochen wurde. 
	    // Der Destruktor darf keine Ausnahme werfen!!
	    if ($this->logfile)
		{
		    try {$this->_logg ("Logfile wird geschlossen ...");}
		    catch (Exception $e) {}
		    fclose($this->logfile);
		}
	    self::$instancecounter--;
	}
	
	// getInstance()-Methode - konstruiert bei Bedarf eine
	// Instanz und gibt diese zurueck. 
	public static function getInstance()
	{
	    if ( self::instancecounter <= 0)
		{
		    LoggClass::$instance = new LoggClass(); 
		}
	    return LoggClass::$instance; 
	}

      // log-Methoden der Instanz - abweichend vom ueblichen 
      // Konzept privat, da statt dessen die statischen Methoden
      // genutzt werden sollen
	private function _logg($message)
	{
	    if (! fputs($this->logfile,  
			date("H:i:s") . "\t" . $message . "\n"))
		{ 
		    throw new Exception ( /* ... */ );
		}
	}
	
	// Oeffentliche statische Log-Methode
	public static function logg($message)
	{
	    LoggClass::getInstance()->_logg($message); 
	}
	
    }
?>

!!! Listing 6:

<?php
// ...
// logg ein wenig 
LoggClass::logg("hello World!");
// ...
// Am Ende des Skriptes
LoggClass::logg ("bin jetzt am Ende - "
			. "gleich wird das Logfile automatisch geschlossen");
?>

!!! Listing 7:

<?php

// Globale Benutzermeldung
$message = "";

// Instanziiere Session Objekt. 
// Wenn dabei ein Fehler auftritt 
// --> Abbruch und Aufruf Exception Handler
$Session = new SessionClass();

// Pruefe, ob Benutzer eingeloggt ist
if ( $Session->getState() >= 1 ) 
{
    // Lies Buecher aus Datenbank
    try
	{
	    $DbMapper = new SimpleDbMapperClass
		(DBNAME, DBUSER, DBPASSWORD);
	    $Books = $DbMapper->readBooks();
	}
    catch (Exception $e)
	{
	    $Books = array();
	    LoggClass::logg("Fehler beim Lesen der Buecher");
	    // setze Fehlermeldung 
	    $message = "Es ist ein Fehler aufgetreten :-(";
	}
}

?>

!!! Listing 8

<?php
// Globale Benutzermeldung
$message = "";

// Instanziiere Session Objekt
// Falls ein Fehler auftritt 
// --> Abbruch und Aufruf Exception Handler
$Session = new SessionClass();

// Pruefe, ob Benutzer eingeloggt ist. 
if ( $Session->getState() >= 1 ) 
{
    try
	{
	    $DbMapper = new SimpleDbMapperClass
		(DBNAME, DBUSER, DBPASSWORD);
	}
    catch (Exception $e)
	{
	    // Exception Handling. 
	    // Abbruch und Aufruf Exception Handler 
	    // oder Fortsetzung ohne Datenbankzugriff
	}

    // BuchId initialisieren
    $bookid = 0;

    // setze Buch aufgrund von POST-Parametern 
    if (isset ($_POST['submitBookChange'])) 
	{  
	    $bookid = $_POST ['hiddenBookId'];
	    $Book =  new BookClass
		(( isset($_POST['selectAuthorId']) 
		   ? $_POST['selectAuthorId'] : 0),
		 new AuthorClass(),      // setze erstmal leeren Autor
		 ( isset($_POST['inputTitle']) 
		   ? $_POST['inputTitle'] : ''), 
		 ( isset($_POST['inputSubtitle']) 
		   ? $_POST['inputSubtitle'] : '')
		 );

	    // lies Autor aufgrund geposteter AutorId aus der Datenbank
	    try
		{
		    if ( isset($_POST['selectAuthorId']) )
			{
			    $Book->setAuthor
				($DbMapper->readAuthor
				 ($_POST['selectAuthorId']));
			}
		}
	    catch (Exception $e)
		{
		    // Exception Handling
		    // Abbruch und Aufruf Exception Handler 
		    // oder Fortsetzung ohne Autor
		}
	}
	else // wenn keine post-Parameter gesetzt, 
	//  lies Buch aus der Datenbank
	{
	    // pruefe, ob BuchId als Get-Parameter uebergeben
	    if (isset($_GET['bookid']))
		{
		    // Setze BuchId
		    $bookid = $_GET['bookid'];
		    try
			{
			    $Book = $DbMapper->readBook($bookid); 
			}
		    catch (Exception $e)
			{
			    // Exception Handling
			    // Abbruch und Aufruf Exception Handler 
			    // oder Fortsetzung ohne Buch
			}
		}
		else // weder ein get - noch ein post-Parameter
		//      --> konstruiere neues Buch 
		    { 
			$Book = new BookClass(); 
		    }
	} // 

	// Speichere Buecherdaten
	if (isset ($_POST['submitBookChange']))
	{
	    if ($bookid > 0) // Buch muss gespeichert werden
		{
		    try
			{
			    $DbMapper->updateBook($bookid, $Book);
			    $message .= "Buch wurde geaendert";
			}
		    catch (Exception $e)
			{
			    LoggClass::logg
				("Fehler beim Speichern des Buches");
			    $message .= "Buch konnte nicht gespeichert werden";
			}
		}
	    else // neues Buch muss angelegt werden
		{
		    try
			{
			    $bookid  = $DbMapper->insertBook($Book);
			    $message .= "Buch wurde angelegt";
			}
		    catch (Exception $e)
			{
			    LoggClass::logg
				("Fehler beim Speichern des Buches");
			    $message .= "Buch konnte nicht angelegt werden";
			}
		}
	}
} // if ( $Session->getState() >= 1 ) 
?>

!!! Listing 9;

<?php


class HtmlGeneratorClass
{
	private $username = 0;

	// Konstruktor ist privat, um Instanziierung zu vermeiden
	private function __construct() {}

	// Methode zur Generierung eines select-Tags. 
	// nimmt als Parameter
	// - den Tag-Namen
	// - eine Liste von Werten
	// - die Id des ausgewaehlten Items

	public static function getSelectTag($name, $options, $selected = 0)
	{
		$returnvalue = "<select name=\"$name\">\n";
		if (is_array($options))
		{
			foreach ($options as $optionkey => $optionvalue)
			{
				$returnvalue 
				.= "<option value=\"" . $optionkey . "\"";
				if ($selected == $optionkey)
					{$returnvalue .= " selected";}
				$returnvalue .= ">" . optionvalue . "</option>\n";
			}
		}
		$returnvalue .= "</select>\n";
		return $returnvalue;
	}

	// Methode zur Generierung der Navigationsleiste. 
	// Die Navigationsleiste ist abhängig vom Session-Status, 
	// d.h. dem angemeldeten Benutzer soll eine andere Navi-Leiste
	// angezeigt werden als dem nicht angemeldeten. 
	// Der Session-Status wird als Variable uebergeben. 
	public static function getNaviBar($sessionstate)
	{
		$returnvalue .= "<table>\n";
		$returnvalue .= "<tr><td>"
			. "<a href=\"index.html\">" . "Home</a>"
			. "</td></tr>\n";
		if ($sessionstate)
		{
			// alle Links fuer den angemeldeten Benutzer
			$returnvalue .= "<tr><td>"
				. "<a href=\"booklist.php\">" . "B&uuml;cher</a>"
				. "</td></tr>\n";
		}
		else
		{
			// hier alle Links fuer den nicht angemeldeten Benutzer
		}
		$returnvalue .= "</table>\n";
		return $returnvalue;
	}
}

?>

!!! Listing 10:

<html>
<head>
</head>
<body>
<table>
<tr>
<td></td><td><h1>&Uuml;berschrift 1</h1></td>
</tr>
<tr>
<td><?php echo HtmlGeneratorClass::getVerticalNaviBar($Session->getState()) ?></td>
<td>
<?php if ($Session->getState() >= 1): ?>
<h2>B&uuml;cherliste</h2>
<table>
<?php foreach ($Books as $bookid => $Book): ?>
<tr>
<td>
<?php echo $Book->getAuthorLastName() ?>, 
<?php echo $Book->getAuthorFirstName() ?>:&nbsp; 
<?php echo $Book->getTitle() ?>
</td>
<td><a href="bookview.php?bookid=<?php   echo $bookid ?>">anzeigen</a></td>
<td><a href="bookchange.php?bookid=<?php echo $bookid ?>">aendern</a></td>
<td><a href="bookdelete.php?bookid=<?php echo $bookid ?>">loeschen</a></td>
</tr>
<?php endforeach; ?>
</table>
<?php echo $message ?>
<?php else: ?>
Bitte loggen Sie sich erst ein.
<?php endif; ?>
</td>	
</tr>
</table>
</body>	   
</html>	   

!!! Listing 11:

<html>
<head>
</head>
<body>
<table>
<tr><td></td><td><h1>&Uuml;berschrift 1</h1></td></tr>
<tr>
<td><?php echo HtmlGeneratorClass::getVerticalNaviBar($Session->getState()) ?></td>
<td>
<?php if ($Session->getState() >= 1): ?>
<h2>Buch anlegen / &auml;ndern </h2>
<?php echo $message; ?>
<form action="bookchange.php" method="POST">
<input type="hidden" name="hiddenBookId" value="<?php echo $bookid ?>">
Autor: <?php echo HtmlGeneratorClass::getSelectTag("selectAuthorId", $DbMapper->readAuthorNames(), $Book->getAuthorId()); ?>
<br>
Titel: <input type="text" name="inputTitle" 
value="<?php echo $Book->getTitle(); ?>">
<br>
Untertitel: <input type="text" name="inputSubtitle" value="<?php echo $Book->getSubtitle(); ?>">
<br>
<input type="submit" name="submitBookChange" value="Speichern">
</form>
<?php else: ?>
Bitte loggen Sie sich erst ein.
<?php endif; ?>
</td>
</tr>
</table>
</body>   
</html>   

