Bernd: PDO Klasse erweitern

Servus,

ich versuche schon die ganze Zeit, folgendes um zusetzen: Ich möchte eine Art Ersatzfunktion für bindParam erstellen die die Sache etwasübersichtlicher machen soll. Dazu habe ich die PDO Klasse erweitert:

class ePDO extends PDO
{
	/**
	 * Secure PDO Prepare
	 *
	 * @param string $Query
	 */
	public function SQL($Query)
	{
		if(strpos($Query, 'WHERE') === false && strpos($Query, 'INSERT') === false)
		{
			die('ERROR: No WHERE clause defined.');
		}

		return $this->prepare($Query);
	}

	/**
	 * Binding numbers to same Postname
	 */
	public function Number($Name)
	{
		return ? bindParam(':'. $Name, $_POST[$Name], PDO::PARAM_INT);
	}
}

Die "SQL" Funktion habe ich gemacht, nachdem ich ein update in meiner Datenbank ausgeführt habe und irgendwann gemerkt habe, dass ich alle Daten überschrieben habe bei bestimmten Werten. Ist mir natürlich erst aufgefallen, als ich die Backups gelöscht habe, wo die noch korrekt waren... Die funktioniert auch wie gewünscht. Nur ich weiß nicht, wie ich das mit dem bindParam machen soll. Der Einfachheit halber, habe ich das erstmal auf public gesetzt.

Statt
$Q = $do->db->SQL
$Q->bindParam

möchte ich
$Q = $do->db->SQL
$Q->Numbers

haben.

Habe dabei schon einige sachen ausprobiert. Selbst die Funktion direkt an

$Q = $do->db->SQL('Query')->Numbers

hängen geht nicht. Anscheinend fehlt mir da noch das richtige Verständnis, wie man richtig innerhalb von Klassen auf die Methoden zugreift.

Kann mir jemand weiterhelfen und ggf. die Lösung für das Problem geben?

