PHP návod - jednoduchý guestbook bez databázy v OOP

Publikované 03.3.2014 14:00   |  Návody, PHP 0

Aj v dnešnom článku o PHP návode si ukážeme ako vytvoriť ďalší komponent v OOP. Vytvoríme si jednoduchú aplikáciu knihy návštev, ktorá nebude závislá od databázy. Áno, je to bezpečné. Na naše údaje nebude mať možnosť žiaden návštevník pristúpiť aj keď tieto údaje sa budú nachádzať v jednom súbore.


Nikto nebude vidieť do súboru našich správ, o to sa postará .htaccess. Ale to neskôr. Poďme postupne. Keďže ako aj iné návody, píšem to tak, aby ste mali aplikáciu, ktorú bude potrebné určite niečim doplniť, ale hlavnú kostru aplikácie už budete mať za sebou.

Prv si vytvoríme hierarchiu skriptovacích súborov, ako by naša aplikácia vyzerala:

Guestbook files

Môžeme tam vidieť 2 triedy - guestbook.class.php a pagination.class.php. Prv sa poďme venovať triede Guestbook:

class Guestbook (classes/guestbook.class.php)

V tejto triede budú zahrnuté metódy pre prácu s guestbookom. Na začiatok si ale vytvorme deklaráciu triedu a zavedieme si konštantu ADMIN_PASS, ktorá bude slúžiť pre autorizáciu používateľa (oprávnenie na mazanie príspevkov), vlastnosti $per_page a $lines. Vlastnosť $per_page bude obsahovať koľko príspevkov na stranu sa má zobraziť a vlastnosť $lines bude obsahovať koľko správ máme (bude využitá pri stránkovaní):

class Guestbook
{
    const ADMIN_PASS = '56ac2821246eb2d132df130db19aa9da25850fd3'; // tajneheslo
    public $per_page = 5; // počet príspevkov na stranu
    public $lines = 0; // celkový počet príspekov
    
    // ...
    
}

Konštanta ADMIN_PASS obsahuje zašifrovanú hodnotu jednosmernou šifrou sha1.

V tomto návode nám konštruktor potreba nebude, lebo nie je čo spraviť vopred pred vytvorením samotného guestbooku. Pustime sa rovno na metódu addMessage(), ktorá bude obsahovať parametre ako $author, $email a $message. Táto metóda sa bude starať o to, aby uložilo správne ďalší príspevok. Začiatok je teda následovný:

    // metóda na uloženie správy
    public function addMessage($author, $email, $message)
    {
      // ...
    }

Určite nám tieto 3 veci nebudú stačiť na kompletnú správu. Je potreba mať nejaké identifikačné číslodátum, kedy bola daná správa odoslaná a ip adresa aby sme vedeli, kto danú správu vôbec odoslal. To sa už postaráme vo vnutri metódy:

      $date = time(); // čas odoslaného príspevku
      $id = md5(uniqid(time())); // unikátne ID príspevku
      $ip = $_SERVER['REMOTE_ADDR']; // IP klienta

Na vytvorenie záznamu budeme musieť použiť funkciu fopen(), kde ako prvý parameter nastavíme cestu k súboru a ako druhý nastavíme spôsob, ako chceme so súborom pracovať. Modifikátor a znamená, že súbor otvoríme iba pre zápis a kurzor sa posunie na koniec súboru. Zoznam všetkých modifikátorov pre prácu so súbormi nájdete na oficiálnej dokumentácii.

Aby sa nestalo, že počas zápisu sa v danom súbore zapíše aj niečo iné, uzamkneme si dočasne tento súbor iba pre našu potrebu, zvyšok počká až kým nedopíšeme záznam. Na to nám slúži funkcia flock(). Po zápise môžeme odomknúť náš súbor rovnakou funkciou, len iným parametrom a ukončiť spojenie s daným súborom pomocou funkcie fclose(). Potom už len vrátime true alebo false podľa toho, či sa vyskytla alebo nevyskytla chyba počas zápisu:

      $messages_file = fopen('guestbook_msgs/messages.txt', 'a'); // vyberieme súbor
      
      flock($messages_file, LOCK_EN); // uazmekneme súbor tak, aby sa počas nášho zásahu nemohol zmeniť obsah
      
      // napíšeme nový riadok do súboru, každý záznam oddeľujeme tagom <gb>
      $write = fwrite($messages_file, $id . '<gb>' . htmlspecialchars($date) . '<gb>' . htmlspecialchars($author) . '<gb>' . htmlspecialchars($email) . '<gb>' . htmlspecialchars($ip) . '<gb>' . ereg_replace("(\r\n|\n|\r)", "<br>", htmlspecialchars($message)) . "\n");
      
      flock($messages_file, LOCK_UN); // po zápise môžeme súbor odomknúť
      fclose($message_file); // uzavrieme spojenie s daným súborom

Kód ereg_replace("(\r\n|\n|\r)", "<br>", htmlspecialchars($message)) hovorí, že nám všetky nové riadky premenení na znak <br>. Nemohli by sme použiť funkciu nl2br(), keďže tá nam tieto zmeny aj vykoná, ale taktiež ponechá nové riadky čo by v našom prípade znamenalo, že by správa na N riadkov bolo neúplných N nových správ, keďže každý riadok tvorí samostatnú správu.

Celá metóda by vyzerala takto:

    // metóda na uloženie správy
    public function addMessage($author, $email, $message)
    {
      $date = time(); // čas odoslaného príspevku
      $id = md5(uniqid(time())); // unikátne ID príspevku
      $ip = $_SERVER['REMOTE_ADDR']; // IP klienta
      
      $messages_file = fopen('guestbook_msgs/messages.txt', 'a'); // vyberieme súbor
      
      flock($messages_file, LOCK_EN); // uazmekneme súbor tak, aby sa počas nášho zásahu nemohol zmeniť obsah
      
      // napíšeme nový riadok do súboru, každý záznam oddeľujeme tagom <gb>
      $write = fwrite($messages_file, $id . '<gb>' . htmlspecialchars($date) . '<gb>' . htmlspecialchars($author) . '<gb>' . htmlspecialchars($email) . '<gb>' . htmlspecialchars($ip) . '<gb>' . ereg_replace("(\r\n|\n|\r)", "<br>", htmlspecialchars($message)) . "\n");
      
      flock($messages_file, LOCK_UN); // po zápise môžeme súbor odomknúť
      fclose($message_file); // uzavrieme spojenie s daným súborom
    }

