Kirjautuminen

Haku

Tehtävät

Keskustelu: Nettisivujen teko: Olio-ohjelmointi PHP:llä

Sivun loppuun

Macro [10.12.2009 16:20:42]

#

Koitin tässä aloittaa PHP:llä olio-ohjelmoinnin, koska sitä näytetään käytettävän niin monessa paikassa. Koitin tehdä ihan simppelin ohjelman.

<?php
class Henkilo {
	public function __costruct($nimi, $ika, $kotikunta) {
		$this -> nimi = $nimi;
		$this -> ika = $ika;
		$this -> kotikunta -> $kotikunta;
	}
}

$tiedot = new Henkilo("Pekka", 16, "Pajulahti");
echo "Hei", $tiedot -> nimi, "!";
?>

Tulostuksena on "Hei!", kun pitäisi tulostuksen olla "Hei Pekka!". Mitäköhän tällä kertaa tein väärin?

Metabolix [10.12.2009 16:28:17]

#

Kannattaa (harjoitellessa ja virheitä etsiessä) säätää PHP:n kaikki avuliaat ilmoitukset päälle joko php.ini-tiedostosta tai skriptissä.

display_errors = On
error_reporting = E_ALL | E_STRICT
<?php
ini_set("display_errors", 1);
ini_set("error_reporting", E_ALL | E_STRICT);

Koodistasi tulee seuraava ilmoitus:
Notice: Undefined property: Henkilo::$nimi in /tmp/- on line 11

Et ole esitellyt jäseniä.

<?php
class Henkilo {
  public $nimi;
  // ...
}

Nuolen (->) ympärille ei ole tapana jättää välejä. Lisäksi kotikunnan kohdalla on toinen aivan ilmeinen virhe.

Edit. Olet näköjään myös kirjoittanut funktion nimen väärin.

Macro [10.12.2009 16:34:47]

#

Muokkaus. Ongelmani oli virheellinen nimi: __costruct olisi pitänyt olla __construct. =)

Macro [10.12.2009 20:17:44]

#

Jos minulla on näitä muuttujia (Mitä nimeltään ovatkaan, $this->jotain = jotain), niin voinko jotenkin asettaa kaikki $this->henkilo_$nimi kohdat globaaleiksi? Koska kun käyttäjä voi määritellä $nimi-muuttujan arvon, niin siinä voi olla mitä vain. Kun taas voi olla monta kohtaa ja pitää muuttaa jotain, niin pitää pystyä hakemaan tiettyä henkilo_x-kohtaa.
Ymmärsittekö? =)

Metabolix [10.12.2009 20:28:56]

#

Taidat nyt yrittää väkisin jotain aivan typerää.

Jos haluat monta henkilöä, luo monta Henkilö-luokan ilmentymää (oliota). Jos haluat yhteen luokkaan monen henkilön tiedot, käytä taulukkoa, jossa on henkilöolioita.

<?php
class Firma {
  private $henkilosto = array();
  public function lisaa(Henkilo $h) {
    $this->poista($h);
    $this->henkilosto[] = $h;
  }
  public function poista(Henkilo $h) {
    $i = array_search($h, $this->henkilosto);
    if ($i !== false) {
      unset($this->henkilosto[$i]);
    }
  }
}

$pekka = new Henkilo("Pekka");
$paavo = new Henkilo("Paavo");
$ab_koodipaja_oy = new Firma();

// Uusia työntekijöitä...
$ab_koodipaja_oy->lisaa($pekka);
$ab_koodipaja_oy->lisaa($paavo);

// Annetaan potkut.
$ab_koodipaja_oy->poista($pekka);

En testannut, mutta vähintäänkin ajatus käynee tästä ilmi.

Macro [10.12.2009 21:12:52]

#

Kiitos, tämä luokilla ohjelmointi on hauskaa. Koitin tehdä tälläistä systeemiä:

<?php
class Tyopaikka {
	public $tyontekijat;
	public function __construct($firma) {
		echo "<h3>Firman $firma henkilöstö:</h3>\n";
	}

	public function Henkilo($nimi, $osoite, $email, $kokemus, $ika) {
		$tyontekijat[] = "$nimi, $osoite, $email, $kokemus, $ika";
	}

	public function Tyontekijat() {
		for($i = 0; $i < sizeof($tyontekijat); $i++) {
			$tyontekijatiedot = explode(", ", $tyontekijat[$i]);

			echo $tyontekijatiedot[0] . ":\n<ul>\n";
			echo "Osoite: ", $tyontekijatiedot[1], "<br>\n";
			echo "Sähköposti: ", $tyontekijatiedot[2], "<br>\n";
			echo "Kokemus: ", $tyontekijatiedot[3], "<br>\n";
			echo "Ikä: ", $tyontekijatiedot[4], "<br>\n";
			echo "</ul>\n";
		}
	}
}

$firma = new Tyopaikka("HS");
$martti = $firma->Henkilo('Martti', 'Ohjelmointikuja 2', 'martti.ohjelmoija@ol.fi', 'Pajulahden sanomat', 56);
$tyontekijat = $firma->Tyontekijat();
?>

Ensin lisään Martin firmaan, mutta kun koitan hakea niitä ($firma->Tyontekijat()), niin tulostuu tyhjää. Debuggasin tuota, niin funktiossa Tyontekijat ei ole näkyvyyttä $henkilot-taulukolle.

jo123 [10.12.2009 21:19:13]

#

Ainenkin funktiossa Henkilo pitää $tyontekijat tauluun viitata näin aiemman sijasta: $this->tyontekijat.

Edit: Samoin siinä työntekijöiden tulostamisfunktiossa (Tyontekijat).

Teuro [10.12.2009 21:33:12]

#

Jospa minäkin työnnän lusikkani soppaan ja heitän peliin tällaisen koodin pätkän.

<?php
class Yritys{
	private $_tyontekijat;

	public function lisaa_duunari(Tyontekija $tyontekija){
		$this->_tyontekijat[] = $tyontekija;
	}

	public function laske_palkat(){
		$tmp_palkka = 0;

		for($a = 0; $a < count($this->tyontekijat); $a++){
			$tmp_palkka += $this->_tyontekijat[$a]->anna_palkka();
		}

		return $tmp_palkka;
	}
};

class Tyontekija{
	private $_etunimi;
	private $_sukunimi;
	private $_osasto;
	private $_palkka;

	public function __construct($en, $sn, $os, $pa){
		$this->_etunimi = $en;
		$this->_sukunimi = $sn;
		$this->_osasto = $os;
		$this->_palkka = $pa;
	}

	public function anna_nimi(){
		return ($this->_etunimi . " " . $this->_sukunimi);
	}

	public function anna_palkka(){
		return $this->_palkka;
	}
};

$duunarit = file("tt.txt");
$kpl = count($duunarit);

$firma = new Yritys();

for($a = 0; $a < $kpl; $a++){
	$tiedot = explode("|", $duunarit[$a]);
	$en = $tiedot[1];
	$sn = $tiedot[2];
	$os = $tiedot[3];
	$pa = $tiedot[4];

	$tmp_duunari = new Tyontekija($en, $sn, $os, $pa);

	$firma->lisaa_duunari($tmp_duunari);
}

echo "palkat yhteensä = " . $firma->laske_palkat();
?>

tt.txt tiedosto voisi olla vaikka tällainen:

0|Juha|Teurokoski|Työnjohto|2100
1|Joku|Muu|Työnjohto|1750

Tällöin tuloste olisi palkat yhteensä = 3850

Teuro [11.12.2009 14:41:01]

#

Luokalla 'Yritys' ei ole jasentä Yritys::$firma, Yritys::$perustettu, Yritys::$tyontelijoita, eikä Yritys::$tyontekijat. Työntekijöiden pitäisi luokan Tyontekija olioita, nythän ne ovat taulukossa merkkijonona. Eli siis tee ensin sellainen koodi, joka toimii ihan oikein. Sen jälkeen voit lisätä tuohon koodiin noita hienouksia.

EDIT: jaha macro poistikin viestinsä.

Macro [11.12.2009 20:56:59]

#