Danke, Bernd

  1. Tach!

    Ich möchte eine Art Ersatzfunktion für bindParam erstellen die die Sache etwasübersichtlicher machen soll. Dazu habe ich die PDO Klasse erweitert:

    Alternativer Vorschlag: Man kann die Parameter auch als Array dem execute() übergeben und spart sich so das Gebinde. Dass die dann alle als String übergeben werden, stört nicht weiter.

    class ePDO extends PDO
    {
    	/**
    	 * Secure PDO Prepare
    	 *
    	 * @param string $Query
    	 */
    	public function SQL($Query)
    	{
    		if(strpos($Query, 'WHERE') === false && strpos($Query, 'INSERT') === false)
    		{
    			die('ERROR: No WHERE clause defined.');
    		}
    		return $this->prepare($Query);
    	}
    
    	/**
    	 * Binding numbers to same Postname
    	 */
    	public function Number($Name)
    	{
    		return ? bindParam(':'. $Name, $_POST[$Name], PDO::PARAM_INT);
    	}
    }
    

    Nur ich weiß nicht, wie ich das mit dem bindParam machen soll. Der Einfachheit halber, habe ich das erstmal auf public gesetzt.

    Das nützt nur nichts, weil das prepare() ein Objekt der Klasse PDOStatement zurückgibt, dein Bind-Ersatz jedoch in ePDO steht. Der müsste in PDOStatement angesiedelt werden. Dummerweise kann man aber die bestehende Klasse nicht erweitern. Eine neue erstellen, die von PDOStatement erbt und dein Bindung enthält, ist zwar möglich, aber das prepare() stört sich daran nicht und gibt weiterhin PDOStatement zurück. Nun könnte man aufwendig mit dem Decorator-Pattern das PDOStatement in eine neue Klasse einbetten und am Ende von ePDO::SQL() das PDOStatement dekorieren und zurückgeben. Aber das kann man sich sparen, wenn man im PHP-Handbuch das PDO-Attribut PDO::ATTR_STATEMENT_CLASS entdeckt hat. Damit kannst du also eine abgeleitete Klasse von PDOStatement mit deinen Bind-Methoden erweitern und dem PDO verklickern, dass es diese nehmen soll.

    Um nun ein Fluent Interface oder Method Chaining zu erreichen, muss dein Bind-Ersatz nicht das Ergebnis von bindParam() zurückgeben, denn das ist nur ein Boolean. Stattdessen sollte das innerhalb der Ersatzmethode ausgewertet werden und im Fehlerfall eine Exception werfen. Im Gutfall muss $this zurückgegeben werden, also ein Verweis auf sich selbst, so dass man da weitere Methoden der Klasse hintendrangehängt aufrufen kann.

    Und auch execute() darf nicht das Ende der Fahnenstange sein, weil du die Instanz auch noch brauchst, um die Ergebnismenge abzurufen. Mindestens das execute() muss also so überschrieben werden, dass es ebenfalls $this zurückgibt (oder eine Exception wirft).

    dedlfix.

  2. Servus,

    ich versuche schon die ganze Zeit, folgendes um zusetzen: Ich möchte eine Art Ersatzfunktion für bindParam erstellen die die Sache etwasübersichtlicher machen soll. Dazu habe ich die PDO Klasse erweitert:

    Einzelne Methoden Delegieren ist einfacher als die Erbschaft einer ganzen Klasse anzutrefen. Du erstellst eine Instanz der PDO-Klasse und legst diese Instanz in ein Attribut der Instanz Deiner eigenen Klasse.

    In einer eigenen Methode, die auch so heißen kann wie die gleichnamige PDO-Methode, kannst Du sodann die PDO-Instanz nutzen, weil sie ja als Attribut zur Verfügung steht.

    Eine Factory wäre darüber hinaus ein zweckmäßiger Rahmen für Sowas, wo Du in Deiner eigenen Klasse nur noch Methoden aufrufst: Methoden in denen bei Bedarf Instanzen nicht verwandter Klassen erstellt und ggf. an eigene Attribute gebunden werden.

    Und Nochwas: HTTP kennt keine Datentypen. D.h., Parameter sind nicht etwa integer sondern grundsätzlich Bytesequenzen. pl

    1. Tach!

      Einzelne Methoden Delegieren ist einfacher als die Erbschaft einer ganzen Klasse anzutrefen. Du erstellst eine Instanz der PDO-Klasse und legst diese Instanz in ein Attribut der Instanz Deiner eigenen Klasse.

      In einer eigenen Methode, die auch so heißen kann wie die gleichnamige PDO-Methode, kannst Du sodann die PDO-Instanz nutzen, weil sie ja als Attribut zur Verfügung steht.

      Ja, und dann schreibst du Wrapper auch für sämtliche Methoden, die du gar nicht ändern wolltest, oder wie hast du dir das vorgestellt?

      Und dann gehts weiter, denn das eigentlich Interessante im vorliegenden Fall ist die Klasse PDOStatement, deren Verhalten der OP gern anders hätte. Schreibst du da noch eine Wrapper-Klasse drumherum. Ich sehe grad nicht, wie hier zwei Dekoratoren einfacher sein sollen, als das Erweitern einer Klasse um die gewünschte Funktionalität, und den Rest so zu lassen, wie er bereits in ausreichender Form vom System bereitgestellt wird.

      Was ist denn der konkrete Vorteil deines Vorschlags gegenüber der Vererbung?

      Eine Factory wäre darüber hinaus ein zweckmäßiger Rahmen für Sowas, wo Du in Deiner eigenen Klasse nur noch Methoden aufrufst: Methoden in denen bei Bedarf Instanzen nicht verwandter Klassen erstellt und ggf. an eigene Attribute gebunden werden.

      PHP ist das in diesem Fall verwendete System. Da heißen Attribute Eigenschaften und ein Binden auf Klassenebene gibt es nicht. Es wäre schön, wenn du die für das betreffende System üblicherweise verwendeten Fachbegriffe nehmen würdest und auch von den Eigenschaften dieses Systems und nicht irgendeines anderen ausgehen würdest.

      Und Nochwas: HTTP kennt keine Datentypen. D.h., Parameter sind nicht etwa integer sondern grundsätzlich Bytesequenzen.

      In welcher Art und Weise beeinflusst das Datenbankzugriffe?

      dedlfix.

      1. Und dann gehts weiter, denn das eigentlich Interessante im vorliegenden Fall ist die Klasse PDOStatement,

        PHP Programmierer sind bekannt dafür, dass sie für jeden Furz ne eigene Klasse brauchen.

        Was ist denn der konkrete Vorteil deines Vorschlags gegenüber der Vererbung?

        Ein Methodenaufruf, fertig. M. ist innerhalb der Factory definiert und erstellt ggf. die DB-Verbindung. Ob das mit PDO oder mysqli oder sonstwas erfolgt, ist völlig Wurscht. Und ob da eine Statement-Klasse erstellt werden muss ist auch egal. Eine Factory abstrahiert und die Methode ist austauschbar. Wie der DB-Name lautet, bringt $this (die eigene Instanz) in einer seiner Eigenschaften (Attribute) mit. Credentials sind zentral hinterlegt.

        Und Nochwas: HTTP kennt keine Datentypen. D.h., Parameter sind nicht etwa integer sondern grundsätzlich Bytesequenzen.

        In welcher Art und Weise beeinflusst das Datenbankzugriffe?

        I.d.R. definieren Datenbanken Datentypen. Wer hätte das gedacht. pl

        1. Tach!

          PHP Programmierer sind bekannt dafür, dass sie für jeden Furz ne eigene Klasse brauchen.

          Selten so einen Quatsch gelesen.

          Was ist denn der konkrete Vorteil deines Vorschlags gegenüber der Vererbung?

          Ein Methodenaufruf, fertig.

          Das möchte ich sehen. Bitte zeig doch mal in Code oder Pseudocode, wie du das Problem des OP konkret lösen würdest.

          Und Nochwas: HTTP kennt keine Datentypen. D.h., Parameter sind nicht etwa integer sondern grundsätzlich Bytesequenzen.

          In welcher Art und Weise beeinflusst das Datenbankzugriffe?

          I.d.R. definieren Datenbanken Datentypen. Wer hätte das gedacht.

          Und weiter? Was hat HTTP damit zu tun?

          dedlfix.