webForumDet fria alternativet

Klass i OOP5

12 svar · 955 visningar · startad av dreas

dreasMedlem sedan jan. 2004140 inlägg
#1

Hej alla!
Man är så himla tacksam över webForum, när det väl behövs trillar man in igen.. söker igenom det man förnärvarande nöter sina hjärnknölar på, och i nödläge lägger man upp en post.

Det är så att jag just nu bygger om en webbplats som jag tidigare konstruerat i efterhand och lagt till funktioner allteftersom. Tro mig - den koden har blivit så insane att inte ens NASA skulle förstå den :)

I samband med ombyggnaden har jag ju snappat upp ett och annat som har hänt på PHP "scenen" sen jag sist tog mig tid att uppdatera mig. Jag pratar självklart om PHP5 och införandet av högre stöd av OOP (objekt orienterad programmering, fint att man t.o.m. kan översätta förkortningen till svenska!) jämfört med PHP4.

Jag är i fullfärd med konstruktionen av min första klass, kallad User. Detta är uppbyggt av smärre kunskap sedan tidigare inom Java (har tokglömt allt från $this-> och konstruktorns egentliga betydelse hehe), samt av det man läst på sig runt om på webben gällande OOP i PHP;

<?php

class User {

	public $id, $email, $firstname, $lastname;
	private $phone, $ref;
	protected $password;
		
	public function __construct() {

	}
	
	private boolean function ctrlUser($email) {		
		$this->email = $email;
		$this->sql = mysql_query("SELECT count(*) FROM users WHERE email = '".$this->email."'") or die(mysql_error());
		
		if (mysql_result($this->sql, 0) > 0) {
			return true;
		} 
		else {
			return false;
		}
	
	}
	
	public function createUser($email, $password, $firstname, $lastname, $phone, $ref) {
		//$db = new DB(); OPEN DATABASE - place in global
		$this->email = $email;
		$this->password = $password;
		$this->firstname = $firstname;
		$this->lastname = $lastname;
		$this->phone = $phone;
		$this->state = "created"; //extra
		$this->admin = "0"; //extra
		$this->ref = $ref; //extra

		if ($this->ctrlUser($this->email)) {
			$msg = "Användarnamnet/e-mail är redan använt. Ett mail har skickats till din angivna e-mailadress med dina inloggningsuppgifter samt status.";
			return "<script Language='JavaScript'>location.href=('?msg=$msg');</script>";
		} 
		else {
			mysql_query("INSERT INTO users (email,password,firstname,lastname,phone,ref,state,admin,created) VALUES ('".$this->email."','".$this->password."','".$this->firstname."','".$this->lastname."','".$this->phone."','".$this->ref."','".$this->state."','".$this->admin."','".time()."')" or die(mysql_error());
		}//else
	}
	
	public function editUser($id, $email, $password, $firstname, $lastname, $phone, $ref) {
		$this->id = $id;
		$this->email = $email;
		$this->password = $password;
		$this->firstname = $firstname;
		$this->lastname = $lastname;
		$this->phone = $phone;
		$this->ref = $ref;
		
		if ($this->ctrlUser($this->email)) {
			mysql_query("UPDATE users SET email = '".$this->email."', password = '".$this->password."', firstname = '".$this->firstname."', lastname = '".$this->lastname."', phone = '".$this->phone."', ref = '".$this->ref."' WHERE id = '".$this->id."'") or die(mysql_error());
		}
		else {
			//returnera error
		}

	}
	
	public function removeUser() {
	
	}
	
	public function getAllUsers() {
	
	}

}

?>

Jag har ännu inte provat denna kod, tänkte mest kolla ifall mitt tänk stämmer. Nu till en del av mina driljarder frågor:

  • Har jag gjort rätt med att deklarera de olika variablerna i början? Kan jag komma åt en protected (som password) i alla funktioner inom klassen? Finns det överhuvudtaget ngt syfte att sätta variabeln till protected?
  • Konstruktorn är i detta fall lämnad helt tom. Behöver då denna existera i koden? Bör jag sätta in alla variabler (dvs: $this->.. som finns i alla funktioner) i konstruktorn istället? Vill undvika att behöva slänga med alla variabler när man kör klassen, då inte alla behövs i alla funktioner. Eller MÅSTE jag deklarera alla variabler i konstruktorn?
  • Är ctrlUser rätt tänkt, dvs. att jag satt den till boolean function samt att den returnerar det den ska?
  • Går det bra att returnera det script jag har för att generera meddelandet på sidan?
  • Måste jag alltid deklarera mysql_query med $this-> eller körs de som står "lösa" korrekt?

Sa ju att det skulle bli en del :)
Ytterst tacksam för all hjälp jag kan få! Kanske från en vass gammal kompis här som gillar att snurra? ;)