Teraz nám prichádza na rad metóda aj na vymazanie správy. Čo by to bol za guestbook, keby nebol schopný vymazať správy jednoducho. Vytvoríme si metódu deleteMessage(), kde nám ako parameter bude stačiť $id, teda unikátne číslo príspevku:

    // metóda na vymazanie správy
    public function deleteMessage($id)
    {
      //
    }

Výmaz vykonáme následovne: otvoríme si súbor cez funkciu file(), dostaneme to v tvare array a cez foreach prejdeme každý riadok. Riadky si rozdelíme vďaka tagu <gb> a vyberieme prvý záznam, čo je id a porovnáme ho s parametrom. Ak sa našla zhoda, neuložíme si tento riadok do outputu, len nastavíme hodnotu premennej $deleted na true, čím povieme, že sa našla zhoda:

      $lines = file('guestbook_msgs/messages.txt');
      
      $deleted = false; // nastavíme si premennú $deleted na false ako východziu hodnotu
      
      // prejdeme každý riadok
      foreach($lines as $line)
      {
        $line_arr = explode('<gb>', $line); // rozdelíme si riadok do poľa vďaka tagu <gb>
        if($line_arr[0] != $id)
          $output[] = $line; // ak sa prvá časť (index 0) nerovná našemu idčku, uložíme si to do outputu
        else
          $deleted = true; // ak sa rovná, riadok si do outputu neuložíme a zmeníme hodnotu $deleted na true
      }

Output už máme pripravený a je potreba nám ho vložiť do toho istého súboru. Teraz si kvôli zápisu otvoríme súbor opäť cez funkciu fopen() a teraz nastavíme modifikátor úpravy na w+, čo znamená, že nám celý súbor vymaže a posunie kurzor na začiatok. Potom budeme cez foreach prechádzať premennú $output, ktorá je typu array a každý riadok do tohto súboru vložíme. Vložia sa len tie riadky, ktoré neobsahovali hľadané id:

      // vyberieme si opäť súbor na zápis a vyprázdnime ho
      $messages_file = fopen('guestbook_msgs/messages.txt', 'w+');
      
      flock($messages_file, LOCK_EN); // opäť si uzamkneme súbor kvôli zápisu
      
      foreach($output as $line)
      {
        fwrite($messages_file, $line); // zapíšeme celý náš output do prázdneho súboru
      }
      
      flock($messages_file, LOCK_UN); // odomkneme súbor
      fclose($message_file); // uzatvoríme spojenie so súborom
      
      return $deleted; // vrátime boolean - či sa výmaz vykonal alebo nie

Celý tvar:

    // metóda na vymazanie správy
    public function deleteMessage($id)
    {
      $lines = file('guestbook_msgs/messages.txt');
      
      $deleted = false; // nastavíme si premennú $deleted na false ako východziu hodnotu
      
      // prejdeme každý riadok
      foreach($lines as $line)
      {
        $line_arr = explode('<gb>', $line); // rozdelíme si riadok do poľa vďaka tagu <gb>
        if($line_arr[0] != $id)
          $output[] = $line; // ak sa prvá časť (index 0) nerovná našemu idčku, uložíme si to do outputu
        else
          $deleted = true; // ak sa rovná, riadok si do outputu neuložíme a zmeníme hodnotu $deleted na true
      }
      
      // vyberieme si opäť súbor na zápis a vyprázdnime ho
      $messages_file = fopen('guestbook_msgs/messages.txt', 'w+');
      
      flock($messages_file, LOCK_EN); // opäť si uzamkneme súbor kvôli zápisu
      
      foreach($output as $line)
      {
        fwrite($messages_file, $line); // zapíšeme celý náš output do prázdneho súboru
      }
      
      flock($messages_file, LOCK_UN); // odomkneme súbor
      fclose($message_file); // uzatvoríme spojenie so súborom
      
      return $deleted; // vrátime boolean - či sa výmaz vykonal alebo nie
    }

A ostala nám už posledná metóda a to na vypísanie správ. Nazvyme si ju displayArray(), to Array tam je preto, keďže nevráti output v podobe HTML kódu, ale v tvare poľa. Čo je určite lepšia voľba, keďže sa o spracovanie údajov nestaráme v modeli guestbook, ale až v indexe. Budeme očakávať parameter $page, ktorý zastupuje číslo strany našich správ. Začiatok je samozejmý:

    // metóda na vrátenie output-u správ podľa stránky
    public function displayArray($page = 1)
    {
      // ...
    }

