среда, 3. септембар 2014.

Labavo vezane komponente u projektu (Dependensy Injection Pattert via PHP)

Prilikom izrade malo zahtevnijih projekata često raste potreba za kreiranjem novih objekata. Međutim, svakako raste potreba za njihovim međusobnim povezivanjima, kako unutar klase, tako i van nje. 
Samo instanciranje objekata, kao i njihovo međusobno povezivanje ne predstavlja problem ali znatno je veći problem što su te veze čvrste. Samim tim je čvrsta i nemogućnost da se aplikacija kasnije nadogradi ili prilagodi nekim novim potrebama. Stoga je neophodno olabaviti zavisnosti.

Pojavom php verzije 5.2 dobijena je mogućnost  prosleđivanja objekata klasama (Dependency Injection Pattern). Dakle, ukoliko je nekoj klasi potreban neki objekat, nesmetano joj se može proslediti. Time je smanjena odgovornost klase da ga sama kreira. 
Dakle Injection == prosleđivanje objekata.

Jedna od mogućnosti prosleđivanja objekata je preko konstruktora klase. Ovo je i najrasprostranjeniji metod, iako se prosleđivanje može vršiti i pomoću setter metoda.

Predstaviću prost primer koji u praksi demonstrira Dependency Injection. Primer se sastoji iz 3 klase, Correct i Fuel sa svojim metodama, kao i Car koju realizuje DI pattern. 

Car.php
<?php
class Car {
    
    //Definisani property klase Car
    private $__correct;
    private $__fuel;
    private $__ignition;

    //Dependency Injection preko konstruktora klase
    public function __construct(Correct $correct, Fuel $fuel) {
        $this->__correct = $correct;
        $this->__fuel = $fuel;
    }
    
    public function go() {
        
        //Automobil moze jedino krenuti ako je ispravan i ako ima goriva
        if ($this->__correct->isCorrect() && $this->__fuel->have_Fuel()) {
            $this->__ignition = TRUE;
        }
        echo 'HERE WE GO!!!!';
    }
    
    public function stop() {
        
        //Ukoliko je automobil neispravan ili nema goriva, ne moze krenuti
        if ($this->__correct->notCorrect() || $this->__fuel->not_Fuel()) {
            $this->__ignition = FALSE;
        }
        echo 'WE HAVE TO STOP!!!';
    }
}

Correct.php
<?php
class Correct {
    
    private $__is_Correct;
    
    public function isCorrect() {
        $this->__is_Correct = TRUE;
    }
    
    public function notCorrect() {
        $this->__is_Correct = FALSE;
    }
    
    public function correct() {
        return $this->__is_Correct;
    }
}

Fuel.php

<?php
class Fuel {
       
    private $__have_Fuel;
    
    public function have_Fuel() {
        
        $this->__have_Fuel = TRUE;
    }
    
    public function not_Fuel() {
        $this->__have_Fuel = FALSE;
    }
    
    public function fuel() {
        return $this->__have_Fuel;
    }
}

Ostaje još index.php koji daje rezultate korišćenjem DI pattern-a. 

<?php
require 'core/init.php'; //Autoloading

$car = new Car(new Correct, new Fuel);

$car->go();
echo "
"; $car->stop();

Kao i ranije, korišćen je autoloading. Iako nema mnogo klasa, zgodno ga je koristiti, naročito ukoliko se javi potreba za preimenovanjem klasa. 

init.php
<?php

spl_autoload_register(function ($class) {
    
    require_once "classes/{$class}.php";
    
});

Ovaj primer, kao i ostale možete videti i na github.com/vlradovanovic
Pozdrav.
Vladimir Radovanović

уторак, 28. јануар 2014.

Validation helper (ispravnost formi), PHP (OOP), PDO API

Ovaj helper ima zadatak da ukaže na neispravno popunjene forme, odnosno nepopunjene forme. 

Helper ne koristi framework za razliku od ostalih projekata na blogu, ali svakako ima za cilj da demonstrira OO pristup, PDO API kroz prepare() statement i tzv. SQL Injection, sve to naravno kroz PHP. Sam helper nije veliki (koristi samo 3 klase), ali se uz veoma malu doradu uspešno može implementirati u svaki custom framework. Na isti (ili barem sličan) način funkcionišu validation_helper, odnosno validation_library u mnogim radnim okvirima kojima upravlja PHP.

Dakle, u našem slučaju ćemo koristiti  bazu sa jednom tabelom, čisto radi provere da li uneti podatak sa forme već postoji u bazi ili ne. Sve ostalo se izvršava direktno u skruptu, bez komunikacije sa bazom.

Napomena: syntax highlighting na blogu hoće po neki put da "pojede" po koji html tag, tako da će vam se možda index.php učiniti malo čudnim. Kompletan projekat se može videti na github.com/vlradovanovic

Baza se može preuzeti ovde, a izgleda ovako:



-- phpMyAdmin SQL Dump
-- version 4.0.4.1
-- http://www.phpmyadmin.net
--
-- Host: 127.0.0.1
-- Generation Time: Jan 28, 2014 at 01:36 PM
-- Server version: 5.5.32
-- PHP Version: 5.4.19

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;

--
-- Database: `validacija`
--
CREATE DATABASE IF NOT EXISTS `validacija` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci;
USE `validacija`;

-- --------------------------------------------------------

--
-- Table structure for table `korisnici`
--

CREATE TABLE IF NOT EXISTS `korisnici` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `korisnicko_ime` varchar(25) NOT NULL,
  `lozinka` varchar(60) NOT NULL,
  `ime_prezime` varchar(35) NOT NULL,
  `email` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

--
-- Dumping data for table `korisnici`
--

INSERT INTO `korisnici` (`id`, `korisnicko_ime`, `lozinka`, `ime_prezime`, `email`) VALUES
(1, 'admin', 'd033e22ae348aeb5660fc2140aec35850c4da997', 'Vladimir Radovanovic', 'vl.radovanovic@gmail');

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;



Input_class.php -- Proverava da li postoji unos i ako postoji deklariše metode ($_GET, odnosno $_POST):
<?php
class Input_class
{
    //Ukoliko postoji unos, prveri da li je GET ili POST, oba dolaze u obzir
    //Default je 'post'
    
    public static function exist($case = 'post') 
    {
        switch ($case) {
            case 'post':
                
                if (!empty($_POST))
                {
                    return TRUE;
                }
                else                    return FALSE;

                break;
                
                  case 'get':
                
                if (!empty($_GET))
                {
                    return TRUE;
                }
                else                    return FALSE;

                break;

            default:
                break;
        }
    }
    
    public static function input($value) 
    {
        if (isset($_POST[$value]))
        {
            return $_POST[$value];
        }
        
        elseif (isset ($_GET[$value])) {
        
            return $_GET[$value];
    }
    return '';
    }    
}

FormValidation_class.php izgleda ovako:
<?php

class FormValidation_class
{
    //Definisani property
    private $__database = null,
            $__error = array(),
            $__success = false;
    
    public function __construct() {
      $this->__database = DataBase_class::instance();

    }
    
    public function check($input, $fields = array())
    {
        foreach ($fields as $fields => $rules) {
            foreach ($rules as $key => $key_value) {
                $value = $input[$fields]; // Unos sa tastature, polje textbox
                
                if ($key === 'obavezno' && empty($value)) //U slucaju da je polje obavezno a da je unos prazan
                {
                   $this->addError("{$fields} je obavezno uneti."); //Npr: Korisnicko ime je obavezno
                }
                
                elseif (!empty ($value)) {
                    
                    switch ($key) {
                        case 'minimalno':

                           if (strlen($value) < $key_value)
                           {
                               $this->addError("Polje {$fields} mora imati najmanje {$key_value} karaktera.");
                           }
                            break;
                            
                        case 'maksimalno':
                        {
                            
                            if (strlen($value) > $key_value)
                            {
                                $this->addError("Polje {$fields} mora imati najviše {$key_value} karaktera.");
                            }
                        }
                            break;
                            
                        case 'jedinstven': //U slucaju da vec postoji taj username u bazi
                        {
                            
                            $query = $this->__database->query("korisnicko_ime");
                           
                            if ($query->count())
                            {
                               $this->addError("{$fields} postoji u bazi, pokušajte sa nekim drugim.");
                            }
                        }
                        break;
                            
                        case 'isto':
                        {
                            
                            if ($value != $input[$key_value])
                            {
                                $this->addError("Polje {$fields} mora biti isto kao polje lozinka");
                            }
                        }
                        break;
                            
                        case 'ispravno':
                        {
                            
                            if (!preg_match("/([\w\-]+\@[\w\-]+\.[\w\-]+)/", $value))
                                    {
                                        $this->addError("Pogrešan {$key_value} format");
                                    }
                        }
                        break;

                        default:
                            break;
                    }
                
            }
            }
        }
        
        if (empty($this->__error))
        {
            $this->__success = TRUE;
        }
        return $this; //Vraca current objekat
    }
    
    public function success()
    {
        return $this->__success;
    }
    
    public function error()
    {
        return $this->__error;
    }
    
    private function addError($error)
    {
       $this->__error[] = $error;
    }

}

DataBase_class.php treba da izgleda ovako:

<?php
class DataBase_class {
    
    //Definisani property
    
    private static $__instance = null;
    
    private  $__pdo,
            $__error = false,
            $__result,
            $__query,
            $__count = 0;
    
    private function __construct() {
        
    // Napraviti konekciju prema bazi
        
    $hostname = "localhost";
    $user = "root";
    $pass = "";
    $dbname = "validacija";
    
    try {
         $conn = "mysql:host=$hostname;dbname=$dbname";
        
        $this->__pdo = new PDO($conn, $user, $pass);
        
    } 
    
         catch (Exception $exc) {
            die($exc->getMessage());
        }
        }
    
        
        public static function instance()
        {
            //Pomocu instance() komuniciramo sa bazom iz razlicitih klasa
            
            if (!isset(self::$__instance))
            {
                self::$__instance = new DataBase_class();
            }
            
            return self::$__instance;
        }
        
        public function query($value)
        {
            $this->__error = FALSE;
            
  
                    if ($this->__query = $this->__pdo->prepare("SELECT * FROM korisnici WHERE korisnicko_ime = :name"))
                    {
                        
                        $this->__query->bindParam(":name", Input_class::input($value));
                        
                        if ($this->__query->execute())
                        {
                              $this->__result = $this->__query->fetchAll(PDO::FETCH_OBJ);
                              $this->__count = $this->__query->rowCount();
                        }
                    }
                
                else {
                    
                $this->__error = TRUE;

            }
          
            return $this;
}


        public function count()
        {
            return $this->__count;
        }
        
       
        
        public function result()
        {
            return $this->__result;
        }
        
        public function error()
        {
            return $this->__error;
        }
    
}

Na kraju index.php izgleda ovako
<?php
spl_autoload_register(function($class) //Biblioteka koja ucitava sve klase projekta
{
    require_once 'class/'.$class.'.php';
}
    );
    
  if (Input_class::exist())
  {
      $formvalidation = new FormValidation_class();
      
      $validate = $formvalidation->check($_POST, array(
          
          'korisnicko_ime' => array(
              'obavezno' => TRUE,
              'minimalno' => 4,
              'maksimalno' => 20,
              'jedinstven' => 'korisnici'
          ),
          
              'lozinka' => array(
              'obavezno' => TRUE,
              'minimalno' => 5,
              'maksimalno' => 25,
      ),
          'ponovo_lozinka' => array(
              
              'obavezno' => TRUE,
              'isto' => 'lozinka' 
          ),
          
          'email' => array(
              'obavezno' => TRUE,
              'ispravno' => 'email'
              
          )));
      
      if ($validate->success())
      {
          ?>


    
        
        Registracija korisnika
        
    
    
        
        
<?php echo 'Validacija uspešno prošla'; ?>
" Polja označena sa * su obavezna!!! "
<?php } else { ?> Registracija korisnika
<?php foreach ($validate->error() as $error) { echo $error, ' '; } ?>
" Polja označena sa * su obavezna!!! "
<?php } } else { ?> Registracija korisnika
" Polja označena sa * su obavezna!!! "
<?php } ?>
Kompletan projekat se može preuzeti ovde.

Srdačan pozdrav.
Vladimir