Tack på förhand!
/Andreas

[EDIT] Hade visst skrivit ett £ tecken istället för ett $ tecken.. London calling :) [/EDIT]

SPiNMedlem sedan mars 20007 896 inlägg
#2

Hej greken :)

Får se om jag kan hjälpa dig med det här. ;)

* Har jag gjort rätt med att deklarera de olika variablerna i början? Kan jag komma åt en protected (som password) i alla funktioner inom klassen? Finns det överhuvudtaget ngt syfte att sätta variabeln till protected?

Ja, du har gjort rätt som deklarerar dina variabler i början. Däremot fungerar inte nyckelordet 'protected' som du tror, 'protected' används för variabler som subklasser/underklasser ska kunna läsa/använda sig av. Alltså när man arbetar med arv. Du kommer däremot åt den överallt i klassen User. 'private'-variabler kommer endast klassen User åt, inte klasser som ärver från User. Så i ditt fall borde även '$password' vara deklarerad som privat. (Om du inte ska arbeta med arv.)

* Konstruktorn är i detta fall lämnad helt tom. Behöver då denna existera i koden? Bör jag sätta in alla variabler (dvs: $this->.. som finns i alla funktioner) i konstruktorn istället? Vill undvika att behöva slänga med alla variabler när man kör klassen, då inte alla behövs i alla funktioner. Eller MÅSTE jag deklarera alla variabler i konstruktorn?

Ta bort konstruktorn. Om du inte har någonting som måste initieras när klassen initieras behövs inte en konstruktor.

* Är ctrlUser rätt tänkt, dvs. att jag satt den till boolean function samt att den returnerar det den ska?

Ja, det tycker jag är rätt tänkt.

* Går det bra att returnera det script jag har för att generera meddelandet på sidan?

Nja, det beror på hur du anropar/placerar det när det returneras. T.ex. inom body-taggarna på sidan fungerar alldeles utmärkt, men sidan kommer ändå att laddas klart innan javascriptet körs. Det kanske vore bättre att sätta en omdirigering direkt i PHP istället?

header("Location: sida.php?msg=" . $msg);

Däremot tycker jag inte att det här passar in i din klass. Det är något som borde skötas utanför klassen, där klassen används.

* Måste jag alltid deklarera mysql_query med $this-> eller körs de som står "lösa" korrekt?

'$this' behöver du inte använda alls egentligen, så länge de lokala variablerna (lokala för funktionen/metoden) inte är döpta till samma sak som instansobjekten/instansvariablerna (klassens variabler). Däremot ökas förståelsen för koden och läsbarheten blir bättre om man visar att det är en instansvariabel/instansobjekt och inte en lokal variabel som används.

Hoppas det blev något klarare.

Med vänliga,
Blömpa ;)

SPiNMedlem sedan mars 20007 896 inlägg
#3

Nu har jag tittat lite mer på din klass... ;)

Jag tycker att du använder ett lite märkligt tillvägagångssätt för metoderna editUser() och createUser(). Dessa metoder anropas med ett antal värden ($id, $password, $email, m.fl.). Sen kopierar du bara dessa värden till de variabler som gäller för klassen, och sedan placerar du värdena i en SQL-fråga. Med andra ord kopierar du värdena helt i onödan första gången.

Så här hade nog jag gjort:

class User {
    private $db;

    public function __construct($db) {
        $this->db = $db;
    }

    public function checkUser($email) {
        $stmt = $this->db->prepare("SELECT COUNT(*) AS EmailCount FROM users WHERE email=:email");
        $stmt->bindParam(":email", $email);
        $stmt->execute();

        $result = $stmt->fetch(PDO::FETCH_ASSOC);

        $stmt->closeCursor();

        if(intval($result["EmailCount"]) > 0)
            return true;
        else
            return false;
    }

    public function createUser($email, $password, $name) {
        $stmt = $this->db->prepare("INSERT INTO users (email, pwd, username) VALUES(:email, :wd, :uname)");
        $stmt->bindParam(":email", $email);
        $stmt->bindParam(":wd", $password);
        $stmt->bindParam(":uname", $name);
        $stmt->execute();
        $stmt->closeCursor();
    }
...

När du använder klassen:

$user = new User(new PDO("mysql:host=localhost;dbname=DIN_DATABAS", "USER", "PASSW"));
$user->createUser("dreas@webforum.nu", "lösenord", "användarnamn");
...
if($user->checkUser("dreas@webforum.nu")) {
    echo "email-adressen upptagen";
}
...

Bara ett litet exempel.

Som du ser använder jag inte mysql_*-funktionerna, utan de nya PDO-funktionerna. Med dessa kan man använda prepared statements som kan underlätta en hel del, då man slipper oroa sig för SQL-injektioner som exempel.

Jag passar även med databas-kopplingen in till konstruktorn för klassen User, så att den används klassen igenom och slipper skapas i varje metod.

Det som saknas i mitt exempel är felhantering. Med PDO kan man fånga s.k. exceptions om något har gått fel, och arbeta med felhanteringen utifrån dom.

Fråga om du undrar något.

/b

dreasMedlem sedan jan. 2004140 inlägg
#4

Haha, Blömpa!! Iskallt! Visste väl att du skulle grejja biffen efter allt jag läst av det du skrivit kring OOP här på webForum :)
Hur är det i Göteborg förresten? Style eller? Jobbar du ngt med webdesign/-applikationer? I annat fall borde du ju verkligen göra det och tjäna lite sköna dineros :)
Lite för offtopic där kanske! Soli soli (japanskt)