Teraz si načítame naše príspevky do takého tvaru, že budeme ignorovať prázdne riadky ak sú a posledný čistý riadok. Každý riadok sa uloží do poľa a my to pole budeme chcieť prevrátiť, lebo chceme aby sa najnovšie príspevky zobrazovali ako tie prvé:

     // načítame si súbor, budeme ignorovať prázdne riadky
      $file = file('guestbook_msgs/messages.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES | FILE_TEXT);
      $file = array_reverse($file); // obratíme pole, nové správy chceme ako prvé

Teraz si uložíme počet riadkov, resp. počet príspevkov do vlastnosti $lines a taktiež si pre zobrazenie tých správnych výsledkov uložíme začiatočnú pozíciu. Aby nenastala chyba pri vypísaní tých správnych záznamov, vykoná sa menšia ochrana, ktorá overí, či pri súčine našich štartovacej pozície a počtu príspevkov na stranu neprekročí náhodou počet všetkých. Ak áno, je potrebné upraviť začiatočnú pozíciu:

      $this->lines = count($file); // uložíme si celkový počet správ

      $numPages = ceil($this->lines / $this->per_page); // vypočítame si, na koľko strán by nás to vyšlo 
      
      $start = max(1, ctype_digit((string) $page) ? $page : 1) - 1; // určime si začiatok, ak ho nemáme, začneme od 1
      
      $numPosts = min($this->per_page, max(1, $this->per_page)); // určíme si na koľko nás vyšlo počet príspevkov na danej strane
      if ($start * $numPosts > $this->lines ) {
        $start = max(0, $this->lines - $numPosts);
      } else {
        $start *= $numPosts;
      }

Zostáva nám už len si pole oddeliť pomocou funkcie array_slice(), kde si určíme odkiaľ až pokiaľ sa nám to pole usekne. Potom už len prejdeme to naše nové pole a uložíme si do poľa $output ďalšie pole, ktoré bude obsahovať všetky potrebné informácie o našom príspevku v čitateľnej podobe. Nakoniec output vrátime:

      // nakoniec si naše príspevky rozdelíme -> získame len tie, ktoré potrebujeme získať
      $sliced = array_slice($file, $start, $numPosts);     
      
      // pripravujeme output
      for ($n = 0; $n < $numPosts && isset($sliced[$n]); $n++ )
      {
        $line = explode('<gb>', $sliced[$n]); // opäť si rozdelíme riadky vďaka tagu <gb>
        // tvoríme dvojrozmerné pole s čitateľnými údajmi
        $output[] = Array('id' => $line[0], 'date' => $line[1], 'author' => $line[2], 'email' => $line[3], 'ip' => $line[4], 'message' => $line[5]);
      }
      
      return $output; // vrátime output

Celá trieda má potom nasledujúci kód:

<?php

class Guestbook
{
    const ADMIN_PASS = '56ac2821246eb2d132df130db19aa9da25850fd3'; // tajneheslo
    public $per_page = 5; // počet príspevkov na stranu
    public $lines = 0; // celkový počet príspekov
    
    // metóda na uloženie správy
    public function addMessage($author, $email, $message)
    {
      $date = time(); // čas odoslaného príspevku
      $id = md5(uniqid(time())); // unikátne ID príspevku
      $ip = $_SERVER['REMOTE_ADDR']; // IP klienta
      
      $messages_file = fopen('guestbook_msgs/messages.txt', 'a'); // vyberieme súbor
      
      flock($messages_file, LOCK_EN); // uazmekneme súbor tak, aby sa počas nášho zásahu nemohol zmeniť obsah
      
      // napíšeme nový riadok do súboru, každý záznam oddeľujeme tagom <gb>
      $write = fwrite($messages_file, $id . '<gb>' . htmlspecialchars($date) . '<gb>' . htmlspecialchars($author) . '<gb>' . htmlspecialchars($email) . '<gb>' . htmlspecialchars($ip) . '<gb>' . ereg_replace("(\r\n|\n|\r)", "<br>", htmlspecialchars($message)) . "\n");
      
      flock($messages_file, LOCK_UN); // po zápise môžeme súbor odomknúť
      fclose($message_file); // uzavrieme spojenie s daným súborom
    }
    
    // metóda na vymazanie správy
    public function deleteMessage($id)
    {
      $lines = file('guestbook_msgs/messages.txt');
      
      $deleted = false; // nastavíme si premennú $deleted na false ako východziu hodnotu
      
      // prejdeme každý riadok
      foreach($lines as $line)
      {
        $line_arr = explode('<gb>', $line); // rozdelíme si riadok do poľa vďaka tagu <gb>
        if($line_arr[0] != $id)
          $output[] = $line; // ak sa prvá časť (index 0) nerovná našemu idčku, uložíme si to do outputu
        else
          $deleted = true; // ak sa rovná, riadok si do outputu neuložíme a zmeníme hodnotu $deleted na true
      }
      
      // vyberieme si opäť súbor na zápis a vyprázdnime ho
      $messages_file = fopen('guestbook_msgs/messages.txt', 'w+');
      
      flock($messages_file, LOCK_EN); // opäť si uzamkneme súbor kvôli zápisu
      
      foreach($output as $line)
      {
        fwrite($messages_file, $line); // zapíšeme celý náš output do prázdneho súboru
      }
      
      flock($messages_file, LOCK_UN); // odomkneme súbor
      fclose($message_file); // uzatvoríme spojenie so súborom
      
      return $deleted; // vrátime boolean - či sa výmaz vykonal alebo nie
    }
    
    // metóda na vrátenie output-u správ podľa stránky
    public function displayArray($page = 1)
    {
      // načítame si súbor, budeme ignorovať prázdne riadky
      $file = file('guestbook_msgs/messages.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES | FILE_TEXT);
      $file = array_reverse($file); // obratíme pole, nové správy chceme ako prvé
      
      $this->lines = count($file); // uložíme si celkový počet správ

      $numPages = ceil($this->lines / $this->per_page); // vypočítame si, na koľko strán by nás to vyšlo 
      
      $start = max(1, ctype_digit((string) $page) ? $page : 1) - 1; // určime si začiatok, ak ho nemáme, začneme od 1
      
      $numPosts = min($this->per_page, max(1, $this->per_page)); // určíme si na koľko nás vyšlo počet príspevkov na danej strane
      if ($start * $numPosts > $this->lines ) {
        $start = max(0, $this->lines - $numPosts);
      } else {
        $start *= $numPosts;
      }
      
      // nakoniec si naše príspevky rozdelíme -> získame len tie, ktoré potrebujeme získať
      $sliced = array_slice($file, $start, $numPosts);     
      
      // pripravujeme output
      for ($n = 0; $n < $numPosts && isset($sliced[$n]); $n++ )
      {
        $line = explode('<gb>', $sliced[$n]); // opäť si rozdelíme riadky vďaka tagu <gb>
        // tvoríme dvojrozmerné pole s čitateľnými údajmi
        $output[] = Array('id' => $line[0], 'date' => $line[1], 'author' => $line[2], 'email' => $line[3], 'ip' => $line[4], 'message' => $line[5]);
      }
      
      return $output; // vrátime output
    }
}

Môžeme si už vytvoriť zložku guestbook_msgs a v nej prázdny súbor messages.txt. Aby sa k tomuto súboru nikto zvonka nedostal, do danej zložky si vytvoríme ešte súbor .htaccess a vložíme mu nasledujúci obsah:

deny from all

class Pagination (classes/pagination.class.php)

Pre uľahčenie príkladu použijem už open-source triedu na stránkovanie akýchkoľvek záznamov. Keďže sa aj venujeme knihe návštev, pri vysvetľovaní triedy by to bol samotný návod ako na stránkovanie, čo ale teraz nechceme. Túto triedu mám z portálu Mis Algoritmos. Trieda vyzera následovne:

<?php

class Pagination
{
	/*
	 Script Name: *Digg Style Paginator Class
	 Script URI: http://www.mis-algoritmos.com/2007/05/27/digg-style-pagination-class/
	 Description: Class in PHP that allows to use a pagination like a digg or sabrosus style.
	 Script Version: 0.4
	 Author: Victor De la Rocha
	 Author URI: http://www.mis-algoritmos.com
	 */

	public $total_pages = -1;// items
	public $limit = null;
	public $target = "";
	public $page = 1;
	public $adjacents = 2;
	public $showCounter = false;
	public $className = "pagination";
	public $parameterName = "page";
	public $urlF = false;// urlFriendly

	/* Buttons next and previous */
	public $nextT = "Next";
	public $nextI = "&#187;"; // &#9658;
	public $prevT = "Previous";
	public $prevI = "&#171;"; // &#9668;

	public $calculate = false;

	// Total items
	public function items($value){$this->total_pages = (int) $value;}

	// how many items to show per page
	public function limit($value){$this->limit = (int) $value;}

	// Page to sent the page value
	public function target($value){$this->target = $value;}

	// Current page
	public function currentPage($value){$this->page = (int) $value;}

	// How many adjacent pages should be shown on each side of the current page?
	public function adjacents($value){$this->adjacents = (int) $value;}

	// show counter?
	public function showCounter($value=""){$this->showCounter=($value===true)?true:false;}

	// to change the class name of the pagination div
	public function changeClass($value=""){$this->className=$value;}

	public function nextLabel($value){$this->nextT = $value;}
	public function nextIcon($value){$this->nextI = $value;}
	public function prevLabel($value){$this->prevT = $value;}
	public function prevIcon($value){$this->prevI = $value;}

	// to change the class name of the pagination div
	public function parameterName($value=""){$this->parameterName=$value;}

	// to change urlFriendly
	public function urlFriendly($value="%"){
		if(eregi('^ *$',$value)){
			$this->urlF=false;
			return false;
		}
		$this->urlF=$value;
	}

	public $pagination;

	public function pagination(){}
	public function show(){
		if(!$this->calculate)
		if($this->calculate())
		echo "<div class=\"$this->className\">$this->pagination</div>\n";
	}
	public function getOutput(){
		if(!$this->calculate)
		if($this->calculate())
		return "<div class=\"$this->className\">$this->pagination</div>\n";
	}
	public function get_pagenum_link($id){
		if(strpos($this->target,'?')===false)
		if($this->urlF)
		return str_replace($this->urlF,$id,$this->target);
		else
		return "$this->target?$this->parameterName=$id";
		else
		return "$this->target&$this->parameterName=$id";
	}

	public function calculate(){
		$this->pagination = "";
		$this->calculate == true;
		$error = false;
		if($this->urlF and $this->urlF != '%' and strpos($this->target,$this->urlF)===false){
			// Es necesario especificar el comodin para sustituir
			echo "Especificaste un wildcard para sustituir, pero no existe en el target<br />";
			$error = true;
		}elseif($this->urlF and $this->urlF == '%' and strpos($this->target,$this->urlF)===false){
			echo "Es necesario especificar en el target el comodin % para sustituir el número de página<br />";
			$error = true;
		}

		if($this->total_pages < 0){
			echo "It is necessary to specify the <strong>number of pages</strong> (\$class->items(1000))<br />";
			$error = true;
		}
		if($this->limit == null){
			echo "It is necessary to specify the <strong>limit of items</strong> to show per page (\$class->limit(10))<br />";
			$error = true;
		}
		if($error)return false;

		$n = trim($this->nextT.' '.$this->nextI);
		$p = trim($this->prevI.' '.$this->prevT);

		/* Setup vars for query. */
		if($this->page)
		$start = ($this->page - 1) * $this->limit;             // first item to display on this page
		else
		$start = 0;                                // if no page var is given, set start to 0
			
		/* Setup page vars for display. */
		$prev = $this->page - 1;                            // previous page is page - 1
		$next = $this->page + 1;                            // next page is page + 1
		$lastpage = ceil($this->total_pages/$this->limit);        // lastpage is = total pages / items per page, rounded up.
		$lpm1 = $lastpage - 1;                        // last page minus 1

		/*
		 Now we apply our rules and draw the pagination object.
		 We're actually saving the code to a variable in case we want to draw it more than once.
		 */

		if($lastpage > 1){
			if($this->page){
				// anterior button
				if($this->page > 1)
				$this->pagination .= "<a href=\"".$this->get_pagenum_link($prev)."\" class=\"prev\">$p</a>";
				else
				$this->pagination .= "<span class=\"disabled\">$p</span>";
			}
			// pages
			if ($lastpage < 7 + ($this->adjacents * 2)){// not enough pages to bother breaking it up
				for ($counter = 1; $counter <= $lastpage; $counter++){
					if ($counter == $this->page)
					$this->pagination .= "<span class=\"current\">$counter</span>";
					else
					$this->pagination .= "<a href=\"".$this->get_pagenum_link($counter)."\">$counter</a>";
				}
			}
			elseif($lastpage > 5 + ($this->adjacents * 2)){// enough pages to hide some
				// close to beginning; only hide later pages
				if($this->page < 1 + ($this->adjacents * 2)){
					for ($counter = 1; $counter < 4 + ($this->adjacents * 2); $counter++){
						if ($counter == $this->page)
						$this->pagination .= "<span class=\"current\">$counter</span>";
						else
						$this->pagination .= "<a href=\"".$this->get_pagenum_link($counter)."\">$counter</a>";
					}
					$this->pagination .= "...";
					$this->pagination .= "<a href=\"".$this->get_pagenum_link($lpm1)."\">$lpm1</a>";
					$this->pagination .= "<a href=\"".$this->get_pagenum_link($lastpage)."\">$lastpage</a>";
				}
				// in middle; hide some front and some back
				elseif($lastpage - ($this->adjacents * 2) > $this->page && $this->page > ($this->adjacents * 2)){
					$this->pagination .= "<a href=\"".$this->get_pagenum_link(1)."\">1</a>";
					$this->pagination .= "<a href=\"".$this->get_pagenum_link(2)."\">2</a>";
					$this->pagination .= "...";
					for ($counter = $this->page - $this->adjacents; $counter <= $this->page + $this->adjacents; $counter++)
					if ($counter == $this->page)
					$this->pagination .= "<span class=\"current\">$counter</span>";
					else
					$this->pagination .= "<a href=\"".$this->get_pagenum_link($counter)."\">$counter</a>";
					$this->pagination .= "...";
					$this->pagination .= "<a href=\"".$this->get_pagenum_link($lpm1)."\">$lpm1</a>";
					$this->pagination .= "<a href=\"".$this->get_pagenum_link($lastpage)."\">$lastpage</a>";
				}
				// close to end; only hide early pages
				else{
					$this->pagination .= "<a href=\"".$this->get_pagenum_link(1)."\">1</a>";
					$this->pagination .= "<a href=\"".$this->get_pagenum_link(2)."\">2</a>";
					$this->pagination .= "...";
					for ($counter = $lastpage - (2 + ($this->adjacents * 2)); $counter <= $lastpage; $counter++)
					if ($counter == $this->page)
					$this->pagination .= "<span class=\"current\">$counter</span>";
					else
					$this->pagination .= "<a href=\"".$this->get_pagenum_link($counter)."\">$counter</a>";
				}
			}
			if($this->page){
				// siguiente button
				if ($this->page < $counter - 1)
				$this->pagination .= "<a href=\"".$this->get_pagenum_link($next)."\" class=\"next\">$n</a>";
				else
				$this->pagination .= "<span class=\"disabled\">$n</span>";
				if($this->showCounter)$this->pagination .= "<div class=\"pagination_data\">($this->total_pages Pages)</div>";
			}
		}

		return true;
	}
}

Viac k tejto triede vysvetľovať nie je potreba, keďže v tomto návode sa kladie priorita na knihu návštev.

autoloader.php

Autoloader sa z minulých návodov nejak nezmenil, načítavame triedy v zložke classes a názvy súborov su malými písmenami:

<?php

// vytvoríme si vlastný autoloader
function __autoload($nazov_triedy)
{
    require_once('classes/' . strtolower($nazov_triedy) . '.class.php');
}

index.php

Ako posledný zo všetkých súborov nám už ostal len index. Pre začiatok si vložíme autoloader, ale ešte predtým povieme, že budeme v tomto skripte pracovať so sessions tým, že vyvoláme funkciu session_start(). Pracovať so sessions budeme preto, lebo sa budeme musieť nejak autorizovať, aby sme mohli mazať správy, poprípade vidieť ostatné údaje ako e-mail a IP adresu:

<?php

session_start(); // aktivujeme sessions

require_once('autoloader.php'); // načítame si autoloader

Potom si načítamie naše inštancie tried a premenné $error_message a $success_message, ktoré budú obsahovať informačné správy nastavíme na null:

// vytvoríme si inštancie tried
$guestbook = new Guestbook();
$pagination = new Pagination();

// nastavíme si naše flash správy zatiaľ na prázdne
$success_message = null;
$error_message = null;

Teraz si môžeme spracovať požiadavku, ak sa klient snaží autorizovať. Autorizuje sa cez URL adresu (čo nie je práve najlepšia voľba, ale to už bude úloha pre vás) a overíme správnosť hesla s konštantou ADMIN_PASS u triedy Guestbook:

// požiadavka na prihlasovanie
if(isSet($_GET['login']) && $_GET['login'])
{
  if(sha1($_GET['login']) == Guestbook::ADMIN_PASS)
  {
    // užívateľ bol úspešne autorizovaný
    $success_message = 'Boli ste úspešne autorizovaný';
    
    $_SESSION['authenticated'] = 1;
    session_regenerate_id(); // vynegerujeme nové session id
  }
  else
    $error_message = 'Nepovolený prístup';
}

Toto ako jediná ochrana určite nestačí. Vysvetlili sme si to v návode tvoríme si bezpečnejšie prihlasovanie v OOP, takže odporúčam, aby ste si potom danú aplikáciu v tomto smere o tieto kroky doplnili. Samozrejme aj využitie CSRF tokenu.

Keď už sme pri autorizovaní, môžeme spracovať požiadavku na odstránenie správy. Na to musíme overiť, či je autorizovaný a ak áno, môžeme sa pokúsiť správu odstrániť. Ak pri vymazaní dostaneme výsledok false, daná správa pod týmto identifikačným číslom neexistuje:

// požiadavka na vymazanie správy
if(isSet($_GET['delete']) && $_GET['delete'])
{
  // overíme, či je užívateľ autorizovaný
  if(isSet($_SESSION['authenticated']) && $_SESSION['authenticated'] == 1)
    if($guestbook->deleteMessage($_GET['delete']))
      $success_message = 'Správa bola úspešne vymazaná'; // výsledok je true, správa sa vymazala
    else
      $error_message = 'Daná správa neexiste'; // výsledok false, správa neexistuje
  else
    $error_message = 'Nepovolený prístup'; // nie je autorizovaný
}

A ako posledná požiadavka je spracovanie na odoslanie novej správy. Tu budeme mať všetky odoslané dáta ako meno, e-mail, správa ale aj ochrana proti botom. Ochranu proti botom si ukážeme troška neskôr, teraz nám ale bude stačiť, že máme očakávať hodnotu superantispam a ak sa daná hodnota nerovná, formulár bude neplatný. Aby sa aj používateľ príliš nepreklikal, tak k danej ocrahne si pridáme jednoduchú captchu. Vygenerujeme si 4 náhodné znaky a overíme, či boli zadané správne. Pri ošetrovaní premenných si aj ošetríme, či meno nie je kratšie ako 2 znaky, e-mail musí byť validný a správa nesmie byť kratšia ako 5 znakov. Ak všetko sedí, môžeme správu odoslať. Na validáciu e-mailu použijeme funkciu filter_var(), kde ako druhý parameter nastavíme FILTER_VALIDATE_EMAIL:

// požiadavka na odoslanie správy
if(isSet($_POST['submit_message']) && $_POST['submit_message'])
{
  // spracujeme si odoslané dáta
  $author = trim($_POST['author']);
  $email = trim($_POST['email']);
  $message = trim($_POST['message']);
  $antibot = trim($_POST['antibot']); // ochrana proti botom
  $captcha = trim($_POST['captcha']); // ochrana proti botom a spamu
  
  // ošetríme si premenné
  
  if(strlen($author) < 2)
    $error_message = 'Meno musí mať dĺžku min. 2 znaky';
    
  if(!filter_var($email, FILTER_VALIDATE_EMAIL))
    $error_message = 'E-mail nie je validný';
    
  if(strlen($message) < 5)
    $error_message = 'Správa musí obsahovať min. 5 znakov';
    
  if($antibot != 'superantibot')
    $error_message = 'Ochrana proti botom zakázala odoslať tento príspevok. V prípade, že nieste BOT si musíte aktivovať JavaScript';
    
  if($captcha != $_SESSION['captcha_code'])
    $error_message = 'Kód z obrázka nebol opísaný správne';
  
  // ak doteraz nebola žiadna chybová správa, môžeme zapísať našu správu  
  if(!$error_message)
  {
    if($guestbook->addMessage($author, $email, $message)) {
      $success_message = 'Správa bola úspešne odoslaná'; // metóda addMessage nám vratila true
      unset($_POST); // vymažeme si $_POST údaje, aby sa nám už vo formulári nezobrazovali 
    }
    else
      $error_message = 'Nepodarilo sa správu odoslať. Akciu prosím zopakujte'; // nastala chyba
  }
}

Už nám len zostáva si vygenerovať output naších správ vzhľadom na akej strane sme a samozrejme nesmie chýbať aj nastavenie si stránkovania:

// určíme si súčasnú stranu
$page = (isSet($_GET['page']) && $_GET['page']) ? (int) $_GET['page'] : 1;
$messages = $guestbook->displayArray($page); // získame output správ v závislosti na akej strane sme

// pripravíme si stránkovanie
$pagination->items($guestbook->lines); // určíme celkový počet záznamov
$pagination->limit($guestbook->per_page); // nastavíme si záznamy na stranu
$pagination->adjacents(3); // rozsah stránkovania - 3 zľava a 3 zprava.
$pagination->currentPage($page); // nastavíme súčasnú stranu
$pagination->nextLabel('Ďalej'); // popis textu na následujúcu stranu
$pagination->prevLabel('Späť'); // popis textu na predchádzajúcu stranu

Môžeme sa pustiť do vypísovania obsahu. Na začiatok si pripravíme HTML kód a nastavíme nejaké kaskadové štýly, aby tie správy nejakej vyzerali a samozrejme aj naše stránkovanie:

<html>
<head>
<title>Guestbook</title>
<meta name="robots" content="noindex, nofollow">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style type="text/css">
#error_message {
  display: block;
  border: 1px solid red;
  padding: 15px;
  margin: 5px 0;
}

#success_message {
  display: block;
  border: 1px solid green;
  padding: 15px;
  margin: 5px 0;
}