Nyt sain sitten itselleni toimivan kirjautumissysteemin luokilla tehtyä. Otti vähän pattiin, kun aina ennen tein jokaiselle systeemille oman kirjautumisfunktionsa. Nyt tämä kelpaa kaikille. Voisin ottaa vastaan parannusehdotuksia. =)

Jos tämä vaikuttaa teistä hyvältä, niin voisin suomentaa sen ja lähettää koodivinkiksi.

<?php
// Start the session
session_start();
// ob_start() is required if the page is printed from the text before headers
ob_start();

// Login class
class LoginForm {
	private $users_table, $username_field, $password_field, $main_page, $password, $username, $yhteys, $session_table;
	// Connecting to database, when start the script
	public function __construct($server, $user, $password, $database) {
		//Try to connect, exceptions trow error
		$this->yhteys = mysql_connect($server, $user, $password) or die("Problems with connecting to database.");
		mysql_select_db($database) or die("Can't connect to database $database!");
	}

	// Settings
	public function Settings($user_table, $username_field, $password_field, $header_page, $session_table) {
		$this->users_table = $user_table;
		$this->username_field = $username_field;
		$this->password_field = $password_field;
		$this->main_page = $header_page;
		$this->session_table = $session_table;
	}

	// The login form generating
	public function Return_login_form() {
		// Return the structure of the form
		echo "<table>\n";
		echo "<form action=\"?login\" method=\"POST\">\n";
		echo "<tr><td>Tunnus:</td><td>Salasana:</td></tr>\n";
		echo "<tr><td><input type=\"text\" name=\"tunnus\"></td><td><input type=\"password\" name=\"salasana\"></td></tr>\n";
		echo "<tr><td><input type=\"submit\" value=\"Kirjaudu\"></td></tr>\n";
		echo "</form>\n</table>\n";
		}

	// Called when get ?login
	public function Check_login() {
		if(isset($_GET["login"])) {
			// Take username and password
			$username = $_POST["tunnus"];
			$password = $_POST["salasana"];

			// Check that the username and password have been met
			if(strlen($username) > 0) {
				if(strlen($password) > 0) {
					$password = md5($password);

					// Check, if there is this user
					$query = mysql_query("SELECT * FROM " . $this->users_table . " WHERE " . $this->username_field . " = '$username' AND " . $this->password_field . " = '$password'")  or die("MySQL Error: Problem with query in login_class.php: " . mysql_error());
					if(mysql_num_rows($query) == 1) {
						// Username and password was right
						// Rand and crypt id
						$id = md5(uniqid(""));

						// Put it in a session
						$_SESSION["id"] = $id;
						// Update it to the database too
						mysql_query("UPDATE " . $this->users_table . " SET " . $this->session_table . " = '$id' WHERE " . $this->username_field . " = '$username' AND " . $this->password_field . " = '$password'") or die("MySQL Error: Problem with query in login_class.php: " . mysql_error());

						// Let's go to the main page
						header("Location: " . $this->main_page);
						die;
					} else {
						// Username or password was wrong
						header("Location: " . $_SERVER["PHP_SELF"] . "?error_id=1");
						die;
					}
				} else {
					// Password field was empty
					header("Location: " . $_SERVER["PHP_SELF"] . "?error_id=2");
					die;
				}
			} else {
				// Username field was empty
				header("Location: " . $_SERVER["PHP_SELF"] . "?error_id=3");
				die;
			}
		}

		// If we got an error
		if(isset($_GET["error_id"])) {
			// Errors:
			// error_id=1: Username or password was wrong
			// error_id=2: Password field was empty
			// error_id=3: Username field was empty

			$error_id = $_GET["error_id"];
			if($error_id == 1) {
				// error_id == 1
				echo "Virhe: Tunnus tai salasana oli väärin!";
			} else if($error_id == 2) {
				// error_id == 2
				echo "Virhe: Anna salasana";
			} else if($error_id == 3) {
				// error_id == 3
				echo "Virhe: Anna tunnus!";
			} else {
				// Exception: Unknown error_id
				echo "Virheellinen error_id!";
			}
		}

		// If the user's browser direction is like ?logout
		if(isset($_GET["logout"])) {
			// Unset id-session
			unset($_SESSION["id"]);
			session_destroy();

			// Navigating to the main page
			header("Location: " . $this->main_page);
			die;
		}

		// If the user have got the right session
		if(isset($_SESSION["id"])) {
			// Has logged in
			$session_id = $_SESSION["id"];

			// Make a query, and check if theres a user with this id
			$query = mysql_query("SELECT * FROM " . $this->users_table . " WHERE " . $this->session_table . " = '$session_id'") or die("MySQL Error: Problem with query in login_class.php: " . mysql_error());
			if(mysql_num_rows($query) == 1) {
				//$this->user[] includes all information about user
				$this->user = mysql_fetch_array($query);
			}
		}
	}
}
?>
*** Käyttöesimerkki
<?php
// Jokaisen sivun alussa pitää sisällyttä login_class.php
include("login_class.php");

// Luodaan uusi lomake. Sarakkeet: MySQL serveri, tunnus, salasana, tietokanta
$log = new LoginForm("localhost", "root", "7bvna378", "test");

// Asetukset: Käyttäjätaulu, käyttäjäsarake, salasanasarake, sivu jonne mennää kirjautumisen jälkeen,
// sarakkeen nimi jossa istunnon id sijaitsee
$log->Settings("users", "tunnus", "salasana", "login_page.php", "session");

// Tarkistetaan tunnukset
$log->Check_login();

if($log->user)
	// On kirjauduttu, tulostetaan tervehdys
	echo "Hei {$log->user["tunnus"]}! <a href=\"?logout\">Kirjaudu ulos</a>";
else
	// Tulostetaan kirjautumislomake
	$log->Return_login_form();
?>

tsuriga [11.12.2009 21:27:09]

#

-Luokan toiminta olisi syytä miettiä uudelleen. Nyt tuo on vähän jokapaikan höylä, taikaolio, joka hoitaa kaiken ja vähän päälle. Olioiden tarkoituksena on tarjota loogiset suhteet toimintojen välille.
-Elä käytä die-komentorakennetta
-Virheentarkistus? if (isset($array['key']))...
-PHP_SELF -> SCRIPT_NAME
-Metodien nimissä käytetään useimmissa koodauskonventioissa camelCase-nimeämiskäytäntöä (Check_login -> checkLogin)
-Location-headerille absoluuttinen URI

EDIT: Poistinpa listastani tuon "Ei SELECT * FROM vaan SELECT kentta1, kentta2... FROM". Toki jos tietojen määrä taulussa alkaa kasvaa niin kannattanee kirjoitella metodeita, jotka hakevat vain osan tiedoista.

Teuro [11.12.2009 21:33:29]

#

No mutta eihän sinun todellakaan tarvitse tehdä kuin yksi ainoa kirjautumisfunktio. Luokat eivät tuo tässä mielessä mitään lisäarvoa tuolle järjestelmälle. Sitten jos kerran ilmoitat, että yhteyden ottaminen tietokantaan heittää poikkeuksen pitäisi se jossakin heittää ja napata.

Taulut kannattaa varmaankin pitää omina muuttujinaan, koska nehän eivät voi oikein mitenkään muuttua kesken kaiken. Kirjautumislomake pitäisi myös erottaa tuosta kirjautumisluokasta, koska sekään ei oikein loogisesti liity tuohon luokkaan. Lomakkeelle mielummin oma luokka, jolloin siitä voidaan luoda uusia ihan toisenlaisiakin lomakkeita.

Kirjatumisen jälkeen tuleva kannan päivityksen yhteydessä ei ole tarvetta vertailla käyttäjänimeä ja salasanaa, koska nehän tarkistettiin jo edellä. Virheet voisi tallentaa taulukkoon ja kutsua error_id:n mukaista taulukon alkiota.

Käyttäjän syötteet pitäisi siivota ennen kantaan ajamista, nythän siellä voi olla ihan mitä vaan.

Tämä voi tuntua pahalta kritiikiltä, mutta tuo ei ole olioajattelua oikein mitenkään. Luokkia kannattaa käyttää, jos niistä tuntuu olevan hyötyä.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta