tangra logo
   
[ class tree: tangra_lib ] [ index: tangra_lib ] [ all elements ]
 

Source for file threads_manager.class.php

Documentation is available at threads_manager.class.php

  1. <?php
  2. // *** Tangra (Application Framework and Tools for PHP)
  3. //  $Id$
  4. //
  5.  
  6. /**
  7.  * Contains class Threads_Manager
  8.  *
  9.  * @package tangra_lib
  10.  * @subpackage  web_site
  11.  */
  12.  
  13. /**
  14.  *
  15.  */
  16. require_once(TANGRA_MAIN_DIR.'core/vars_manager.class.php');
  17. /**
  18.  *
  19.  */
  20. require_once(TANGRA_MAIN_DIR.'web_site/session_vars_manager.class.php');
  21. /**
  22.  *
  23.  */
  24. require_once(TANGRA_MAIN_DIR.'web_site/thread_vars_manager.class.php');
  25.  
  26. /**
  27.  * Variable name for TVM
  28.  *
  29.  */
  30. define('THREADS_MANAGER_NAME''_tm');
  31.  
  32. //TODO - notify on expire to add methods
  33.  
  34. /**
  35.  * Threads Manager
  36.  *
  37.  * Threads concept is enhancement over sessions. Sites that implements threads management
  38.  * can detect when new instance of browser is opened and user is working with two or more
  39.  * browsers simultaneously.
  40.  * Threads manager function is to detect such "forks" and to split dataspace for the treads
  41.  * so each tread to have separate dataspace.
  42.  * This class is not ment to be used directly by the users - quite the opposite - it is ment to be
  43.  * 100% transparent. User have to use Web_Site/Web_Page get_tvm methods in order to get TVM.
  44.  *
  45.  * @package tangra_lib
  46.  * @subpackage  web_site
  47.  * @see Thread_Vars_Manager
  48.  *
  49.  */
  50. class Threads_Manager extends Tangra_Class {
  51.     /**
  52.      * Default lifetime for snapshots
  53.      *
  54.      */
  55.     const SNAPSHOTS_DEFAULT_LIFETIME = 300;
  56.  
  57.     /**
  58.      * Default maximum count of snapshots. Intended to prevend DoS attacks
  59.      *
  60.      */
  61.     const MAX_SNAPSHOTS = 250;
  62.  
  63.     /**
  64.      * name that will be used for the variable that will hold thread "id"
  65.      *
  66.      * @var string 
  67.      * @internal
  68.      */
  69.     private $url_rewrite_var_name;
  70.     /**
  71.      * Array that contain all existing threads
  72.      *
  73.      * @var mixed 
  74.      * @internal
  75.      */
  76.     private $threads = array();
  77.     /**
  78.      * Array that contain map url vars to thread index
  79.      *
  80.      * @var array 
  81.      * @internal
  82.      */
  83.     private $url_var_to_thread_links = array();
  84.     /**
  85.      * Index of current thread
  86.      *
  87.      * @var integer 
  88.      * @internal
  89.      */
  90.     private $current_thread_index;
  91.     /**
  92.      * Current URL variable (thread "id")
  93.      *
  94.      * @var string 
  95.      * @internal
  96.      */
  97.     private $current_url_var;
  98.  
  99.     /**
  100.      * Snapshots of threads
  101.      *
  102.      * @var array 
  103.      * @internal
  104.      */
  105.     private $threads_snapshots = array();
  106.  
  107.     /**
  108.      * Lifetime for snapshots in seconds
  109.      *
  110.      * @var integer 
  111.      */
  112.     private $snapshots_lifetime;
  113.  
  114.     /**
  115.      * Maximum count of snapshots.
  116.      *
  117.      * @var integer 
  118.      */
  119.     private $max_snapshots;
  120.  
  121.     /**
  122.      * Holds next unused thread index
  123.      *
  124.      * @var integer 
  125.      */
  126.     private $next_thread_index = 0;
  127.  
  128.     /**
  129.      * Holds next unused snapshot index per thread
  130.      *
  131.      * @var array 
  132.      */
  133.     private $next_snapshot_index = array();
  134.  
  135.     /**
  136.      * Constructor
  137.      *
  138.      * @param unknown_type $url_rewrite_var_name Name for the URL variable that will be used to track threads
  139.      * @param unknown_type $snapshots_lifetime Number of seconds after which snapshot will be seen as garbage and eventually unset
  140.      */
  141.     function __construct($url_rewrite_var_name$snapshots_lifetime Threads_Manager::SNAPSHOTS_DEFAULT_LIFETIME$max_snapshots Threads_Manager::MAX_SNAPSHOTS{
  142.         $this->url_rewrite_var_name = $url_rewrite_var_name;
  143.         $this->snapshots_lifetime = $snapshots_lifetime;
  144.         $this->max_snapshots = $max_snapshots;
  145.     }
  146.  
  147.  
  148.     /**
  149.      * Processes current context and manages threads
  150.      *
  151.      * @param Web_Context $context 
  152.      */
  153.     public function process(Web_Context $context{
  154.         $this->new_pass();
  155.  
  156.         if ($this->is_url_rewrite_var_passed($context)) {
  157.             $incoming_url_var $this->get_url_rewrite_var($context);
  158.  
  159.             if (array_key_exists($incoming_url_var$this->url_var_to_thread_links)) {
  160.                 if ($this->url_var_to_thread_links[$incoming_url_var]['access_count'== 0{
  161.                     $this->url_var_to_thread_links[$incoming_url_var]['access_count']++;
  162.                     //continue
  163.                     $this->current_thread_index = $this->url_var_to_thread_links[$incoming_url_var]['thread'];
  164. //printbr('continue '.$this->current_thread_index);
  165.                 else {
  166.                     //fork
  167.                     $this->create_new_thread();
  168.  
  169. //printbr('fork '.$this->get_current_thread_index());
  170.                     $this->move_snapshot($this->url_var_to_thread_links[$incoming_url_var]['thread'],
  171.                                             $this->url_var_to_thread_links[$incoming_url_var]['snapshot'],
  172.                                             $this->get_current_thread_index(),
  173.                                             $incoming_url_var
  174.                                             );
  175.                 }
  176.             else {
  177.                 $this->create_new_thread();
  178. //printbr('wrong');
  179.             }
  180.         else {
  181.             $this->create_new_thread();
  182. //printbr('new_direct');
  183.         }
  184.  
  185. //        $this->collect_garbage();
  186.     }
  187.  
  188.  
  189.     /**
  190.      * Updates last access time of current (detected from context) snapshot
  191.      *
  192.      * @param Web_Context $context 
  193.      */
  194.     public function snapshot_update_access_time(Web_Context $context{
  195.         if ($this->is_url_rewrite_var_passed($context)) {
  196.             $incoming_url_var $this->get_url_rewrite_var($context);
  197.             if (array_key_exists($incoming_url_var$this->url_var_to_thread_links)) {
  198.                 $current_thread_index $this->url_var_to_thread_links[$incoming_url_var]['thread'];
  199.                 $current_snapshot_index $this->url_var_to_thread_links[$incoming_url_var]['snapshot'];
  200.                 $this->threads_snapshots[$current_thread_index][$current_snapshot_index]['last_access_time'time();
  201.  
  202.                 //updates (if exist) access time of previos snapshot. That way we can correctly handle browser refresh button
  203.                 if (array_key_exists($current_snapshot_index 1$this->threads_snapshots[$current_thread_index])) {
  204.                     $this->threads_snapshots[$current_thread_index][$current_snapshot_index 1]['last_access_time'time();
  205.                 }
  206. //printbr("Updating: $current_thread_index $current_snapshot_index $incoming_url_var");
  207.             }
  208.         }
  209.     }
  210.  
  211.  
  212.     /**
  213.      * Returns lifetime setting for snapshots
  214.      *
  215.      * @return integer Lifetime in seconds
  216.      */
  217.     public function get_snapshots_lifetime({
  218.         return $this->snapshots_lifetime;
  219.     }
  220.  
  221.  
  222.     /**
  223.      * Creates new thread
  224.      * @internal
  225.      *
  226.      */
  227.     private function create_new_thread({
  228.         $this->current_thread_index = $this->add_new_thread();
  229.     }
  230.  
  231.  
  232.     /**
  233.      * Saves snapshot ot current thread
  234.      *
  235.      */
  236.     public function save_snapshot({
  237.         $current_url_var $this->get_current_url_var();
  238.         $current_thread_index $this->get_current_thread_index();
  239.  
  240.         $this->url_var_to_thread_links[$current_url_var]['thread'$current_thread_index;
  241.         $this->url_var_to_thread_links[$current_url_var]['access_count'0;
  242.  
  243.         $snapshot_index $this->add_snapshot($current_thread_index);
  244.         $this->url_var_to_thread_links[$current_url_var]['snapshot'$snapshot_index;
  245.     }
  246.  
  247.  
  248.     /**
  249.      * Moves snapshot from one thread to another.
  250.      *
  251.      * @param integer $old_threat_index 
  252.      * @param integer $snapshot_index 
  253.      * @param integer $new_thread_index 
  254.      * @param string $url_var 
  255.      * @internal
  256.      */
  257.     private function move_snapshot($old_threat_index$snapshot_index$new_thread_index$url_var{
  258.         $snapshot $this->get_snapshot($old_threat_index$snapshot_index);
  259.         $this->threads[$new_thread_indexclone $snapshot['snapshot'];
  260.  
  261.         $this->url_var_to_thread_links[$url_var]['thread'$new_thread_index;
  262.         $this->url_var_to_thread_links[$url_var]['access_count'1;
  263. //printbr('Moving '.$old_threat_index.'  '.$snapshot_index.' to '.$new_thread_index.' '.$url_var);
  264.         $snapshot_index_new $this->add_snapshot($new_thread_index);
  265.         $this->url_var_to_thread_links[$url_var]['snapshot'$snapshot_index_new;
  266.     }
  267.  
  268.  
  269.  
  270.     /**
  271.      * Prepares ovject for new process
  272.      * @internal
  273.      */
  274.     private function new_pass({
  275.         $this->current_url_var = $this->get_new_url_var();
  276.     }
  277.  
  278.  
  279.     /**
  280.      * Adds snapshot for thread specified by <var>$thread_num</var>
  281.      *
  282.      * @param integer $thread_num thread index
  283.      * @return integer Snapshot index
  284.      * @internal
  285.      */
  286.     private function add_snapshot($thread_num{
  287.         if ($this->count_snapshots(Threads_Manager::MAX_SNAPSHOTS{
  288.             throw new Tangra_Exception('Maximum count of snapshots ('.Threads_Manager::MAX_SNAPSHOTS.') reached. Your session is considered DoS attack.');
  289.         }
  290.  
  291.  
  292.         if (!array_key_exists($thread_num$this->next_snapshot_index)) {
  293.             $c 0;
  294.         else {
  295.             $c $this->next_snapshot_index[$thread_num];
  296.         }
  297.         $this->next_snapshot_index[$thread_num= ++$c;
  298.  
  299.         $this->threads_snapshots[$thread_num][$c]['create_time'time();
  300.         $this->threads_snapshots[$thread_num][$c]['snapshot'clone $this->threads[$thread_num];
  301.         $this->threads_snapshots[$thread_num][$c]['last_access_time'$this->threads_snapshots[$thread_num][$c]['create_time'];
  302.  
  303.  
  304.         $this->collect_garbage();
  305.  
  306.         return $c;
  307.     }
  308.  
  309.  
  310.     private function count_snapshots({
  311.         $ret 0;
  312.  
  313.         foreach ($this->threads_snapshots as $thread{
  314.             $ret += count($thread);
  315.         }
  316.  
  317.         return $ret;
  318.     }
  319.  
  320.  
  321.     /**
  322.      * Returns snapshot of thread
  323.      *
  324.      * @param integer $thread_num thread index
  325.      * @param unknown_type $snapshot_num Snapshot index
  326.      * @return array 
  327.      * @internal
  328.      */
  329.     private function get_snapshot($thread_num$snapshot_num{
  330.         $ret false;
  331. //printbr('Getting snapshot '.$thread_num.' '.$snapshot_num);
  332.         if (array_key_exists($snapshot_num$this->threads_snapshots[$thread_num])) {
  333.             $ret $this->threads_snapshots[$thread_num][$snapshot_num];
  334. //printbr('OK');
  335.         }
  336.  
  337.         return $ret;
  338.     }
  339.  
  340.  
  341.     /**
  342.      * Returns current thread index
  343.      *
  344.      * @return integer 
  345.      */
  346.     public function get_current_thread_index({
  347.         return $this->current_thread_index;
  348.     }
  349.  
  350.  
  351.     /**
  352.      * Returns current URL variable value
  353.      *
  354.      * @return unknown 
  355.      */
  356.     public function get_current_url_var({
  357.         return $this->current_url_var;
  358.     }
  359.  
  360.  
  361.     /**
  362.      * Returns URL variable name
  363.      *
  364.      * @return unknown 
  365.      */
  366.     public function get_url_rewrite_var_name({
  367.         return $this->url_rewrite_var_name;
  368.     }
  369.  
  370.  
  371.     /**
  372.      * Returns reference to current thread Vars_Manager
  373.      *
  374.      * @return Vars_Manager 
  375.      */
  376.     public function &get_current_thread_vm({
  377.         return $this->threads[$this->get_current_thread_index()];
  378.     }
  379.  
  380.  
  381.     /**
  382.      * Checks if URL variable is passed in POST or GET
  383.      *
  384.      * @param Web_Context $context 
  385.      * @return boolean 
  386.      * @internal
  387.      */
  388.     private function is_url_rewrite_var_passed(Web_Context $context{
  389.         return $context->exists_in_get(THREADS_MANAGER_NAME)
  390.                  || $context->exists_in_post(THREADS_MANAGER_NAME);
  391.     }
  392.  
  393.  
  394.     /**
  395.      * Returns value of passed URL rewrite variable
  396.      *
  397.      * @param Web_Context $context 
  398.      * @return string 
  399.      * @internal
  400.      */
  401.     private function get_url_rewrite_var(Web_Context $context{
  402.  
  403.         if ($context->exists_in_get(THREADS_MANAGER_NAME$context)) {
  404.             $incoming_url_var $context->get_from_get(THREADS_MANAGER_NAME);
  405.         else {
  406.             $incoming_url_var $context->get_from_post(THREADS_MANAGER_NAME);
  407.         }
  408.  
  409.         return $incoming_url_var;
  410.     }
  411.  
  412.  
  413.     /**
  414.      * Adds new thread
  415.      *
  416.      * @return integer Index of the new thread
  417.      * @internal
  418.      */
  419.     private function add_new_thread({
  420.         $c $this->next_thread_index;
  421.         $this->threads[$cnew Thread_Vars_Manager();
  422.  
  423.         $this->next_thread_index++;
  424.         return $c;
  425.     }
  426.  
  427.  
  428.     /**
  429.      * Generates random number
  430.      *
  431.      * @return integer 
  432.      * @internal
  433.      */
  434.     private function generate_new_random({
  435.  
  436.         return rand(0100000);
  437.     }
  438.  
  439.  
  440.     /**
  441.      * Generates new value for URL rewrite variable
  442.      *
  443.      * @return string 
  444.      * @internal
  445.      */
  446.     private function get_new_url_var({
  447.         do {
  448.             $url_var md5($this->generate_new_random());
  449.         while(array_key_exists($url_var$this->url_var_to_thread_links));
  450.  
  451.         return $url_var;
  452.     }
  453.  
  454.  
  455.     /**
  456.      * Colects garbage snapshots
  457.      * @internal
  458.      */
  459.     private function collect_garbage({
  460.         foreach($this->threads_snapshots as $thread_index => $snapshot_arr{
  461.             foreach($snapshot_arr as $snapshot_index => $s{
  462.  
  463.  
  464.                 if ($s['last_access_time'$this->snapshots_lifetime < time()) {
  465. //printbr("Unsetting: $thread_index $snapshot_index");
  466.                     unset($this->threads_snapshots[$thread_index][$snapshot_index]);
  467.                     if (count($this->threads_snapshots[$thread_index]== 0{
  468.                         unset($this->threads_snapshots[$thread_index]);
  469.                         unset($this->threads[$thread_index]);
  470.                         unset($this->next_snapshot_index[$thread_index]);
  471.                     }
  472.                     $this->remove_url_var_to_thread_link($thread_index$snapshot_index);
  473.                 else {
  474. //printbr('Thread: '.$thread_index.' Snapshot: '.$snapshot_index.' '.date("Y-m-d H:i:s", $s['last_access_time'] + $this->snapshots_lifetime) . ' - ' . date("Y-m-d H:i:s", time()));
  475.                 }
  476.             }
  477.         }
  478.     }
  479.  
  480.  
  481.     /**
  482.      * Removes url_var_to_thread_link for given snapshot
  483.      *
  484.      * @param integer $thread_index 
  485.      * @param integer $snapshot_index 
  486.      * @internal
  487.      */
  488.     private function remove_url_var_to_thread_link($thread_index$snapshot_index{
  489.         foreach($this->url_var_to_thread_links as $key => $link{
  490.             if ($link['thread'== $thread_index && $link['snapshot'== $snapshot_index{
  491. //printbr('Removing link: '.$key.' T:'.$thread_index.' S:'.$snapshot_index);
  492.                 unset($this->url_var_to_thread_links[$key]);
  493.                 break;
  494.             }
  495.         }
  496.     }
  497.  
  498.  
  499.     /**
  500.      * Returns statistic about usage of threads and snapshots.
  501.      *
  502.      * @return unknown 
  503.      */
  504.     public function get_usage_stat({
  505.         $ret['threads'count($this->threads);
  506.         $ret['snapshots'0;
  507.         foreach($this->threads_snapshots as $ts{
  508.             $ret['snapshots'+= count($ts);
  509.         }
  510.  
  511.         return $ret;
  512.     }
  513.  
  514.  
  515.     public function reset({
  516.         $this->threads_snapshots = array();
  517.         $this->threads = array();
  518.         $this->next_snapshot_index = array();
  519.     }
  520. }