.message {
  display: block;
  border: 1px solid black;
  margin: 10px 0;
  padding: 5px;
}

.actions {
  float: right;
}

.pagination {
  display: block;
  margin: 20px 0;
}

.pagination span, .pagination a {
  padding: 5px;
  margin: 0 5px;
}

.pagination span.disabled {
  border: 1px solid gray;
  color: gray;
}

.pagination span.current {
  border: 1px solid gray;
  color: gray;
}

.pagination a {
  border: 1px solid blue;
  color: blue;
  text-decoration: none;
}

.pagination a:hover {
  text-decoration: none;
  border: 1px solid #3694FF;
  color: #3694FF;
}

input#antibot {
  display: none;
}
</style>
</head>

Teraz sa môžeme pustiť do tela. V tele sa budú nachádzať informáčne správy (error a success) a zatiaľ si vygenerujeme aj náš formulár pre odoslanie správy. Taktiež sa nám tu bude nachádzať skryté políčko antibot:

<body>
<?php if($error_message): ?>
<div id="error_message">
 <?= $error_message; ?>
<?php endif; ?>
</div>
<fieldset>
  <legend>Odoslať správu</legend>
  <form action="" method="post">
  <table style="border: 0;">
    <tr>
      <td><label for="author">Meno:</td>
      <td><input type="text" name="author" id="author" value="<?= $_POST['author']; ?>"></td>
    </tr>
    <tr>
      <td><label for="email">E-mail:</td>
      <td><input type="text" name="email" id="email" value="<?= $_POST['email']; ?>"></td>
    </tr>
    <tr>
      <td><label for="message">Správa:</td>
      <td><textarea name="message" id="message" cols="40" rows="7"><?= $_POST['message']; ?></textarea></td>
    </tr>
    <tr>
      <td><label for="captcha">Captcha:</td>
      <td><img src="captcha.php" alt="captcha" style="position: relative; top: 6px;"> <input type="text" name="captcha" id="captcha" value="" style="width: 50px;" maxlength="4"></td>
    </tr>
    <tr>
      <td>&nbsp;</td>
      <td><input type="submit" name="submit_message" value="Odoslať"></td>
    </tr>
   </table>
   <input type="text" name="antibot" disabled="disabled" id="antibot">
   </form>