Hur som helst har du ju skänkt solljus över mitt funderande! Då har jag grepp på protected/private, konstruktorn kan tas bort (får väl gråta över denna då jag behöver använda en) samt känns $this-> fortfarande lite weird.. men det verkar ju som att jag tänkt rätt iaf! Tack tack!

Lite frågor om upplägget dock;
Denna klass User, ska jag även använda den till funktioner som ex. getUserInfo och getAllUser? Dvs presentations form av Users? Tänker nämligen att createUser, editUser och removeUser används ju vid manipulering av databasen. Presentationsdelen skriver ju egentligen bara ut allt, och då är det kanske dumt att belasta och läsa in onödiga funktioner.
Hur skulle ni lägga upp det? Vet att det finns typ mysql_query klasser man kan skapa, men jag förstår ärligt talat inte syftet med dem, förutom att göra själva query anropet lite mer förståligt, eftersom det tar upp onödigt många rader i php koden på själva sidan jämfört med en vanlig query direkt i koden.
Sedan finns det ju även en klass för databaskoppling har jag sett, samma sak där.. varför inte bara använda en vanlig include för databaskopplingen? Man måste ju ändå inkludera klassen till sidorna.
I själva verket är väl frågan - vad vinner jag på att ha en DB klass istället för att köra en DB include?

Hälsningar,
Sarre ;)

SPiNMedlem sedan mars 20007 896 inlägg
#5

Hoppas du lästa mitt andra svar också, det var lite bättre tycker jag.

Här kommer lite om upplägget;
Jag vet inte hur mycket information som kommer att passas fram och tillbaka mellan dina klasser och ditt presentationslager, men man kan säga att klassen User hanterar en användare (håller information, uppdaterar mot databasen). Kanske borde du också implementera en UserManager-klass? Som hanterar alla användare, skapa ny användare mot databasen, tar bort användare från databasen, osv.

Sånt här är egentligen overkill för webapplikationer som endast lever i någon sekund (om ens det). Det bör egentligen räcka med en User-klass, tycker jag.

Ang. databas-hanteringen så har du svaret i mitt andra inlägg i tråden. :)

Japp, jag arbetar med webapps - you've got PM. ;)

/b

dreasMedlem sedan jan. 2004140 inlägg
#6

Ojoj, här går det undan! :)
Postade precis min reply här ovan, men då kan vi ju ta det svaret du hade innan där. Precis vad jag tänkte, så har jag missförstått det här med $this->
Undrade först ifall dessa egentligen skulle deklareras i constructorn, men du kör med ngt du kallar för stmt? Okej okej.. mycket här nu - we like!!
Måste läsa på om STMT samt PDO (tack tack för den, tyckte det var skit ugly med mysql_query i snajsiga klasser - kul att jag HADE missat nåt!) :)
Ska titta närmare på koden, och som sagt läsa på lite, för att sedan återkomma (hinner inte sitta mer ikväll) om det är okej? Men en fråga så här on the fly - ska jag;
a) köra en sån db connect till klassen
b) skapa mig en db connect klass (har fnulat ihop en efter lite exempel som har __sleep och __wakeup (ooh) hehe)
c) använda mig av en gammal hederlig db connect include i början av varje sida

Jag hade ju lutat mig mer mot c) .. men jag kanske tänker för gammalmodigt och klent?

Hur som helst - stort tack för tiden du la ner på att korrigera till min klass, var lite därför jag tänkte lägga en post här för att se ifall jag hade greppat tänket/kodandet innan jag ger mig på en massa andra klasser.

Hälsningar,
Andreas

SPiNMedlem sedan mars 20007 896 inlägg
#7

'$stmt' är bara ett variabelnamn på ett objekt av typen PDO::Statement. Det tillhör med andra ord PDO-klasserna och det är inga konstigheter. Du greppar det fort.

a-b) Japp, det ska du. Jag tycker det är lite onödigt att skapa en separat klass för databaskopplingen. __sleep() och __wakeup() är overkill, skippa det. :)
c) Ja, det kan du göra. Jag brukar ha en include-fil för de viktiga parametrar som behövs för att skapa en databaskoppling. Ex:

__db__config.php:

<?php
define("DBNAME", "databasnamnet");
define("DBUSR", "databasanvändare");
define("DBPWD", "databaslösenord");
define("DBHOST", "databashost");
?>

sida.php:

<?php
require_once("__db__config.php");

$pdh = new PDO("mysql:host=" . constant("DBHOST") . ";dbname=" . constant("DBNAME"), constant("DBUSR"), constant("DBPWD"));
...

Alltså att man deklarerar konstanter med de värden som kan ändras senare under projektet, eller för att göra återanvändbar kod.

:)

dreasMedlem sedan jan. 2004140 inlägg
#8

Ser stiligt ut med resultatet av hur klassen ser ut nu! Gillar PDO funktionerna, verkar kunna göra det mesta plus att ditt sätt att skapa en ny PDO är ju glasklar!

Hur som helst har jag läst på lite nu om PDO, inga konstigheter egentligen men något som fick mig att fundera var denna varning från php.net:

This back trace will likely reveal the full database connection details, including the username and password. It is your responsibility to catch this exception, either explicitly (via a catch statement) or implicitly via set_exception_handler().

Måste man alltså skapa en set_exception_handler()? Om så är fallet, hur ska man göra det? I varje klass, eller i en klass för sig? Läskigt om koden skulle spotta ur sig information kring login till databasen..

Sedan måste jag bara dubbelkolla hur man kallar en funktion från en annan i samma klass. Gör man det på samma sätt som man gör i Java, att man bara skriver funktionens namn och skickar med rätt värden? Ex:

if(checkUser($email))

Där checkUser är en funktion i samma klass.
Kan även passa på att fråga om det nu inte skulle vara från samma klass, måste man då använd arvskap (extends) eller kan man skapa en instans av klassen innan man kallar på korrekt funktion (likt det man gör i den löpande PHP koden sen)?

Tack på förhand
Andreas

SPiNMedlem sedan mars 20007 896 inlägg
#9

dreas skrev:

Måste man alltså skapa en set_exception_handler()? Om så är fallet, hur ska man göra det? I varje klass, eller i en klass för sig? Läskigt om koden skulle spotta ur sig information kring login till databasen..

Japp, lite läskigt - men hur enkelt som helst att förebygga.

try {
     $pdh = new PDO(...);
     ...
} catch(exception $e) {
     echo "Något gick snett!";
}

Precis som i Java. :)

dreas skrev:

Sedan måste jag bara dubbelkolla hur man kallar en funktion från en annan i samma klass. Gör man det på samma sätt som man gör i Java, att man bara skriver funktionens namn och skickar med rätt värden? Ex:

if(checkUser($email))

Där checkUser är en funktion i samma klass.
Kan även passa på att fråga om det nu inte skulle vara från samma klass, måste man då använd arvskap (extends) eller kan man skapa en instans av klassen innan man kallar på korrekt funktion (likt det man gör i den löpande PHP koden sen)?

Japp, precis som i Java. Det räcker med att anropa med funktionsnamnet. :)

Lycka till!

colioneMedlem sedan juni 20014 421 inlägg
#10

Kasta gäran ett öga på __autoload() så slipper du includa/requira alla filer du behöver. ;)

dreasMedlem sedan jan. 2004140 inlägg
#11

Toppen med svaren blömpa och Colione!

Måste bara dubbelkolla att jag förstått den här exception rätt.. den ska alltså kallas med en try och catch, där try omger databaskopplingen - i vårt exempel:

$pdh = new PDO("mysql:host=" . constant("DBHOST") . ";dbname=" . constant("DBNAME"), constant("DBUSR"), constant("DBPWD"));

Och då ska det vara grönt? Man vill ju gärna vara säker på detta.. konstigt att det är upplagt så i PHP, verkar lite onödigt risky.

Grym funktion du tipsa om Colione, med __autoload()!! Tack tack!

colioneMedlem sedan juni 20014 421 inlägg
#12

btw. Jag tror du redan har upptäckt det men

 private boolean function ctrlUser($email) {

är sytax fel.
public, protected, private och abstract är de enda deklarativa (?) orden för en egenskap/metod i php.

dreasMedlem sedan jan. 2004140 inlägg
#13

Japp, såg det i SPiNs kod. Javan sitter kvar i mig, hehe, tack ändå!
Tack Björn för grym hjälp! Och tack Colione för tipset om autoload, den kommer verkligen komma till användning!

Vänliga hälsningar
Andreas

141 ms totalt · 3 externa anrop · v20260731065814-full.30151723
0 ms — hämta forumlista (cache)
0 ms — hämta statistik (cache)
138 ms — hämta tråd, inlägg och bilagor (db)