PHP

Basic State Controller for a checkout

<?php
/**
 * 
 * @ This class allows us to achieve stateful routing
 *   It only tracks current, completed and previous steps
 *   If the user presses the back button this will do nothing.
 *   
 *   An example of it's usage is in the summary page:
 *    //-- You press edit on the address
 *    //-- This script will hold the information of the previous step (summary)
 *    //-- When you enter a new address or select another it understands that
 *         the summary page is where you need to go and will place you there.
 *
 */

class checkout_state_controller{

	///* Previous stage
	protected $previous;
	
	///* Current stage
	protected $current;
	
	///* All the stages in an array
	protected $stages = array();
	
	///* Singleton instance of this class
	private static $instance;

    /**
     * Shopping basket stages
     * 
     * @ This function returns an array of the stages of the shopping basket.
     *   It allows this class to make a descision about which page to redirect to.
     *   Make sure these are in order.
     * 
     * @params void
     * @return Array $stages
     */
	public function stages(){
		return array(
					"shopping_basket" => new stage(HTTPS_BASE_URL."/shopping_basket.php"), 
					"welcome" => new stage(HTTPS_BASE_URL."/checkout/welcome.php"),
					"shipping_address" => new stage(HTTPS_BASE_URL."/checkout/shipping_address.php"),
					"payment" => new stage(HTTPS_BASE_URL."/checkout/payment.php"), 
					"billing_address" => new stage(HTTPS_BASE_URL."/checkout/billing_address.php"),
					"summary" => new stage(HTTPS_BASE_URL."/checkout/summary.php"),
					"confirmation" => new stage(HTTPS_BASE_URL."/checkout/confirmation.php")
				);
	}	
	
	/**
	 * @ This is the singleton method as defined by the singleton pattern
	 *   This ensures we only ever work on one of this object type.
	 * 
	 * @param void
	 * @return object $this
	 */
	public static function getInstance(){
        if (!isset(self::$instance)) {
            $c = __CLASS__;
            self::$instance = new $c;
        }

        return self::$instance;
    }	
	
    /**
     * Constructor
     * 
     * @ In the constructor we get the stages and read from session
     * 
     * @param void
     * @return void
     */
	private function __construct(){ 
		
		$this->stages = $this->stages();
		$this->read();
		
	}	
	
	/**
	 * PHP_SELF replacement echoer
	 * 
	 * @ This function is designed to echo a string that will
	 *   replace the insecure PHP_SELF function.
	 *   
	 * @params void
	 * @return string $url
	 */
	public function echo_self(){
		return $this->stages[$this->current]->url;
	}
	
	/**
	 * Echo stage url
	 * 
	 * @ This allows us to quickly and easily echo a stage url
	 * 
	 * @params string $stage
	 * @return string $url
	 */
	public function stage_url($stage){
		return $stage ? $this->stages[$stage]->url : "";
	}

	/**
	 * Sets current stage
	 * 
	 * @ This simply sets the active stage
	 * 
	 * @param string $caption
	 * 
	 */
	public function set_active_stage($caption){
		
		if($this->current) $this->previous = $this->current;
		
		$this->current = $caption;
		$this->write();
		
	}
	
	/**
	 * Routing Function
	 * 
	 * @ This function will route the user through the cart.
	 *   It allows for understanding completed sections of 
	 *   the checkout and redirecting between them as needed
	 *   
	 * @param string $caption
	 * @return boolean $success
	 */
	public function route($caption = null){
		
		/**
		 * Support netsuite iframe
		 * 
		 * @ To support the netsuite iframe we go on faith saying if the session variable is set
		 *   then the page is completed.
		 */
		if(isset($_SESSION['payment_selected_payment_option'])) $this->stages["payment"]->completed = true;		
		
		/** Lets make current previous if current exists **/
		if($this->current) $this->previous = $this->current;
		
		/** Lets set the current **/
		if(array_key_exists($caption, $this->stages) && $caption){
			$this->current = $caption;
		}else{
			return false;
		}
		
		/** We have now completed this stage **/
		$this->stages[$this->current]->completed = true;
		
		/** reset and touch the array just to be sure we have the right position **/
		$this->setCurrent($this->current);
			
		/** Lets get the next stage **/
		$next  = true;
			
		while($next){
			
			//* Get next stage
			$next_stage = next($this->stages);
			
			//* Is that stage completed?
			if(!$next_stage->completed){

				//* If not set current to that stage and redirect the user
				$this->current = key($this->stages);
				header("Location: ".$next_stage->url);
				$next = false;
			}
		}	

		/** Write to session **/
		$this->write();		
		
		return true;
	}
	
	/**
	 * A PHP_SELF override
	 * 
	 * @ This function replaces PHP_SELF for the checkout
	 * 
	 * @param void
	 * @return header() $location
	 */
	public function to_self(){
		header("Location: ".$this->stages[$this->current]->url);
	}
	
	/**
	 * Goto Function
	 * 
	 * @ This function will redirect to a specific stage
	 * 
	 * @param string $stage
	 * @return void
	 */
	public function goto_stage($stage){
		
		header("Location: ".$this->stages[$stage]->url);
		
	}
	
	/**
	 * Go back function
	 * 
	 * @ This function will redirect a user to the previous page
	 *   Will not work unless you use set_active_stage at the top
	 *   of the page in question
	 * 
	 * @param string $stage
	 * @return void
	 */
	public function go_back($stage){
		
		header("Location: ".$this->stages[$this->previous]->url);
		
	}

	/**
	 * Array SetCurrent
	 * 
	 * @ This function will allow you to set a current position of the array
	 *   
	 * @param string $step
	 * @return (Object)stage $current_stage
	 */
    public function setCurrent ($stage) {
    
      reset($this->stages);
      for ($i=1; $i<=8; $i++) {
        if ($this->stages[$i] == $stage) break;
      }
      return current($this->stages);
    
    }
  
    /**
     * Array getNext
     * 
     * @ This function will allow you to get the next stage in the list
     * 
     * @param void
     * @return (Object)stage $nextstage
     */
    public function getNext () {
    
      self::setCurrent($this->current);
      return next($this->stages);
    
    }
   
    /**
     * Array getPrev
     * 
     * @ This function will allow you to get the previous stage in the list
     * 
     * @param void
     * @return (Object)stage $previoustage
     */
    public function getPrev () {
    
      self::setCurrent($this->current);
      return prev($this->stages);
    
    }
    
    /**
     * Array getEnd
     * 
     * @ This function will return the end entry of the stage array
     * 
     * @param void
     * @return (Object)stage $endstage
     */
    public function getEnd(){
    	return end($this->stages);
    }
	
	/**
	 * Store read function
	 * 
	 * @ This function will read the stored class from session.
	 *   We do not store the object since you should never do that so
	 *   instead we translate the object in a session storable array.
	 *   
	 * @param void
	 * @return void
	 */
	public function read(){
		
		$checkout_state_controller = $_SESSION['checkout_state_controller'];
		
		if($checkout_state_controller){
			
			$this->previous = $checkout_state_controller['previous'];
			$this->current = $checkout_state_controller['current'];
			
			/** Compose stage array **/
			foreach($checkout_state_controller['stages'] as $stage => $completed){
				
				$this->stages[$stage]->completed = $completed;
				
			}
		}
	}
	
	/**
	 * Store write function
	 * 
	 * @ This function will write this class to session
	 * 
	 * @param void
	 * @return void
	 */
	public function write(){
		
		$_SESSION['checkout_state_controller']['previous'] = $this->previous;
		$_SESSION['checkout_state_controller']['current'] = $this->current;
		
		/** Compose stage array **/
		$stages_arr = Array();
		foreach($this->stages as $stage_name => $object){
			
			$stages_arr[$stage_name] = $object->completed;			
		}
		
		$_SESSION['checkout_state_controller']['stages'] = $stages_arr;
		
	}
	
	/**
	 * Store delete function
	 * 
	 * @ This function will delete a store version of this class from session
	 * 
	 * @param void
	 * @return void
	 */
	public function delete(){
		
		unset($_SESSSION['checkout_state_controller']);
	}

	/**
	 * @ This is the __clone magic called when a user tries to clone this class
	 *   This function will return an error since singleton cannot be copied
	 */
	public function __clone(){
        trigger_error('You cannot clone this class.', E_USER_ERROR);
    }	

}

/**
 * This is the stage entity
 * 
 * @ This is the object in which a stage is place.
 *   It allows us to control excatly what a stage means
 * 
 */
class stage{
	
	protected $url;
	protected $required;
	protected $completed = false;

	public function __construct($url, $required = false){
		$this->url = $url;
		$this->required = $required;
	}

	public function __get($k){
		return $this->{$k};
	}
	
	public function __set($k, $v){
		$this->{$k} = $v;
	}
}
?>


Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s