</fieldset>
<h2>Správy:</h2>
<?php if($success_message): ?>
<div id="success_message">
 <?= $success_message; ?>
<?php endif; ?>
</div>

Súbor captcha.php si ochvíľku ukážeme, ako to v ňom funguje, že nám vygeneruje obrázok a aj uloží do SESSIONS. V tejto chvíli si dokončíme spracovanie našich správ a vypísania stránkovania:

<?php
foreach($messages as $message):
?>
<div class="message">
  <p>
  Napísal <?= $message['author']; ?> o <?= date('d.n.Y H:i:s', $message['date']); ?>
  <?php if(isSet($_SESSION['authenticated']) && $_SESSION['authenticated'] == 1): ?> (<?= $message['email']; ?>) | IP: <?= $message['ip']; ?> <span class="actions"><a href="?delete=<?= $message['id']; ?>">X</a></span><?php endif; ?>
  </p>
  <p><?= $message['message']; ?></p>
</div>
<?php endforeach; ?>
<?= $pagination->show() ?>

Cyklom foreach sme prechádzali správu po správe. Vypísali sme autora a taktiež aj dátum a čas odoslanej správy do našej čitateľnej podoby (zrejme by nám veľmi nepomohlo vidieť čas a dátum odoslanej správy v sekundách od roku 1920 tuším). Potom sme podmienkou

if(isSet($_SESSION['authenticated']) && $_SESSION['authenticated'] == 1)

určili, či daný používateľ je autorizovaný a ak áno, zobrazí sa e-mailová adresa a IP adresa odosielateľa a taktiež možnosť vymazať správu. Potom sme už zobrazli obsah danej správy. Ukončili sme cyklus príkazom endforeach; a vypísali stránkovania pomocou $pagination->show().

Na záver tohto súboru tu máme JavaScript kód, ktorý disabled políčko antibot nastaví na hodnotu false (nie je vypnuté) a taktiež nastaví hodnotu tohto políčka na superantibot. Potom už len ukončíme telo a html syntax:

<script type="text/javascript">
var antibot = document.getElementById("antibot");
antibot.disabled = false;
antibot.value = "superantibot";
</script>
</body>
</html>

Celý kód tohto súboru je následovný:

<?php

session_start(); // aktivujeme sessions

require_once('autoloader.php'); // načítame si autoloader

// vytvoríme si inštancie tried
$guestbook = new Guestbook();
$pagination = new Pagination();

// nastavíme si naše flash správy zatiaľ na prázdne
$success_message = null;
$error_message = null;

// požiadavka na prihlasovanie
if(isSet($_GET['login']) && $_GET['login'])
{
  if(sha1($_GET['login']) == Guestbook::ADMIN_PASS)
  {
    // užívateľ bol úspešne autorizovaný
    $success_message = 'Boli ste úspešne autorizovaný';
    
    $_SESSION['authenticated'] = 1;
    session_regenerate_id(); // vynegerujeme nové session id
  }
  else
    $error_message = 'Nepovolený prístup';
}

// požiadavka na vymazanie správy
if(isSet($_GET['delete']) && $_GET['delete'])
{
  // overíme, či je užívateľ autorizovaný
  if(isSet($_SESSION['authenticated']) && $_SESSION['authenticated'] == 1)
    if($guestbook->deleteMessage($_GET['delete']))
      $success_message = 'Správa bola úspešne vymazaná'; // výsledok je true, správa sa vymazala
    else
      $error_message = 'Daná správa neexiste'; // výsledok false, správa neexistuje
  else
    $error_message = 'Nepovolený prístup'; // nie je autorizovaný
}

// požiadavka na odoslanie správy
if(isSet($_POST['submit_message']) && $_POST['submit_message'])
{
  // spracujeme si odoslané dáta
  $author = trim($_POST['author']);
  $email = trim($_POST['email']);
  $message = trim($_POST['message']);
  $antibot = trim($_POST['antibot']); // ochrana proti botom
  $captcha = trim($_POST['captcha']); // ochrana proti botom a spamu
  
  // ošetríme si premenné
  
  if(strlen($author) < 2)
    $error_message = 'Meno musí mať dĺžku min. 2 znaky';
    
  if(!filter_var($email, FILTER_VALIDATE_EMAIL))
    $error_message = 'E-mail nie je validný';
    
  if(strlen($message) < 5)
    $error_message = 'Správa musí obsahovať min. 5 znakov';
    
  if($antibot != 'superantibot')
    $error_message = 'Ochrana proti botom zakázala odoslať tento príspevok. V prípade, že nieste BOT si musíte aktivovať JavaScript';
    
  if($captcha != $_SESSION['captcha_code'])
    $error_message = 'Kód z obrázka nebol opísaný správne';
  
  // ak doteraz nebola žiadna chybová správa, môžeme zapísať našu správu  
  if(!$error_message)
  {
    if($guestbook->addMessage($author, $email, $message)) {
      $success_message = 'Správa bola úspešne odoslaná'; // metóda addMessage nám vratila true
      unset($_POST); // vymažeme si $_POST údaje, aby sa nám už vo formulári nezobrazovali 
    }
    else
      $error_message = 'Nepodarilo sa správu odoslať. Akciu prosím zopakujte'; // nastala chyba
  }
}

// určíme si súčasnú stranu
$page = (isSet($_GET['page']) && $_GET['page']) ? (int) $_GET['page'] : 1;
$messages = $guestbook->displayArray($page); // získame output správ v závislosti na akej strane sme

// pripravíme si stránkovanie
$pagination->items($guestbook->lines); // určíme celkový počet záznamov
$pagination->limit($guestbook->per_page); // nastavíme si záznamy na stranu
$pagination->adjacents(3); // rozsah stránkovania - 3 zľava a 3 zprava.
$pagination->currentPage($page); // nastavíme súčasnú stranu
$pagination->nextLabel('Ďalej'); // popis textu na následujúcu stranu
$pagination->prevLabel('Späť'); // popis textu na predchádzajúcu stranu

?>
<html>
<head>
<title>Guestbook</title>
<meta name="robots" content="noindex, nofollow">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style type="text/css">
#error_message {
  display: block;
  border: 1px solid red;
  padding: 15px;
  margin: 5px 0;
}

#success_message {
  display: block;
  border: 1px solid green;
  padding: 15px;
  margin: 5px 0;
}

.message {
  display: block;
  border: 1px solid black;
  margin: 10px 0;
  padding: 5px;
}

.actions {
  float: right;
}

.pagination {
  display: block;
  margin: 20px 0;
}

.pagination span, .pagination a {
  padding: 5px;
  margin: 0 5px;
}

.pagination span.disabled {
  border: 1px solid gray;
  color: gray;
}

.pagination span.current {
  border: 1px solid gray;
  color: gray;
}

.pagination a {
  border: 1px solid blue;
  color: blue;
  text-decoration: none;
}

.pagination a:hover {
  text-decoration: none;
  border: 1px solid #3694FF;
  color: #3694FF;
}

input#antibot {
  display: none;
}
</style>
</head>
<body>
<?php if($error_message): ?>
<div id="error_message">
 <?= $error_message; ?>
<?php endif; ?>
</div>
<fieldset>
  <legend>Odoslať správu</legend>
  <form action="" method="post">
  <table style="border: 0;">
    <tr>
      <td><label for="author">Meno:</td>
      <td><input type="text" name="author" id="author" value="<?= $_POST['author']; ?>"></td>
    </tr>
    <tr>
      <td><label for="email">E-mail:</td>
      <td><input type="text" name="email" id="email" value="<?= $_POST['email']; ?>"></td>
    </tr>
    <tr>
      <td><label for="message">Správa:</td>
      <td><textarea name="message" id="message" cols="40" rows="7"><?= $_POST['message']; ?></textarea></td>
    </tr>
    <tr>
      <td><label for="captcha">Captcha:</td>
      <td><img src="captcha.php" alt="captcha" style="position: relative; top: 6px;"> <input type="text" name="captcha" id="captcha" value="" style="width: 50px;" maxlength="4"></td>
    </tr>
    <tr>
      <td>&nbsp;</td>
      <td><input type="submit" name="submit_message" value="Odoslať"></td>
    </tr>
   </table>
   <input type="text" name="antibot" disabled="disabled" id="antibot">
   </form>
</fieldset>
<h2>Správy:</h2>
<?php if($success_message): ?>
<div id="success_message">
 <?= $success_message; ?>
<?php endif; ?>
</div>
<?php
foreach($messages as $message):
?>
<div class="message">
  <p>
  Napísal <?= $message['author']; ?> o <?= date('d.n.Y H:i:s', $message['date']); ?>
  <?php if(isSet($_SESSION['authenticated']) && $_SESSION['authenticated'] == 1): ?> (<?= $message['email']; ?>) | IP: <?= $message['ip']; ?> <span class="actions"><a href="?delete=<?= $message['id']; ?>">X</a></span><?php endif; ?>
  </p>
  <p><?= $message['message']; ?></p>
</div>
<?php endforeach; ?>
<?= $pagination->show() ?>

<script type="text/javascript">
var antibot = document.getElementById("antibot");
antibot.disabled = false;
antibot.value = "superantibot";
</script>
</body>
</html>

Na záver sa ale vraťme k súboru captcha.php. Tento súbor si pripraví 4-miestny kód, ktorý sa získa odrezaním prvých 4 znakoch zo sha1 šifry. Tento kód si uložíme do SESSIONS. Potom sa pripraví obrázok, nastavia rozmery, pripraví sa farba pozadia a písma. Pozadie vyplníme našou nastavenou farbou a vložíme text do obrázka. Na koniec pripravíme hlavičku a vygenerujeme obrázok. Následne ten obrázok môžeme z pamäti vymazať:

<?php
session_start(); // začneme sessions

$captcha_code = substr(sha1(uniqid(time())), 0, 4); // vezmeme si len prvé 4 znaky sha1 šifry
$_SESSION['captcha_code'] = $captcha_code; // uložíme si ich

// vytvoríme si obrázok
$im = imagecreatetruecolor(50, 24);
$bg = imagecolorallocate($im, 22, 86, 165); // pozadie bude svetlomodré
$fg = imagecolorallocate($im, 255, 255, 255); // farba písma biela
imagefill($im, 0, 0, $bg); // vyplníme pozadie
imagestring($im, 5, 5, 5,  $captcha_code, $fg); // vložíme reťazec
header('Cache-Control: no-cache, must-revalidate'); // nebudeme si tento obrázok ukladať do pamäte
header('Content-type: image/png'); // pripravíme si výstup
imagepng($im); // vrátime obrázok
imagedestroy($im); // môžeme ho následne vymazať z pamäti

A to je všetko. Ako aj teraz, môžete si pozrieť demo celého tohto príkladu. Pre autorizovanie je potreba zadať ešte do URL:

?login=tajneheslo

A to je všetko. V prípade nejakých návrhov, názorov alebo chýb nevahájte napísať komentár.

Komentáre

Žiadne komentáre. Budte prvý, kto prispeje.

Odoslať komentár

Posledné Referencie

Deffender User Control Panel Deffender BG & FUN private server Eduard Karpiel - portfólio VideoVizitky.SK Apartmány Maladinovo Lymfoštúdia Eva

O Mne

Mám 22 rokov. Študujem Informatiku v odbore programovanie a taktiež sa venujem tvorbe nových webstránok. Pracujem s jazykmi

  • PHP (OOP) & MySQL
  • HTML & CSS (len drobné zmeny)
  • Symfony2 Framework (začiatočník, ale učím sa rýchlo - viď tento web)

Kontakt

V prípade záujmu alebo nejakého dotazu ma neváhajte kontaktovať na nižšie uvedených možnostiach. Poprípade môžete využiť kontaktný formulár

ICQ: 357-248-017
Skype: lkopo__ (upřednostňujte)
E-mail: