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?
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
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.
Muokkaus. Ongelmani oli virheellinen nimi: __costruct olisi pitänyt olla __construct. =)
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ö? =)
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.
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.
Ainenkin funktiossa Henkilo pitää $tyontekijat tauluun viitata näin aiemman sijasta: $this->tyontekijat.
Edit: Samoin siinä työntekijöiden tulostamisfunktiossa (Tyontekijat).
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
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ä.
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(); ?>
-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.
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ä.
Aihe on jo aika vanha, joten et voi enää vastata siihen.