PHP Verständnisproblem statische Methoden

lordg2009

Lt. Commander
Registriert
Apr. 2009
Beiträge
1.552
Hi,

ich (Hobbyprogrammierer) habe mir vorgenommen, ein früheres Projekt mit ausschließlich prozeduralen Code, welches durch mehrere Erweiterungen sehr aufgebläht wurde neu und übersichtlich objektorientiert zu schreiben. Objekte habe ich bisher immer nur benutzt (vor allem in js).

Dabei verstehe ich den Sinn von statischen Methoden noch nicht wirklich:
Statische Eigenschaften verstehe ich als Variablen welche außerhalb des instanzierten Objektes initialisiert werden, z.B. ein Zähler, der die Anzahl der erstellten Objekte speichert. Aber um auf diese zuzugreifen, oder diesen zu verändern brauche ich doch keine statische Methode, auch eine ganz normal Methode aus einem Objekt kann die statische Eigenschaft doch verändern.
Beschrieben wurde die statische Methode als Methode, die ich aufrufen kann, ohne ein Objekt zu erstellen. Aber warum sollte ich das wollen. Wenn ich eine Methode ohne Objekt benutzen möchte, kann ich mir doch einfach eine function schreiben?

Kann mir jemand diese Grundlage verständlich erklären?

Vielen Dank
 
Static

Objekte sind Instanzvertreter mit einer Identität, die mit anderen Objekten zur Laufzeit Nachrichten austauschen, usw.

Nicht alle Klassen sollen diese Voraussetzungen erfüllen, was ist bspw. mit "Hilfsklassen", die nur bestimmte Aufgaben erledigen sollen (bspw. eine Rechenklasse)? Es ist nicht notwendig, dass diese Klassen instanziert werden.

Statische Methoden können daher auf Klassen aufgerufen bzw. ausgeführt werden, ohne dass diese instanziert werden müssen.
 
Du hast recht - eine statische Methode (sofern sie public ist) unterscheidet sich im Prinzip nur wenig von einer globalen Funktion. Es gibt im Wesentlichen zwei Fälle, wann statische Methoden eingesetzt werden:

- Um thematisch zusammengehörende Funktionen zu gruppieren. Die Klasse dient dabei nur als eine Art Namensraum. Das ist aber auch nicht ideal im Sinne der objektorientierten Programmierung; hier sollte man stattdessen versuchen, eine echte Klasse (Stichwort: Value-Object-Pattern) zu bauen.

- Als Pseudo-Konstruktoren, z.B. um dem "Konstruktor" einen aussagekräftigen Namen geben zu können oder auch um mehrere "Konstruktoren" in einer Klasse haben zu können. Das ist im Sinne der OOP völlig in Ordnung. Häufig wird dabei der eigentliche Konstruktor private gemacht, so dass die Klasse nur mit Hilfe der statischen Pseudokonstruktoren instanziiert werden kann.
 
Dann versteh ich es so, dass ich statische Methoden nicht unbedingt brauche. Eventuell ergibt sich im Verlauf. Was man mit statischen Methoden erreichen kann, kann man auch mit nicht statischen erreichen, sie sind aber in bestimmten Fällen semantisch sinnvoller.

Ich wurde gebeten, meine Fragen in einem Thread zusammenzufassen. Ich habe damit stets schlechte Erfahrungen gemacht. Die Fragen sind über die Suche schlechter zu finden, der Titel ist nicht mehr aussagekräftig, in den meißten Fällen wird dann doch nur eine Frage diskutiert, die anderen fallen unter den Tisch. Eine Frage pro Thread find ich am sinnvolsten.
Aber hat der Moderator erst mal einen Thread geschlossen ist sein Wort Gesetz, also wollen wir uns daran halten.

Hier meine zweite Frage:

Hi,

eine weitere Grundlagenfrage:
Wie ist das allgemeine Prozedere für Datenbankabfragen in Objekten?

Eigenschaften meines Objektes sollen mit Werten aus meiner Datenbank belegt werden. Dazu möchte ich mysqli verwenden, was an sich ja auch ein Objekt ist. Verschiedene Klassen werden verschiedene Daten aus der Datenbank benötigen.

Wie ist hier die übliche Vorgehensweise:
1) Kann ich Objekte in Objekten erstellen oder benutzen?
2) Erstelle ich das mysqli Objekt außerhalb des Objektes und übergebe es z.B. per constructor() oder per Methode an das Objekt?
3) Erstelle ich das mysqli Objekt außerhalb des Objektes, erstelle für mein Objekt einzelne setMethoden() und setze die Eigenschaften dann einzeln nach Datenbankabfrage ausßerhalb des Objektes und arbeite damit innerhalb des Objektes gar nicht mit mysqli?

1) scheint mir am transparentesten. Das Objekt kümmert sich dann komplett selbst um die Datenbankabfrage, ohne dass ich außerhalb mit mysqli arbeiten muss. Aber dann würden mehrere Objekte gleichzeitig eine Datenbankverbindung aufbauen und überhaupt wären mehrere statt nur einer Datenbankverbindung notwendig.
2) Ich müsste ich die Datenbankverbindung nur einmal herstellen, diese dann aber mehrmals an verschiedene Objekte übergeben. Die Objekte würden dann eine Datenbankverbindung "parallel nutzen". Geht das?
3) scheint mir äußert umständlich. Ich hätte viel prozeduralen Code außerhalb der Objekte und müsste einen Haufen setMethoden() implementieren.

Vielen Dank für eure Hilfe​
 
Abhängigkeiten (Dependecies) am besten an die Objekte übergeben (Injizieren, injection).
Wenn du eine Klasse "Artikel" hast und diese Daten aus der Datenbank holen soll, dann gib dieser Klasse im Konsturktur das mysqli Objekt mit.

Code:
$databaseConnection = new mysqli('localhost', 'blub', 'blub', 'blub');


class Article {
     protected $data;
     public function __construct($articleId, $databaseConnection) {
          $this->data = $databaseConnection->query('SELECT * FROM article WHERE id = $articleId')->fetch_array();
    }

}

$article = new Article(25, $databaseConnection); // Hole den Artikel mit der ID 25
 
Meffen schrieb:
Abhängigkeiten (Dependecies) am besten an die Objekte übergeben (Injizieren, injection).
Wenn du eine Klasse "Artikel" hast und diese Daten aus der Datenbank holen soll, dann gib dieser Klasse im Konsturktur das mysqli Objekt mit.

Code:
$databaseConnection = new mysqli('localhost', 'blub', 'blub', 'blub');


class Article {
     protected $data;
     public function __construct($articleId, $databaseConnection) {
          $this->data = $databaseConnection->query('SELECT * FROM article WHERE id = $articleId')->fetch_array();
    }

}

$article = new Article(25, $databaseConnection); // Hole den Artikel mit der ID 25


https://www.w3schools.com/php/php_mysql_prepared_statements.asp

prepared statements benutzen. Indem du die ArtikelID direkt in den SQL-Syntax schreibst prädestinierst du SQL-Injections
 
Zum Thema Datenbankverbindung in die Objekte hineingeben: Das kann man machen (Stichwort: Active-Record-Pattern), halte ich aber nicht für optimal. Es kommt hier darauf an, was dein Programm macht. Wenn es wirklich nur darum geht, Daten hin- und herzuschaufeln, dann ist dieser Ansatz mMn vertretbar. Wenn es aber auch nennenswert Logik gibt, die mit den Objekten arbeitet, dann rate ich davon ab und empfehle, stattdessen das Data-Mapper-Pattern anzuwenden. Das entspricht im Wesentlichen deinem Punkt 3. Die Objekte haben dabei in der Tat nichts mehr mit MySQLi zu tun, sondern bekommen ihre Daten von außen angereicht. Allerdings vermeide ich Setter-Methoden und benutze stattdessen Reflection. Den ganzen Code, der dafür benötigt wird, kapsele ich in einer eigenen Klasse (Repository-Pattern), so dass ich auch nicht "viel prozeduralen Code außerhalb der Objekte" bekomme.

Ja, das ist erst mal zusätzlicher Aufwand, und nicht immer lohnt es sich. Wenn das Programm aber komplex werden soll oder wenn du "professionelle" Entwicklungsmethoden lernen möchtest, dann empfehle ich dir, es so durchzuziehen. Langfristig bleibt dadurch der Code flexibler und besser wartbar.

Du könntest dir auch ein Framework anschauen - im PHP-Bereich kann ich Symfony empfehlen. Das bringt vieles, was man für Webanwendungen braucht, schon mit.
 
Oder das Slim Framework, wenn man unter "Symfony" noch drunter hinweg tauchen möchte. Kann man dann auch um Twig uvm. erweitern. Stellt aber im Grundsatz zunächst "nur" einen ordentlichen Router nach PSR-7 und ein Container-Model bereit.
Ist halt wie Frage, wie abstrakt und mit wie viel "Overhead" man seine Aufgabe ansetzen möchte. Für REST-APIs verwendet ich zum Beispiel Slim mit Slim-JWT und spare dadurch einiges an Ressourcen und habe dennoch etwas ordentliches zusammen genagelt.

Generell mal eine Stichwortliste zum vervollständigen der bereits bestehenden Stichworte für die Nutzung in Google:
Factory Method, Dependency Injection, Container, CRUD-Design, Middleware und mein Lieblingswort: Datenbankabstraktionsschicht
 
Ich habe jetzt versucht, eine Datenbankverbindung an mein erstes selbsterstelltes Objekt zu übergeben. Leider funktioniert es nicht und ich vermute, dass ich irgendwo einen simplen Syntaxfehler übersehe. Könnt ihr noch mal drüberschauen?

Meine Klasse (./libraries/m_exam.php):
PHP:
<?php
class m_exam
{
	private $mysqli;
	
	private $anonym;
	private $completeness;
	private $questionCount;
	private $dateUpload;
	private $disabled;
	private $disabledUserId;
	private $examId;
	private $examGroup;
	private $explenation;
	private $fieldId;
	private $finished;
	private $lastChange;
	private $origFile;
	private $origYear;
	private $publicFill;
	private $quality;
	private $rating;
	private $ratingCount;
	private $subjectId;
	private $universityId;
	private $userId;
	private $username;
	
	function __construct($mysqlObject) {
		if(!$mysqlObject) {
			throw new Exception('No mysqli object was passed.');
		} else {
			$this->mysqli = $mysqlObject;
		}
	}
	
	public function loadExamById($examId) {
		$query = "SELECT anonym, completeness, questionCount, dateUpload, disabled, disabledUserId, examGroup, explenation, fieldId, finished, lastChange, origFile, origYear, publicFill, quality, rating, ratingCount, subjectId, universityId, userId, username FROM exams WHERE examId=?";
		$stmt = $this->mysqli->prepare($query);
		$stmt->bind_param("i", $examId);
		$stmt->execute();
		$stmt->bind_result($anonym, $completeness, $questionCount, $dateUpload, $disabled, $disabledUserId, $examGroup, $explenation, $fieldId, $finished, $lastChange, $origFile, $origYear, $publicFill, $quality, $rating, $ratingCount, $subjectId, $universityId, $userId);
		$stmt->fetch();
		$stmt->close();
	}
	
	public function getTest($string) {
		switch ($string) {
			case 'origYear':
				print $his->origYear;
				break;
			case 'query':
				print $this->query;
				break;
		}
	}
}
?>

meine index.php (Auszüge, cdns und htmlcode spare ich mit mal
PHP:
<?php
	error_reporting(E_ALL);
	
	spl_autoload_register(function ($class_name) {
		require './libraries/' . $class_name . '.php';
	});
	
	$mysqli = new mysqli('localhost', '107946-examsdb', 'sc3Nhq5FXESw', '107946_examsdb');
?>

jetzt kommt HTML Code

<?php
	$myTest = new m_exam();
	print $myTest->getTest('origYear');
?>

PS:
Merkwürdigerweise wirft mir die Seite auch keine Fehler entgegen, obwohl ich error_reporting(E_ALL); gesetzt habe?
 
ini_set('display_errors', 1);

Auch mal den Wert in der php.ini prüfen. Es kann sein, dass der Wert nicht on-the-fly geändert werden kann.
Und auch mal in den Quellcode von der anscheinen leeren html-Seite gucken.
 
Darlis schrieb:
ini_set('display_errors', 1);

Auch mal den Wert in der php.ini prüfen. Es kann sein, dass der Wert nicht on-the-fly geändert werden kann.
Und auch mal in den Quellcode von der anscheinen leeren html-Seite gucken.

Hat funktioniert, habe es gleich in der php.ini geändert. Danke.

Nächstes Problem:

1) ich habe jetzt das Datenbank objekt übergeben. Eine mysql statement sieht jetzt wie folgt aus:
PHP:
$stmt = $this->mysqli->prepare($query);
ist das in Ordnung, oder sollte man Objekte nicht verschachteln?
PHP:
$mysqli = $this->mysqli;
$stmt = $mysqli->prepare($query);

2) Ich gehe davon aus, dass Variablen die ohne (public, private oder protected) in einer Methode initialisiert werden nur in der Methode gültig sind. Z.B. $query und $stmt in meinem Fall, oder?

3) Wenn ich nach ModelViewerController - Ansatz arbeite: Ist es üblich, wenn ich die model-Object Eigenschaften aus der Datenbank fülle und bei Darstellung dieses Object komplett an ein viewer-object übergebe und dann die Eigenschaften des model-object aus dem viewer-object heraus anspreche? Alternativ würde mir noch einfallen, die Eigenschaften alle einzeln an das viewer-object zu übergeben und damit neue Eigenschaften zu füllen oder man nutzt z.B. JSON oder ein array, um daten zwischen den Objekten auszutauschen.
 
1) Ist schon OK so.
2) Ja, du kannst aber sowieso keine Variablen mit public, private oder protected innerhalb einer Methode *deklarieren*.
3) Alle Daten in einem Objekt zu kapseln ist imho besser, hängt aber auch vom Anwendungsfall ab.
 

Ähnliche Themen

Zurück
Oben